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

Dynamic completion #406

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
93 changes: 93 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,59 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*

- [Zinit<a name="zinit"></a>](#zinita-namezinita)
- [Zinit Wiki<a name="zinit-wiki"></a>](#zinit-wikia-namezinit-wikia)
- [Install<a name="install"></a>](#installa-nameinstalla)
- [Automatic<a name="automatic"></a>](#automatica-nameautomatica)
- [Manual<a name="manual"></a>](#manuala-namemanuala)
- [Usage<a name="usage"></a>](#usagea-nameusagea)
- [Introduction<a name="introduction"></a>](#introductiona-nameintroductiona)
- [Plugins and snippets<a name="plugins-and-snippets"></a>](#plugins-and-snippetsa-nameplugins-and-snippetsa)
- [Upgrade Zinit and plugins<a name="upgrade-zinit-and-plugins"></a>](#upgrade-zinit-and-pluginsa-nameupgrade-zinit-and-pluginsa)
- [Turbo and lucid<a name="turbo-and-lucid"></a>](#turbo-and-lucida-nameturbo-and-lucida)
- [Migration<a name="migration"></a>](#migrationa-namemigrationa)
- [Frequently Asked Questions<a name="frequently-asked-questions"></a>](#frequently-asked-questionsa-namefrequently-asked-questionsa)
- [Use `zi ice svn` if a plugin/snippet requires an entire subdirectory<a name="use-zi-ice-svn-if-a-pluginsnippet-requires-an-entire-subdirectory"></a>](#use-zi-ice-svn-if-a-pluginsnippet-requires-an-entire-subdirectorya-nameuse-zi-ice-svn-if-a-pluginsnippet-requires-an-entire-subdirectorya)
- [Use `zi ice as'completion'` to directly add single file completion snippets<a name="use-zi-ice-ascompletion-to-directly-add-single-file-completion-snippets"></a>](#use-zi-ice-ascompletion-to-directly-add-single-file-completion-snippetsa-nameuse-zi-ice-ascompletion-to-directly-add-single-file-completion-snippetsa)
- [More Examples<a name="more-examples"></a>](#more-examplesa-namemore-examplesa)
- [How to Use<a name="how-to-use"></a>](#how-to-usea-namehow-to-usea)
- [Ice Modifiers<a name="ice-modifiers"></a>](#ice-modifiersa-nameice-modifiersa)
- [Cloning Options<a name="cloning-options"></a>](#cloning-optionsa-namecloning-optionsa)
- [Selection of Files (To Source, …)<a name="selection-of-files-to-source-%E2%80%A6"></a>](#selection-of-files-to-source-a-nameselection-of-files-to-source-a)
- [Conditional Loading<a name="conditional-loading"></a>](#conditional-loadinga-nameconditional-loadinga)
- [Plugin Output<a name="plugin-output"></a>](#plugin-outputa-nameplugin-outputa)
- [Completions<a name="completions"></a>](#completionsa-namecompletionsa)
- [Command Execution After Cloning, Updating or Loading<a name="command-execution-after-cloning-updating-or-loading"></a>](#command-execution-after-cloning-updating-or-loadinga-namecommand-execution-after-cloning-updating-or-loadinga)
- [Sticky-Emulation Of Other Shells<a name="sticky-emulation-of-other-shells"></a>](#sticky-emulation-of-other-shellsa-namesticky-emulation-of-other-shellsa)
- [Others<a name="others"></a>](#othersa-nameothersa)
- [Order of Execution<a name="order-of-execution"></a>](#order-of-executiona-nameorder-of-executiona)
- [Zinit Commands<a name="zinit-commands"></a>](#zinit-commandsa-namezinit-commandsa)
- [Help<a name="help"></a>](#helpa-namehelpa)
- [Loading and Unloading<a name="loading-and-unloading"></a>](#loading-and-unloadinga-nameloading-and-unloadinga)
- [Completions<a name="completions-1"></a>](#completionsa-namecompletions-1a)
- [Tracking of the Active Session<a name="tracking-of-the-active-session"></a>](#tracking-of-the-active-sessiona-nametracking-of-the-active-sessiona)
- [Reports and Statistics<a name="reports-and-statistics"></a>](#reports-and-statisticsa-namereports-and-statisticsa)
- [Compiling<a name="compiling"></a>](#compilinga-namecompilinga)
- [Other<a name="other"></a>](#othera-nameothera)
- [Updating Zinit and Plugins<a name="updating-zinit-and-plugins"></a>](#updating-zinit-and-pluginsa-nameupdating-zinit-and-pluginsa)
- [Completions<a name="completions-2"></a>](#completionsa-namecompletions-2a)
- [Calling `compinit` Without Turbo Mode<a name="calling-compinit-without-turbo-mode"></a>](#calling-compinit-without-turbo-modea-namecalling-compinit-without-turbo-modea)
- [Calling `compinit` With Turbo Mode<a name="calling-compinit-with-turbo-mode"></a>](#calling-compinit-with-turbo-modea-namecalling-compinit-with-turbo-modea)
- [Ignoring Compdefs<a name="ignoring-compdefs"></a>](#ignoring-compdefsa-nameignoring-compdefsa)
- [Disabling System-Wide `compinit` Call (Ubuntu)<a name="disabling-system-wide-compinit-call-ubuntu"></a>](#disabling-system-wide-compinit-call-ubuntua-namedisabling-system-wide-compinit-call-ubuntua)
- [Zinit Module<a name="zinit-module"></a>](#zinit-modulea-namezinit-modulea)
- [Hints and Tips<a name="hints-and-tips"></a>](#hints-and-tipsa-namehints-and-tipsa)
- [Customizing Paths<a name="customizing-paths"></a>](#customizing-pathsa-namecustomizing-pathsa)
- [Non-GitHub (Local) Plugins<a name="non-github-local-plugins"></a>](#non-github-local-pluginsa-namenon-github-local-pluginsa)
- [Extending Git<a name="extending-git"></a>](#extending-gita-nameextending-gita)
- [Use named directories for plugin dirs](#use-named-directories-for-plugin-dirs)
- [Changelog<a name="changelog"></a>](#changeloga-namechangeloga)
- [Support<a name="support"></a>](#supporta-namesupporta)
- [Getting Help and Community<a name="getting-help-and-community"></a>](#getting-help-and-communitya-namegetting-help-and-communitya)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

<p align="center">
<a href="https://github.com/zdharma-continuum/zinit">
<img src="https://raw.githubusercontent.com/zdharma-continuum/zinit/images/zinit.png"/>
Expand Down Expand Up @@ -1081,9 +1137,46 @@ zi as'null' lucid sbin wait'1' for \

Target directory for installed files is `$ZPFX` (`~/.local/share/zinit/polaris` by default).

### Use named directories for plugin dirs

Named directories are shorthands in the form of `~NAMED_DIR`
(upper case not required). They are setup via `hash` command
with `-d` option, and point to a directory, for example:

```zsh
# Set up a GHUB named directory
hash -d GHUB=~/github
# Then use as:
cd ~GHUB/my-project
# PWD is now: ~/github/my-project
```

A nice usecase for it can be labelling all plugin directories
with `~plugin-id` (for `user-id/plugin-id` plugin), via
following snippet added to the end of zshrc:

```zsh
for i in $ZINIT[PLUGINS_DIR]/*; do
# Remove all except the final ID component.
q=${${i:t}##*---}
# Remove trailing slash.
q=${q%/}
# Hash the final ID part with directory.
# After this it's possible to `cd ~fzf`
# to get to directory of `junegunn/fzf`.
hash -d $q=$i
done
```
## Changelog<a name="changelog"></a>

Link to the [CHANGELOG](doc/CHANGELOG.md).
## Completion for Plugin IDs and Ices

Zinit supports a custom completion for plugin IDs (`Alt-Shift-A`) and
for ices (`Alt-Shift-C`). Just place the cursor after e.g.: `wa` and
press `Alt-Shift-C` to have it completed to `wait`. The same with
plugin IDs – `fzf` then `Alt-Shift-A` to have `junegunn/fzf` (if it's
installed).

## Support<a name="support"></a>

Expand Down
131 changes: 131 additions & 0 deletions zi-action-complete
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#!/usr/bin/env zsh
#
# -*- mode: sh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
#
# Copyright (c) 2022 zdharma-continuum and contributors
# Copyright (c) 2016-2022 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
14 changes: 12 additions & 2 deletions zinit.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -3315,14 +3315,24 @@ if [[ -e ${${ZINIT[BIN_DIR]}}/zmodules/Src/zdharma/zplugin.so ]] {
# create so that for sure no warncreateglobal warning is issued
typeset -g REPLY

# a searchable menu of tags for current directory
zinit null light-mode autoload'zi-browse-symbol' for %$ZINIT[BIN_DIR]
zinit null light-mode autoload'zi-browse-symbol;zi-action-complete;zi-process-buffer' \
for %$ZINIT[BIN_DIR]
zle -N zi-browse-symbol
zle -N zi-browse-symbol-backwards zi-browse-symbol
zle -N zi-browse-symbol-pbackwards zi-browse-symbol
zle -N zi-browse-symbol-pforwards zi-browse-symbol
zstyle -s ':zinit:browse-symbol' key ZINIT_TMP || ZINIT_TMP='\eQ'
[[ -n $ZINIT_TMP ]] && bindkey $ZINIT_TMP zi-browse-symbol
bindkey "^K" zi-browse-symbol

# A custom completion of plugin ids (alt-a) and of ice names (alt-c)
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

# Local Variables:
# mode: Shell-Script
Expand Down