Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial version of an idea – an on demand plugin-id completor and #331

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions zi-action-complete
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# -*- mode: sh; sh-indentation: 4; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
vladdoster marked this conversation as resolved.
Show resolved Hide resolved
# Copyright (c) 2016-2020 Sebastian Gniazdowski and contributors.

zi-action-complete()
{
# Emulate zsh and useful options
emulate -L zsh -o extended_glob -o warn_create_global -o typeset_silent \
-o no_short_loops -o rc_quotes -o no_auto_pushd

# The index of the match that's inserted in cmd line
integer -g ziac_midx
typeset -ga ziac_matches ziac_wids
typeset -g ziac_prevw ziac_mt
local -a match mbegin mend
typeset -gA Times

# Emulate real time if needed
if (( !$+EPOCHREALTIME )); then
(( !$+SECONDS )) && float -g SECONDS
float EPOCHREALTIME=$SECONDS
fi

#
# Invoke the helper/worker function that does all needed $BUFFER processing
#

zi-process-buffer || return 1

integer i j pos=$CURSOR size=${#ZINIT_PB_WORDS} newcursor
local w PWIDGET buf
(( Times[$WIDGET] = Times[$WIDGET] <= 0 ?
EPOCHREALTIME-3 : Times[$WIDGET] ))
# Detect series.
if (( EPOCHREALTIME - Times[$WIDGET] < 1.5 )); then
PWIDGET=$WIDGET
else
PWIDGET= ziac_prevw=
fi
Times[$WIDGET]=$EPOCHREALTIME

# Cursor at empty space? Only if not use the word
[[ $BUFFER[CURSOR] == [[:space:]] ]] || \
w=$ZINIT_PB_WORDS[ZINIT_PB_SELECTED_WORD]

if [[ $WIDGET == zi-action-complete-ice && $WIDGET != $PWIDGET && \
( $ziac_prevw != $w || -z $w ) ]]
then
local -a ice_order=(
${(Aons:|:)ZINIT[ice-list]}
${(@)${(A@kons:|:)${ZINIT_EXTS[ice-mods]//\'\'/}}/(#s)<->-/}
)
ziac_prevw=$w
match=()
w=${w//(#b)(([=:]|)[\'\"]?#([\'\"]|(#e)))/}
ziac_mt=$match[1]

ziac_matches=( ${(onM)ice_order:#*$w*} )
elif (( ziac_midx )) && [[ $WIDGET == *-ice && $WIDGET == $PWIDGET ]]; then
ziac_midx+=1
fi
if [[ $WIDGET == zi-action-complete && $WIDGET != $PWIDGET && \
( $ziac_prevw != $w || -z $w ) ]]; then
ziac_matches=( $ZINIT[PLUGINS_DIR]/*${w//\//---}*(-onND[1,18]) )
ziac_prevw=$w
ziac_mt=
elif (( ziac_midx )) && [[ $WIDGET == *-complete && $WIDGET == $PWIDGET ]]; then
ziac_midx+=1
fi
if (( !ziac_midx || ziac_midx > $#ziac_matches )); then
ziac_midx=1
fi

if [[ -z $ziac_matches ]]; then
zle -M "No matches for $w found"
return 1
fi
zle -M "${(j: :)${ziac_matches[@]:t}//---//}"
REPLY=${${ziac_matches[$ziac_midx]:t}//---//}$ziac_mt
for (( i=1; i<=size; i++ )); do
# Check if we're at (i.e. directly at or after,
# when after are just spaces) current word
if [[ $i = $ZINIT_PB_SELECTED_WORD ]]; then
# INSERT MODE? I.e.: addition of a new token at the pointed free space?
if (( pos > (ZINIT_PB_WORDS_BEGINNINGS[i] + ${#ZINIT_PB_WORDS[i]} - 1) )); then
# We need to introduce new word
# First move all words and spaces forward
for (( j=size; j>=i+1; j-- )); do
ZINIT_PB_WORDS[j+1]=${ZINIT_PB_WORDS[j]}
ZINIT_PB_SPACES[j+1]=${ZINIT_PB_SPACES[j]}
ZINIT_PB_WORDS_BEGINNINGS[j+1]=${ZINIT_PB_WORDS_BEGINNINGS[j]}
done
size+=1
# New word is introduced at position i+1, after current word
# It doesn't have word beginnings and spaces assigned
# It has to take spaces from word next to it, i+2
ZINIT_PB_WORDS[i+1]=$REPLY
ZINIT_PB_WORDS_BEGINNINGS[i+1]="$(( pos + 1 ))"

# Now split spaces
# cursor_spaces: spaces from current word to the cursor
integer cursor_spaces=$(( pos - ZINIT_PB_WORDS_BEGINNINGS[i] - ${#ZINIT_PB_WORDS[i]} + 1 ))
# take that from spaces of word "next" in: current NEW next
integer after_spaces=$(( ZINIT_PB_SPACES[i+2] - cursor_spaces ))
local empty=""
ZINIT_PB_SPACES[i+1]="${(l:cursor_spaces:: :)empty}"
ZINIT_PB_SPACES[i+2]="${(l:after_spaces:: :)empty}"

# Cursor will be at end of newly added word
newcursor=$(( ZINIT_PB_WORDS_BEGINNINGS[i+1] + ${#ZINIT_PB_WORDS[i+1]} - 1 ))
# OR REPLACE MODE – substitute the match for the input/needle token
else
ZINIT_PB_WORDS[i]=$REPLY

# Cursor will be at end of newly substituted word
newcursor=$(( ZINIT_PB_WORDS_BEGINNINGS[i] + ${#ZINIT_PB_WORDS[i]} - 1 ))

# Update word beginnings of following words – skipped
fi
fi
buf+=$ZINIT_PB_SPACES[i]$ZINIT_PB_WORDS[i]
done

buf+=$ZINIT_PB_SPACES[i]
BUFFER=$buf
CURSOR=$newcursor
}

98 changes: 98 additions & 0 deletions zi-process-buffer
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Input:
# $1 - optional buffer to process (default is $BUFFER)
# $2 - optional parameter containing cursor (default is $CURSOR)
#
# Output:
# ZINIT_PB_WORDS - split of $1 into shell words; array
# ZINIT_PB_WORDS_BEGINNINGS - indexes of first letters of corresponding words in ZINIT_PB_WORDS
# ZINIT_PB_SPACES - white spaces before corresponding words in ZINIT_PB_WORDS
# ZINIT_PB_SELECTED_WORD - index in ZINIT_PB_WORDS pointing to word activated by cursor position
# ZINIT_PB_LEFT - left part of active word
# ZINIT_PB_RIGHT - right part of active word
#

emulate -LR zsh
setopt typesetsilent extendedglob noshortloops

local MBEGIN MEND MATCH mbegin mend match

local buf="${1:-$BUFFER}"
local cursor="${2:-$CURSOR}"

ZINIT_PB_WORDS=( "${(Z+n+)buf}" )
ZINIT_PB_SPACES=( )
ZINIT_PB_WORDS_BEGINNINGS=( )
ZINIT_PB_SELECTED_WORD=-1

# (Z+n+) will return 1 element for buf that is empty or only whitespace
if [[ $buf = ( |$'\t')# ]]; then
ZINIT_PB_WORDS=( )
integer nwords=0
else
integer nwords="${#ZINIT_PB_WORDS}"
fi

# Remove ZINIT_PB_WORDS one by one, counting characters,
# computing beginning of each word, to find
# place to break the word into 2 halves (for
# complete_in_word option)

local i word wordlen
integer char_count=0

# (Z) handles spaces nicely, but we need them for the user
# Also compute words beginnings and the selected word
for (( i=1; i<=nwords; i++ )); do
# Remove spurious space generated by Z-flag when
# input is an unbound '$(' (happens with zsh < 5.1)
# and also real spaces gathered by an unbound '$(',
# to handle them in a way normal to this loop
ZINIT_PB_WORDS[i]="${ZINIT_PB_WORDS[i]%% ##}"
word="${ZINIT_PB_WORDS[i]}"

# In general, $buf can start with white spaces
# We will not search for them, but instead for
# leading character of current shell word,
# negated. This is an ambition to completely
# avoid character classes

# Remove white spaces
buf="${buf##(#m)[^$word[1]]#}"
# Count them
char_count=char_count+"$#MATCH"
# This is the beginning of current word
ZINIT_PB_WORDS_BEGINNINGS[i]=$(( char_count + 1 ))
# Remember the spaces
ZINIT_PB_SPACES[i]=$MATCH

# Remove the word
wordlen="${#word}"
[[ "${buf[1,wordlen]}" != $word ]] && return 1 # should not happen unless bug in (z)
buf="${buf[wordlen+1,-1]}"

# Spaces point to previous shell word
# Visual cursor right after spaces (-ge) -> not enough to select previous word (-gt required)
[[ $ZINIT_PB_SELECTED_WORD -eq -1 && $char_count -gt $cursor ]] && ZINIT_PB_SELECTED_WORD=$(( i-1 ))

# Actual characters point to current shell word
# Visual cursor right after letters (-ge) -> enough to select current word
char_count=char_count+"$#word"
[[ $ZINIT_PB_SELECTED_WORD -eq -1 && $char_count -ge $cursor ]] && ZINIT_PB_SELECTED_WORD=$i
done

# What's left in $buf can be only white spaces
char_count=char_count+"$#buf"
ZINIT_PB_SPACES[i]=$buf

# Visual cursor right after spaces (-ge) -> enough to select last word
[[ $ZINIT_PB_SELECTED_WORD -eq -1 && $char_count -ge $cursor ]] && ZINIT_PB_SELECTED_WORD=$(( i-1 ))

# Divide active word into two halves
integer diff=$(( cursor - ZINIT_PB_WORDS_BEGINNINGS[ZINIT_PB_SELECTED_WORD] + 1 ))
word="${ZINIT_PB_WORDS[ZINIT_PB_SELECTED_WORD]}"
ZINIT_PB_LEFT="${word[1,diff]}"
ZINIT_PB_RIGHT="${word[diff+1,-1]}"

[[ $ZINIT_PB_SELECTED_WORD -gt 0 ]]

# vim:ft=zsh
vladdoster marked this conversation as resolved.
Show resolved Hide resolved
10 changes: 10 additions & 0 deletions zinit.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -3281,4 +3281,14 @@ zle -N zi-browse-symbol-pbackwards zi-browse-symbol
zle -N zi-browse-symbol-pforwards zi-browse-symbol
bindkey "^K" zi-browse-symbol

# A custom completion of plugin ids (alt-a) and of ice names (alt-c)
zinit null light-mode autoload'zi-action-complete;zi-process-buffer' for %$ZINIT[BIN_DIR]
zle -N zi-action-complete
zle -N zi-action-complete-ice zi-action-complete
# Alt-A and Alt-C are default.
zstyle -s ":zinit:action-complete:plugin-id" key ZINIT_TMP || ZINIT_TMP='\eA'
[[ -n $ZINIT_TMP ]] && bindkey $ZINIT_TMP zi-action-complete
zstyle -s ":zinit:action-complete:ice" key ZINIT_TMP || ZINIT_TMP='\eC'
[[ -n $ZINIT_TMP ]] && bindkey $ZINIT_TMP zi-action-complete-ice

# vim:ft=zsh:sw=4:sts=4:et:foldmarker=[[[,]]]:foldmethod=marker