From a8fdd21227d76e077ad034c6ff7108ae44bd57a7 Mon Sep 17 00:00:00 2001 From: Marlon Richert Date: Wed, 3 Apr 2024 15:17:43 +0300 Subject: [PATCH] Move recent paths completion to a keybinding Resolves #640. --- Completions/_autocomplete__recent_paths | 8 +-- Functions/Init/.autocomplete__async | 26 ++++++--- Functions/Init/.autocomplete__compinit | 4 +- Functions/Init/.autocomplete__key-bindings | 1 + Functions/Init/.autocomplete__recent-dirs | 64 +++++++++++----------- README.md | 54 +++++++++--------- 6 files changed, 86 insertions(+), 71 deletions(-) diff --git a/Completions/_autocomplete__recent_paths b/Completions/_autocomplete__recent_paths index d0866da..b18452e 100644 --- a/Completions/_autocomplete__recent_paths +++ b/Completions/_autocomplete__recent_paths @@ -1,5 +1,5 @@ #autoload -zmodload -F zsh/parameter p:functions +zmodload -Fa zsh/parameter p:functions local -aU files=() local -a expl=() displ=() @@ -7,16 +7,14 @@ local -Pi ret=1 i=0 local -P singular= plural= for singular plural in directory directories file files; do - if [[ -v functions[+autocomplete:recent-$plural] && - $_comp_tags == (|* )(|(all|local|globbed)-)$plural(| *) ]] && + if [[ -v functions[+autocomplete:recent-$plural] ]] && +autocomplete:recent-$plural "$PREFIX$SUFFIX"; then files=( "$reply[@]" ) _description -V recent-$plural expl "recent $singular" - compadd "$expl[@]" -D files -- "${files[@]:t}" for i in {1..$#files}; do displ=( "${${(D)files[i]:h}%/}/$files[i]:t" ) - compadd "$expl[@]" -d displ -P "${${displ[1]:h}%/}/" -fW "${${files[i]:h}%/}/" \ + compadd -U "$expl[@]" -d displ -P "${${displ[1]:h}%/}/" -fW "${${files[i]:h}%/}/" \ -- "$files[i]:t" && ret=0 done diff --git a/Functions/Init/.autocomplete__async b/Functions/Init/.autocomplete__async index 5889c7d..007b944 100644 --- a/Functions/Init/.autocomplete__async +++ b/Functions/Init/.autocomplete__async @@ -15,6 +15,7 @@ ${0}:precmd() { [[ -v ZSH_AUTOSUGGEST_IGNORE_WIDGETS ]] && ZSH_AUTOSUGGEST_IGNORE_WIDGETS+=( history-incremental-search-backward + recent-paths .autocomplete:async:complete:fd-widget ) @@ -27,7 +28,8 @@ ${0}:precmd() { builtin zle -C ._list_choices list-choices .autocomplete:async:list-choices:completion-widget - builtin zle -N history-incremental-search-backward .autocomplete:async:history-incremental-search + builtin zle -N history-incremental-search-backward .autocomplete:async:toggle-context + builtin zle -N recent-paths .autocomplete:async:toggle-context add-zle-hook-widget line-init .autocomplete:async:reset-context add-zle-hook-widget line-pre-redraw .autocomplete:async:complete @@ -37,13 +39,13 @@ ${0}:precmd() { add-zle-hook-widget isearch-exit .autocomplete:async:isearch-exit } -.autocomplete:async:history-incremental-search() { +.autocomplete:async:toggle-context() { if [[ $curcontext == $WIDGET* ]]; then unset curcontext else typeset -g curcontext=${WIDGET}::: fi - .autocomplete:async:complete + zle .autocomplete:async:complete -w } .autocomplete:async:reset-context() { @@ -349,7 +351,7 @@ ${0}:precmd() { .autocomplete:async:sufficient-input() { local min_input= if ! builtin zstyle -s ":autocomplete:${curcontext}:" min-input min_input; then - if [[ $curcontext == *history-* ]]; then + if [[ -n $curcontext ]]; then min_input=0 else min_input=1 @@ -404,11 +406,18 @@ ${0}:precmd() { } .autocomplete:async:list-choices:main-complete() { - local -i _autocomplete__max_lines=0 - if [[ $curcontext == *history-* ]]; then + local -i _autocomplete__max_lines + + case $curcontext in + *history-* ) setopt $_autocomplete__func_opts[@] autocomplete:_main_complete:new - history-lines _autocomplete__history_lines - else + ;; + recent-paths:* ) + setopt $_autocomplete__func_opts[@] + autocomplete:_main_complete:new - recent-paths _autocomplete__recent_paths + ;; + * ) { () { emulate -L zsh @@ -431,7 +440,8 @@ ${0}:precmd() { .autocomplete:async:unshadow compadd .autocomplete:async:unshadow _describe } - fi + ;; + esac } .autocomplete:async:shadow() { diff --git a/Functions/Init/.autocomplete__compinit b/Functions/Init/.autocomplete__compinit index 9bb8155..73bd76e 100644 --- a/Functions/Init/.autocomplete__compinit +++ b/Functions/Init/.autocomplete__compinit @@ -122,8 +122,8 @@ EOF PREFIX=$PREFIX$SUFFIX SUFFIX= - autocomplete:_complete:old "$@" || - _autocomplete__recent_paths "$@" + autocomplete:_complete:old "$@" + # WORKAROUND: Some completion functions mistakenly don't return 0 when they have succeeded. (( compstate[nmatches] > nmatches )) } diff --git a/Functions/Init/.autocomplete__key-bindings b/Functions/Init/.autocomplete__key-bindings index 0e4aafb..4933c67 100644 --- a/Functions/Init/.autocomplete__key-bindings +++ b/Functions/Init/.autocomplete__key-bindings @@ -43,6 +43,7 @@ local backtab=$terminfo[kcbt] main+=( '\t' complete-word "$backtab" insert-unambiguous-or-complete + '^X/' recent-paths ) ${0}:bind history-search-backward vi-backward-blank-word '\ep' '^P' $key_alt_up[@] diff --git a/Functions/Init/.autocomplete__recent-dirs b/Functions/Init/.autocomplete__recent-dirs index 68a7610..4e000d1 100644 --- a/Functions/Init/.autocomplete__recent-dirs +++ b/Functions/Init/.autocomplete__recent-dirs @@ -3,36 +3,38 @@ zmodload -Fa zsh/files b:zf_mv b:zf_mkdir zmodload -F zsh/parameter p:commands p:dirstack p:functions ${0}:precmd() { - if [[ ! -v functions[+autocomplete:recent-directories] ]]; then - setopt autopushd pushdignoredups # Set *global* shell options. - builtin autoload -RUz chpwd_recent_filehandler - - local __='' - builtin zstyle -s :chpwd: recent-dirs-file __ || - builtin zstyle ':chpwd:*' recent-dirs-file ${XDG_DATA_HOME:-$HOME/.local/share}/zsh/chpwd-recent-dirs - builtin zstyle -s :chpwd: recent-dirs-max __ || - builtin zstyle ':chpwd:*' recent-dirs-max 0 - - if ! (( $#dirstack[@] )); then - local -aU reply=() - chpwd_recent_filehandler - dirstack=( ${^reply[@]:#$PWD}(N-/) ) - fi - - +autocomplete:recent-directories:save() { - chpwd_recent_filehandler $PWD $dirstack[@] - } - add-zsh-hook chpwd +autocomplete:recent-directories:save - - +autocomplete:recent-directories() { - # Don't complete current dir, its children, or nonexistent ones. - typeset -ga reply=( ${^dirstack[@]:#$PWD(|/[^/]#)}(N) ) - - local -i exact=$reply[(I)*/$PREFIX$SUFFIX] - (( exact )) && - reply=( $reply[exact] $reply[1,exact-1] $reply[exact+1,-1] ) - - (( $#reply[@] )) - } + [[ -v functions[+autocomplete:recent-directories] ]] && + return + + setopt autopushd pushdignoredups # Set *global* shell options. + builtin autoload -RUz chpwd_recent_filehandler + + local __='' + builtin zstyle -s :chpwd: recent-dirs-file __ || + builtin zstyle ':chpwd:*' recent-dirs-file ${XDG_DATA_HOME:-$HOME/.local/share}/zsh/chpwd-recent-dirs + builtin zstyle -s :chpwd: recent-dirs-max __ || + builtin zstyle ':chpwd:*' recent-dirs-max 0 + + if ! (( $#dirstack[@] )); then + local -aU reply=() + chpwd_recent_filehandler + dirstack=( ${^reply[@]:#$PWD}(N-/) ) fi + + +autocomplete:recent-directories:save() { + chpwd_recent_filehandler $PWD $dirstack[@] + } + add-zsh-hook chpwd +autocomplete:recent-directories:save + + +autocomplete:recent-directories() { + typeset -ga reply + + # Don't complete /, ~, $PWD or its children, or nonexistent dirs. + reply=( ${^dirstack[@]:#([/~]|$PWD(|/[^/]#))}(N) ) + + [[ -n $1 ]] && + reply=( ${(M)reply:#*${(~j:*:)${(s::)1}}*} ) + + (( $#reply[@] )) + } } diff --git a/README.md b/README.md index cf774ee..28dfd1b 100644 --- a/README.md +++ b/README.md @@ -73,14 +73,15 @@ Otherwise, simply use your package manager or plugin manager's update mechanisms | ---: | ---: | ---: | :--- | :--- | Enter
Return | | | | Exit menu text search or exit menu | Tab | | | Insert first listed menu item | Exit menu text search or exit menu -| ShiftTab | | | Insert substring occuring in all listed completions | Exit menu text search or exit menu +| ShiftTab | | | Insert substring occurring in all listed completions | Exit menu text search or exit menu | | CtrlN | J | Cursor down or enter completion menu | Change selection | | CtrlP | K | Cursor up or enter [history menu](#history-menu) | Change selection | Alt | AltN | CtrlN | Enter completion menu | Next section | Alt | AltP | CtrlP | Enter history menu | Previous section | PgDn | | | | Page down | PgUp | | | | Page up -| | CtrlR | / | Toggle history search mode | Start menu text search or go to previous match +| | CtrlX / | | Toggle recent path search | +| | CtrlR | / | Toggle history search | Start menu text search or go to previous match | | CtrlS | ? | Start menu text search | Start menu text search or go to next match | | CtrlSpace | V | Toggle selection mode | Add another item | | Ctrl-
Ctrl/ | U | | Undo last item @@ -208,29 +209,7 @@ Modifying this list will change when a space is inserted. If you change the list to `'*'`, a space is always inserted. If you put no elements in the list, then a space is never inserted. -### Use a custom backend for recent directories -Autocomplete comes with its own backend for keeping track of and listing recent -directories (which uses part of -[`cdr`](https://zsh.sourceforge.io/Doc/Release/User-Contributions.html#Recent-Directories) -under the hood). However, you can override this and supply Autocomplete with -recent directories from any source that you like. To do so, define a function -like this: -```zsh -+autocomplete:recent-directories() { - typeset -ga reply=( [code that generates an array of absolute paths] ) -} -``` - -### Add a backend for recent files -Out of the box, Autocomplete doesn't track or offer recent files. However, it -will do so if you add a backend for it: -```zsh -+autocomplete:recent-files() { - typeset -ga reply=( [code that generates an array of absolute paths] ) -} -``` - -### Start each new line in history search mode +### Start each command line in history search mode This will make Autocomplete behave as if you pressed CtrlR at the start of each new command line: ```zsh zstyle ':autocomplete:*' default-context history-incremental-search-backward @@ -273,6 +252,31 @@ Note that for autocompletion and history search, the maximum number of lines is lines that fit on screen. However, there is no such limit for the history menu. If that generates more lines than fit on screen, you can simply scroll upwards to see more. +### Use a custom backend for recent directories +Autocomplete comes with its own backend for keeping track of and listing recent directories (which +uses part of +[`cdr`](https://zsh.sourceforge.io/Doc/Release/User-Contributions.html#Recent-Directories) under the +hood). However, you can override this and supply Autocomplete with recent directories from any +source that you like. To do so, define a function like this: + +```sh ++autocomplete:recent-directories() { + + typeset -ga reply=( ) +} +``` + +#### Add a backend for recent files +Out of the box, Autocomplete doesn't track or offer recent files. However, it will do so if you add +a backend for it: + +```sh ++autocomplete:recent-files() { + + typeset -ga reply=( ) +} +``` + ## Troubleshooting Try the steps in the [bug report template](.github/ISSUE_TEMPLATE/bug-report.md).