From 545051e44407ac42215495d283c37cea9f8be362 Mon Sep 17 00:00:00 2001 From: Marlon Richert Date: Tue, 26 Mar 2024 14:59:51 +0200 Subject: [PATCH] Simplify keybinding system --- Functions/Init/.autocomplete__key-bindings | 119 +++++----------- README.md | 153 +++++++++++---------- 2 files changed, 117 insertions(+), 155 deletions(-) diff --git a/Functions/Init/.autocomplete__key-bindings b/Functions/Init/.autocomplete__key-bindings index c7af265..0e4aafb 100644 --- a/Functions/Init/.autocomplete__key-bindings +++ b/Functions/Init/.autocomplete__key-bindings @@ -1,108 +1,59 @@ #!/bin/zsh zmodload zsh/complist -zmodload -F zsh/parameter p:funcstack p:functions zmodload -F zsh/terminfo p:terminfo local -Pa prefix=( '\e'{\[,O} ) -typeset -ga _autocomplete__key_up=( ${^prefix}A ) -typeset -ga _autocomplete__key_down=( ${^prefix}B ) -typeset -ga _autocomplete__key_alt_up=( '\e'$^_autocomplete__key_up '\e[1;3A' ) -typeset -ga _autocomplete__key_alt_down=( '\e'$^_autocomplete__key_down '\e[1;3B' ) +local -Pa key_up=( ${^prefix}A ) +local -Pa key_down=( ${^prefix}B ) +local -Pa key_alt_up=( '\e'$^key_up '\e[1;3A' ) +local -Pa key_alt_down=( '\e'$^key_down '\e[1;3B' ) -${0}:bind() { - local -P keymap=$1 widget=$2 - shift 2 - builtin bindkey -M "$keymap" "${@:^^widget}" -} +local -A main=() emacs=() vicmd=() menukeys=() -${0}:bind-menu() { - 0=${0%:*} - ${0}:bind isearch "$@" - ${0}:bind menuselect "$@" -} +${0}:bind() { + local -P key= widget=$1 menuwidget=$2 -${0}:bound() { - [[ $( builtin bindkey -M "$1" "$3" ) == \"[^[:space:]]##\"\ $2 ]] -} + emacs[$3]=$widget + menukeys[$3]=$menuwidget -${0}:rebind() { - 0=${0%:*} + vicmd[$4]=$widget + key="^[$4" + menukeys[$key]=$menuwidget - local -P keymap=$1 old=$2 new=$3 key= - shift 3 + shift 4 for key; do - ${0}:bound "$keymap" "$old" "$key" && - builtin bindkey -M "$keymap" "$key" "$new" + main[$key]=$widget + menukeys[$key]=$menuwidget done } -${0}:unbind() { - 0=${0%:*} - ${0}:bound "$1" "$2" "$3" && - builtin bindkey -M "$1" -r "$2" -} - -${0}:bind main up-line-or-search $_autocomplete__key_up[@] -${0}:bind-menu up-history $_autocomplete__key_up[@] -${0}:bind main down-line-or-select $_autocomplete__key_down[@] -${0}:bind-menu down-history $_autocomplete__key_down[@] - -${0}:rebind emacs up-line-or-{history,search} '^P' -${0}:bind-menu up-history '^P' -${0}:rebind emacs down-line-or-{history,select} '^N' -${0}:bind-menu down-history '^N' - -${0}:rebind vicmd up-line-or-{history,search} 'k' -${0}:rebind vicmd down-line-or-{history,select} 'j' +${0}:bind up-line-or-search up-history '^P' 'k' $key_up[@] +${0}:bind down-line-or-select down-history '^N' 'j' $key_down[@] -local -Pa menukeys=( +menukeys+=( '^@' accept-and-hold - '^_' .undo + '^[v' accept-and-hold + '^_' undo + '^[u' undo "$terminfo[kpp]" backward-word "$terminfo[knp]" forward-word - '^[v' backward-word - '^V' forward-word ) -builtin bindkey -M isearch "$menukeys[@]" -builtin bindkey -M menuselect "$menukeys[@]" -${0}:precmd() { - emulate -L zsh - setopt $_autocomplete__func_opts[@] - - 0=${0%:*} - - ${0}:rebind main expand-or-complete complete-word '\t' - ${0}:rebind main expand-or-complete-prefix complete-word '\t' - ${0}:rebind main menu-expand-or-complete complete-word '\t' - - local backtab=$terminfo[kcbt] - ${0}:rebind main undefined-key insert-unambiguous-or-complete "$backtab" - - ${0}:bound main complete-word '\t' && - ${0}:bind-menu accept-line '\t' - - ${0}:bind main history-search-backward $_autocomplete__key_alt_up[@] - ${0}:bind-menu vi-backward-blank-word $_autocomplete__key_alt_up[@] - ${0}:bind main menu-select $_autocomplete__key_alt_down[@] - ${0}:bind-menu vi-forward-blank-word $_autocomplete__key_alt_down[@] - - ${0}:rebind emacs history-search-backward history-search-backward '\ep' - ${0}:bind-menu vi-backward-blank-word '\ep' - ${0}:rebind emacs history-search-forward menu-select '\en' - ${0}:bind-menu vi-forward-blank-word '\en' +local backtab=$terminfo[kcbt] +main+=( + '\t' complete-word + "$backtab" insert-unambiguous-or-complete +) - ${0}:rebind vicmd vi-rev-repeat-search history-search-backward 'N' - ${0}:rebind vicmd vi-repeat-search menu-select 'n' +${0}:bind history-search-backward vi-backward-blank-word '\ep' '^P' $key_alt_up[@] +${0}:bind history-search-forward vi-forward-blank-word '\en' '^N' $key_alt_down[@] - ${0}:rebind emacs history-incremental-search-backward{,} '^R' - ${0}:bind-menu history-incremental-search-backward '^R' - ${0}:rebind emacs history-incremental-search-forward menu-search '^S' - ${0}:bind-menu history-incremental-search-forward '^S' +${0}:bind history-incremental-search-backward history-incremental-search-backward '^R' '/' +${0}:bind menu-search history-incremental-search-forward '^S' '?' - ${0}:rebind vicmd {vi-history,history-incremental}-search-backward '/' - ${0}:rebind vicmd vi-history-search-forward menu-search '?' +bindkey -M main "${(kv@)main}" +bindkey -M emacs "${(kv@)emacs}" +bindkey -M vicmd "${(kv@)vicmd}" +bindkey -M menuselect "${(kv@)menukeys}" - unset -m '_autocomplete__key_*' - unfunction ${0}:{{,re,un}bind,bound} -} +unfunction ${0}:bind diff --git a/README.md b/README.md index 97f049a..cf774ee 100644 --- a/README.md +++ b/README.md @@ -69,65 +69,101 @@ Otherwise, simply use your package manager or plugin manager's update mechanisms 1. Restart your shell. ## Keyboard shortcuts +| `main` | `emacs` | `vicmd` | On the command line | In the menus +| ---: | ---: | ---: | :--- | :--- +| 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 +| | 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 +| | 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 +| | CtrlG | | | Undo all added items + +Note: +* Plugins or other scripts that you load _after_ loading Autocomplete may override these bindings. + If you find that some shortcuts don't work as expected, then you can fix them by running + [`bindkey` commands](#reassign-keys) at the end of your `.zshrc` file. * Depending on your terminal, not all keybindings might be available to you. * Instead of Alt, your terminal might require you to press Escape, Option or Meta. -* In most terminals, Enter is interchangeable with Return, - but in some terminals, it is not. - -### On the command line -| `main` | `emacs` | `vicmd` | `viins` | Action -| -----: | ------: | ------: | ------: | -----: -| | Tab | | Tab | Insert top completion -| ShiftTab | | | | Insert substring occurring in all listed completions -| | CtrlP | K | | Cursor up _-or-_ [History menu](#history-menu) -| | CtrlN | J | | Cursor down _-or-_ Completion menu -| Alt | AltP | ShiftN | | History menu (always) -| Alt | AltN | N | | Completion menu (always) -| | CtrlS | ? | | Search through _all_ menu text -| | CtrlR | / | | Toggle [history search mode](#real-time-history-search) on/off - -### In the menus -| Key(s) | Action | -| -----: | ------ -| | Change selection -| Alt | Backward one group (completion only) -| Alt | Forward one group (completion only) -| PgUp
AltV | Page up -| PgDn
CtrlV | Page down -| CtrlS | Enter search mode _-or-_ Go to next match -| CtrlR | Enter search mode _-or-_ Go to previous match -| Tab
Enter | Exit search mode _-or-_ Exit menu -| CtrlSpace | Add another completion -| Ctrl-
Ctrl/ | Remove last completion -| CtrlG | Remove all completions -| Other keys | Depends on the keymap from which you opened the menu. See the Zsh manual on [menu selection](https://zsh.sourceforge.io/Doc/Release/Zsh-Modules.html#Menu-selection). +* `main` is whichever keymap was aliased to `main` when Autocomplete was sourced. + * By default, this is `emacs`. + * If you run `bindkey -v`, then this becomes `viins`. +* In the menus, the bindings listed under `vicmd` require you to press Alt for each, + instead of just once. +* The bindings listed under `emacs` and `vicmd` are always both active in the menus, no matter which + keymap you actually use. This is a limitation of Zsh. +* What any other keys do while you're in a menu depends on the keymap from which you opened the + menu. See the Zsh manual section on [menu + selection](https://zsh.sourceforge.io/Doc/Release/Zsh-Modules.html#Menu-selection) for more info. ## Configuration -The following are the most commonly requested ways to configure Autocomplete's -behavior. Add these to your `.zshrc` file to use them. +The following are the most commonly requested ways to configure Autocomplete's behavior. To use any +of these, add the code shown to your `.zshrc` file and modify it there, then restart you shell. -### Pass arguments to `compinit` +### Reassign keys +You can use [Zsh's `bindkey` +command](https://zsh.sourceforge.io/Doc/Release/Zsh-Line-Editor.html#Zle-Builtins), _after_ loading +Autocomplete, to customize your keybindings. Below are some examples of what you can do with this. -If necessary, you can let Autocomplete pass arguments to `compinit` as follows: +#### Make Tab and ShiftTab cycle completions on the command line +This makes Tab and ShiftTab, when pressed on the command line, +cycle through listed completions, without changing what's listed in the menu: ```sh -zstyle '*:compinit' arguments -D -i -u -C -w +bindkey '^I' menu-complete +bindkey "$terminfo[kcbt]" reverse-menu-complete ``` -### Reassign Tab -You can reassign Tab to do something else than the default. This -includes letting another plugin set it. Here are two examples of what you can -do with this: +#### Make Tab and ShiftTab go to the menu +This makes Tab and ShiftTab, when pressed on the command line, +enter the menu instead of inserting a completion: +```sh +bindkey '^I' menu-select +bindkey "$terminfo[kcbt]" menu-select +``` -#### Make Tab and ShiftTab cycle completions on the command line -```zsh -bindkey '\t' menu-complete "$terminfo[kcbt]" reverse-menu-complete +#### Make Tab and ShiftTab change the selection in the menu +This makes Tab and ShiftTab move the selection in the menu right +and left, respectively, instead of exiting the menu or exiting menu search: +```sh +bindkey -M menuselect '^I' menu-complete +bindkey -M menuselect "$terminfo[kcbt]" reverse-menu-complete ``` -#### Make Tab go straight to the menu and cycle there -```zsh -bindkey '\t' menu-select "$terminfo[kcbt]" menu-select -bindkey -M menuselect '\t' menu-complete "$terminfo[kcbt]" reverse-menu-complete +#### Make and always move the cursor on the command line +This makes and always move the cursor on the command line, even when you +are in the menu: +```sh +bindkey -M menuselect '^[[D' .backward-char '^[OD' .backward-char +bindkey -M menuselect '^[[C' .forward-char '^[OC' .forward-char +``` + +#### Make Enter always submit the command line +This makes Enter always submit the command line, even when you are in the menu: +```sh +bindkey -M menuselect '^M' .accept-line +``` + +#### Restore Zsh-default functionality +Autocomplete overrides the behavior of some of Zsh's built-in keyboard widgets. To use the original +widget instead, prefix it with a `.`: +```sh +bindkey '^R' .history-incremental-search-backward +bindkey '^S' .history-incremental-search-forward +``` + +### Pass arguments to `compinit` + +If necessary, you can let Autocomplete pass arguments to `compinit` as follows: +```sh +zstyle '*:compinit' arguments -D -i -u -C -w ``` ### First insert the common substring @@ -237,31 +273,6 @@ 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. -### Reset history key bindings to Zsh default -Add any of the following to your `.zshrc` file _after_ sourcing Autocomplete: - -#### Reset and -```zsh -() { - local -a prefix=( '\e'{\[,O} ) - local -a up=( ${^prefix}A ) down=( ${^prefix}B ) - local key= - for key in $up[@]; do - bindkey "$key" up-line-or-history - done - for key in $down[@]; do - bindkey "$key" down-line-or-history - done -} -``` - -#### Preserve Zsh-default keybindings -To prevent Autocomplete from overriding a default keybinding, add a `.` in front of the widget's name. For example: -``` -bindkey '^R' .history-incremental-search-backward -bindkey '^S' .history-incremental-search-forward -``` - ## Troubleshooting Try the steps in the [bug report template](.github/ISSUE_TEMPLATE/bug-report.md).