Skip to content

Commit

Permalink
Initial version of an idea – an on demand plugin-id completor and
Browse files Browse the repository at this point in the history
verifier. One will be able to obtain, e.g.:

zinit fzf<Alt>-a » zinit junegunn/fzf
zinit cd rip<Alt>-a » zinit cd BurntSushi/ripgrep
zinit cd tmux/tmux<Alt>-v » "tmux/tmux" highlighted eighter in blue
                          » or red, depending on existence  of
                          » such plugin

and so on. The <Alt>-a and <Alt>-v shortcuts work with ALL commands,
regardless if it's zinit command or not.

Two autoloaded functions added, ziactioncomplete (the proper imple-
mentation of the main features) and ziprocessbuffer (a very useful
library function that takes care of $BUFFFER/$CURSOR processing).
  • Loading branch information
psprint committed Jul 6, 2022
1 parent a4dc13f commit 7363ebb
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 0 deletions.
64 changes: 64 additions & 0 deletions ziactioncomplete
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# -*- mode: sh; sh-indentation: 4; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# Copyright (c) 2016-2020 Sebastian Gniazdowski and contributors.

ziactioncomplete()
{
# 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

# Invoke the helper/worker function that does all needed $BUFFER processing
ziprocessbuffer
integer i j pos=$CURSOR size=${#ZINIT_PB_WORDS} newcursor
local w=$ZINIT_PB_WORDS[ZINIT_PB_SELECTED_WORD] buf
local -a matches=( $ZINIT[PLUGINS_DIR]/*${w//\//---}*(-onND[1,8]) )
REPLY=${${matches[1]:t}//---//}
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
# Check if we're at the word itself,
# or at some distance after it
if [[ $WIDGET == ziactioncompleteinsert ]] && (( 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 ))
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
}

7 changes: 7 additions & 0 deletions zinit.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -3232,4 +3232,11 @@ if [[ -e ${${ZINIT[BIN_DIR]}}/zmodules/Src/zdharma/zplugin.so ]] {

# Create so that for sure no warncreateglobal warning is issued
typeset -g REPLY

zinit null light-mode autoload'ziactioncomplete;ziprocessbuffer' for %$ZINIT[BIN_DIR]
zle -N ziactioncomplete
zle -N ziactioncompleteinsert ziactioncomplete
bindkey '\ea' ziactioncomplete
bindkey '\eA' ziactioncompleteinsert

# vim:ft=zsh:sw=4:sts=4:et:foldmarker=[[[,]]]:foldmethod=marker
96 changes: 96 additions & 0 deletions ziprocessbuffer
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# 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]}"

# vim:ft=zsh

0 comments on commit 7363ebb

Please sign in to comment.