diff --git a/doc_src/bind.txt b/doc_src/bind.txt index 88039d03b..d778cd31e 100644 --- a/doc_src/bind.txt +++ b/doc_src/bind.txt @@ -3,12 +3,14 @@ \subsection bind-synopsis Synopsis \fish{synopsis} bind [(-M | --mode) MODE] [(-m | --sets-mode) NEW_MODE] + [--preset | --user] [(-s | --silent)] [(-k | --key)] SEQUENCE COMMAND [COMMAND...] -bind [(-M | --mode) MODE] [(-k | --key)] SEQUENCE -bind (-K | --key-names) [(-a | --all)] +bind [(-M | --mode) MODE] [(-k | --key)] [--preset] [--user] SEQUENCE +bind (-K | --key-names) [(-a | --all)] [--preset] [--user] bind (-f | --function-names) bind (-L | --list-modes) bind (-e | --erase) [(-M | --mode) MODE] + [--preset] [--user] (-a | --all | [(-k | --key)] SEQUENCE [SEQUENCE...]) \endfish @@ -32,7 +34,7 @@ When multiple `COMMAND`s are provided, they are all run in the specified order w If no `SEQUENCE` is provided, all bindings (or just the bindings in the specified `MODE`) are printed. If `SEQUENCE` is provided without `COMMAND`, just the binding matching that sequence is printed. -Key bindings are not saved between sessions by default. **Bare `bind` statements in config.fish won't have any effect because it is sourced before the default keybindings are setup.** To save custom keybindings, put the `bind` statements into a function called `fish_user_key_bindings`, which will be autoloaded. +To save custom keybindings, put the `bind` statements into config.fish. Alternatively, fish also automatically executes a function called `fish_user_key_bindings` if it exists. Key bindings may use "modes", which mimics Vi's modal input behavior. The default mode is "default", and every bind applies to a single mode. The mode can be viewed/changed with the `$fish_bind_mode` variable. @@ -54,6 +56,8 @@ The following parameters are available: - `-a` or `--all` See `--erase` and `--key-names` +- `--preset` and `--user` specify if bind should operate on user or preset bindings. User bindings take precedence over preset bindings when fish looks up mappings. By default, all `bind` invocations work on the "user" level except for listing, which will show both levels. All invocations except for inserting new bindings can operate on both levels at the same time. `--preset` should only be used in full binding sets (like when working on `fish_vi_key_bindings`). + \subsection bind-functions Special input functions The following special input functions are available: diff --git a/share/functions/__fish_shared_key_bindings.fish b/share/functions/__fish_shared_key_bindings.fish index 6b46be177..0ab3b7827 100644 --- a/share/functions/__fish_shared_key_bindings.fish +++ b/share/functions/__fish_shared_key_bindings.fish @@ -9,103 +9,103 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod return 1 end - bind $argv \cy yank + bind --preset $argv \cy yank or return # protect against invalid $argv - bind $argv \ey yank-pop + bind --preset $argv \ey yank-pop # Left/Right arrow - bind $argv -k right forward-char - bind $argv -k left backward-char - bind $argv \e\[C forward-char - bind $argv \e\[D backward-char + bind --preset $argv -k right forward-char + bind --preset $argv -k left backward-char + bind --preset $argv \e\[C forward-char + bind --preset $argv \e\[D backward-char # Some terminals output these when they're in in keypad mode. - bind $argv \eOC forward-char - bind $argv \eOD backward-char + bind --preset $argv \eOC forward-char + bind --preset $argv \eOD backward-char - bind $argv -k ppage beginning-of-history - bind $argv -k npage end-of-history + bind --preset $argv -k ppage beginning-of-history + bind --preset $argv -k npage end-of-history # Interaction with the system clipboard. - bind $argv \cx fish_clipboard_copy - bind $argv \cv fish_clipboard_paste + bind --preset $argv \cx fish_clipboard_copy + bind --preset $argv \cv fish_clipboard_paste - bind $argv \e cancel - bind $argv \t complete - bind $argv \cs pager-toggle-search + bind --preset $argv \e cancel + bind --preset $argv \t complete + bind --preset $argv \cs pager-toggle-search # shift-tab does a tab complete followed by a search. - bind $argv --key btab complete-and-search + bind --preset $argv --key btab complete-and-search - bind $argv \e\n "commandline -i \n" - bind $argv \e\r "commandline -i \n" + bind --preset $argv \e\n "commandline -i \n" + bind --preset $argv \e\r "commandline -i \n" - bind $argv -k down down-or-search - bind $argv -k up up-or-search - bind $argv \e\[A up-or-search - bind $argv \e\[B down-or-search - bind $argv \eOA up-or-search - bind $argv \eOB down-or-search + bind --preset $argv -k down down-or-search + bind --preset $argv -k up up-or-search + bind --preset $argv \e\[A up-or-search + bind --preset $argv \e\[B down-or-search + bind --preset $argv \eOA up-or-search + bind --preset $argv \eOB down-or-search # Alt-left/Alt-right - bind $argv \e\eOC nextd-or-forward-word - bind $argv \e\eOD prevd-or-backward-word - bind $argv \e\e\[C nextd-or-forward-word - bind $argv \e\e\[D prevd-or-backward-word - bind $argv \eO3C nextd-or-forward-word - bind $argv \eO3D prevd-or-backward-word - bind $argv \e\[3C nextd-or-forward-word - bind $argv \e\[3D prevd-or-backward-word - bind $argv \e\[1\;3C nextd-or-forward-word - bind $argv \e\[1\;3D prevd-or-backward-word - bind $argv \e\[1\;9C nextd-or-forward-word #iTerm2 - bind $argv \e\[1\;9D prevd-or-backward-word #iTerm2 + bind --preset $argv \e\eOC nextd-or-forward-word + bind --preset $argv \e\eOD prevd-or-backward-word + bind --preset $argv \e\e\[C nextd-or-forward-word + bind --preset $argv \e\e\[D prevd-or-backward-word + bind --preset $argv \eO3C nextd-or-forward-word + bind --preset $argv \eO3D prevd-or-backward-word + bind --preset $argv \e\[3C nextd-or-forward-word + bind --preset $argv \e\[3D prevd-or-backward-word + bind --preset $argv \e\[1\;3C nextd-or-forward-word + bind --preset $argv \e\[1\;3D prevd-or-backward-word + bind --preset $argv \e\[1\;9C nextd-or-forward-word #iTerm2 + bind --preset $argv \e\[1\;9D prevd-or-backward-word #iTerm2 # Alt-up/Alt-down - bind $argv \e\eOA history-token-search-backward - bind $argv \e\eOB history-token-search-forward - bind $argv \e\e\[A history-token-search-backward - bind $argv \e\e\[B history-token-search-forward - bind $argv \eO3A history-token-search-backward - bind $argv \eO3B history-token-search-forward - bind $argv \e\[3A history-token-search-backward - bind $argv \e\[3B history-token-search-forward - bind $argv \e\[1\;3A history-token-search-backward - bind $argv \e\[1\;3B history-token-search-forward - bind $argv \e\[1\;9A history-token-search-backward # iTerm2 - bind $argv \e\[1\;9B history-token-search-forward # iTerm2 + bind --preset $argv \e\eOA history-token-search-backward + bind --preset $argv \e\eOB history-token-search-forward + bind --preset $argv \e\e\[A history-token-search-backward + bind --preset $argv \e\e\[B history-token-search-forward + bind --preset $argv \eO3A history-token-search-backward + bind --preset $argv \eO3B history-token-search-forward + bind --preset $argv \e\[3A history-token-search-backward + bind --preset $argv \e\[3B history-token-search-forward + bind --preset $argv \e\[1\;3A history-token-search-backward + bind --preset $argv \e\[1\;3B history-token-search-forward + bind --preset $argv \e\[1\;9A history-token-search-backward # iTerm2 + bind --preset $argv \e\[1\;9B history-token-search-forward # iTerm2 # Bash compatibility # https://github.com/fish-shell/fish-shell/issues/89 - bind $argv \e. history-token-search-backward + bind --preset $argv \e. history-token-search-backward - bind $argv \el __fish_list_current_token - bind $argv \ew 'set tok (commandline -pt); if test $tok[1]; echo; whatis $tok[1]; commandline -f repaint; end' + bind --preset $argv \el __fish_list_current_token + bind --preset $argv \ew 'set tok (commandline -pt); if test $tok[1]; echo; whatis $tok[1]; commandline -f repaint; end' # ncurses > 6.0 sends a "delete scrollback" sequence along with clear. # This string replace removes it. - bind $argv \cl 'echo -n (clear | string replace \e\[3J ""); commandline -f repaint' - bind $argv \cc __fish_cancel_commandline - bind $argv \cu backward-kill-line - bind $argv \cw backward-kill-path-component - bind $argv \e\[F end-of-line - bind $argv \e\[H beginning-of-line + bind --preset $argv \cl 'echo -n (clear | string replace \e\[3J ""); commandline -f repaint' + bind --preset $argv \cc __fish_cancel_commandline + bind --preset $argv \cu backward-kill-line + bind --preset $argv \cw backward-kill-path-component + bind --preset $argv \e\[F end-of-line + bind --preset $argv \e\[H beginning-of-line - bind $argv \ed 'set -l cmd (commandline); if test -z "$cmd"; echo; dirh; commandline -f repaint; else; commandline -f kill-word; end' - bind $argv \cd delete-or-exit + bind --preset $argv \ed 'set -l cmd (commandline); if test -z "$cmd"; echo; dirh; commandline -f repaint; else; commandline -f kill-word; end' + bind --preset $argv \cd delete-or-exit # Allow reading manpages by pressing F1 (many GUI applications) or Alt+h (like in zsh). - bind $argv -k f1 __fish_man_page - bind $argv \eh __fish_man_page + bind --preset $argv -k f1 __fish_man_page + bind --preset $argv \eh __fish_man_page # This will make sure the output of the current command is paged using the default pager when # you press Meta-p. # If none is set, less will be used. - bind $argv \ep '__fish_paginate' + bind --preset $argv \ep '__fish_paginate' # Make it easy to turn an unexecuted command into a comment in the shell history. Also, # remove the commenting chars so the command can be further edited then executed. - bind $argv \e\# __fish_toggle_comment_commandline + bind --preset $argv \e\# __fish_toggle_comment_commandline # The [meta-e] and [meta-v] keystrokes invoke an external editor on the command buffer. - bind \ee edit_command_buffer - bind \ev edit_command_buffer + bind --preset $argv \ee edit_command_buffer + bind --preset $argv \ev edit_command_buffer # Support for "bracketed paste" # The way it works is that we acknowledge our support by printing @@ -135,17 +135,17 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod # Exclude paste mode or there'll be an additional binding after switching between emacs and vi for mode in (bind --list-modes | string match -v paste) - bind -M $mode -m paste \e\[200~ '__fish_start_bracketed_paste' + bind --preset -M $mode -m paste \e\[200~ '__fish_start_bracketed_paste' end # This sequence ends paste-mode and returns to the previous mode we have saved before. - bind -M paste \e\[201~ '__fish_stop_bracketed_paste' + bind --preset -M paste \e\[201~ '__fish_stop_bracketed_paste' # In paste-mode, everything self-inserts except for the sequence to get out of it - bind -M paste "" self-insert + bind --preset -M paste "" self-insert # Without this, a \r will overwrite the other text, rendering it invisible - which makes the exercise kinda pointless. # TODO: Test this in windows (\r\n line endings) - bind -M paste \r "commandline -i \n" - bind -M paste "'" "__fish_commandline_insert_escaped \' \$__fish_paste_quoted" - bind -M paste \\ "__fish_commandline_insert_escaped \\\ \$__fish_paste_quoted" + bind --preset -M paste \r "commandline -i \n" + bind --preset -M paste "'" "__fish_commandline_insert_escaped \' \$__fish_paste_quoted" + bind --preset -M paste \\ "__fish_commandline_insert_escaped \\\ \$__fish_paste_quoted" end function __fish_commandline_insert_escaped --description 'Insert the first arg escaped if a second arg is given' diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index 34e6c4df3..941477552 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -6,7 +6,7 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fis end if not set -q argv[1] - bind --erase --all # clear earlier bindings, if any + bind --erase --all --preset # clear earlier bindings, if any if test "$fish_key_bindings" != "fish_default_key_bindings" # Allow the user to set the variable universally set -q fish_key_bindings @@ -28,76 +28,76 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fis or return # protect against invalid $argv # This is the default binding, i.e. the one used if no other binding matches - bind $argv "" self-insert + bind --preset $argv "" self-insert or exit # protect against invalid $argv - bind $argv \n execute - bind $argv \r execute + bind --preset $argv \n execute + bind --preset $argv \r execute - bind $argv \ck kill-line + bind --preset $argv \ck kill-line - bind $argv \eOC forward-char - bind $argv \eOD backward-char - bind $argv \e\[C forward-char - bind $argv \e\[D backward-char - bind $argv -k right forward-char - bind $argv -k left backward-char + bind --preset $argv \eOC forward-char + bind --preset $argv \eOD backward-char + bind --preset $argv \e\[C forward-char + bind --preset $argv \e\[D backward-char + bind --preset $argv -k right forward-char + bind --preset $argv -k left backward-char - bind $argv -k dc delete-char - bind $argv -k backspace backward-delete-char - bind $argv \x7f backward-delete-char + bind --preset $argv -k dc delete-char + bind --preset $argv -k backspace backward-delete-char + bind --preset $argv \x7f backward-delete-char # for PuTTY # https://github.com/fish-shell/fish-shell/issues/180 - bind $argv \e\[1~ beginning-of-line - bind $argv \e\[3~ delete-char - bind $argv \e\[4~ end-of-line + bind --preset $argv \e\[1~ beginning-of-line + bind --preset $argv \e\[3~ delete-char + bind --preset $argv \e\[4~ end-of-line # OS X SnowLeopard doesn't have these keys. Don't show an annoying error message. - bind $argv -k home beginning-of-line 2>/dev/null - bind $argv -k end end-of-line 2>/dev/null - bind $argv \e\[3\;2~ backward-delete-char # Mavericks Terminal.app shift-ctrl-delete + bind --preset $argv -k home beginning-of-line 2>/dev/null + bind --preset $argv -k end end-of-line 2>/dev/null + bind --preset $argv \e\[3\;2~ backward-delete-char # Mavericks Terminal.app shift-ctrl-delete - bind $argv \ca beginning-of-line - bind $argv \ce end-of-line - bind $argv \ch backward-delete-char - bind $argv \cp up-or-search - bind $argv \cn down-or-search - bind $argv \cf forward-char - bind $argv \cb backward-char - bind $argv \ct transpose-chars - bind $argv \et transpose-words - bind $argv \eu upcase-word + bind --preset $argv \ca beginning-of-line + bind --preset $argv \ce end-of-line + bind --preset $argv \ch backward-delete-char + bind --preset $argv \cp up-or-search + bind --preset $argv \cn down-or-search + bind --preset $argv \cf forward-char + bind --preset $argv \cb backward-char + bind --preset $argv \ct transpose-chars + bind --preset $argv \et transpose-words + bind --preset $argv \eu upcase-word # This clashes with __fish_list_current_token - # bind $argv \el downcase-word - bind $argv \ec capitalize-word + # bind --preset $argv \el downcase-word + bind --preset $argv \ec capitalize-word # One of these is alt+backspace. - bind $argv \e\x7f backward-kill-word - bind $argv \e\b backward-kill-word - bind $argv \eb backward-word - bind $argv \ef forward-word - bind $argv \e\[1\;5C forward-word - bind $argv \e\[1\;5D backward-word - bind $argv \e\< beginning-of-buffer - bind $argv \e\> end-of-buffer + bind --preset $argv \e\x7f backward-kill-word + bind --preset $argv \e\b backward-kill-word + bind --preset $argv \eb backward-word + bind --preset $argv \ef forward-word + bind --preset $argv \e\[1\;5C forward-word + bind --preset $argv \e\[1\;5D backward-word + bind --preset $argv \e\< beginning-of-buffer + bind --preset $argv \e\> end-of-buffer - bind $argv \ed kill-word + bind --preset $argv \ed kill-word # Ignore some known-bad control sequences # https://github.com/fish-shell/fish-shell/issues/1917 - bind $argv \e\[I 'begin;end' - bind $argv \e\[O 'begin;end' + bind --preset $argv \e\[I 'begin;end' + bind --preset $argv \e\[O 'begin;end' # term-specific special bindings switch "$TERM" case 'rxvt*' - bind $argv \e\[8~ end-of-line - bind $argv \eOc forward-word - bind $argv \eOd backward-word + bind --preset $argv \e\[8~ end-of-line + bind --preset $argv \eOc forward-word + bind --preset $argv \eOd backward-word case 'xterm-256color' # Microsoft's conemu uses xterm-256color plus # the following to tell a console to paste: - bind $argv \e\x20ep fish_clipboard_paste + bind --preset $argv \e\x20ep fish_clipboard_paste end end diff --git a/share/functions/fish_hybrid_key_bindings.fish b/share/functions/fish_hybrid_key_bindings.fish index bf2bbf015..2d0ad6c75 100644 --- a/share/functions/fish_hybrid_key_bindings.fish +++ b/share/functions/fish_hybrid_key_bindings.fish @@ -1,5 +1,5 @@ function fish_hybrid_key_bindings --description "Vi-style bindings that inherit emacs-style bindings in all modes" - bind --erase --all # clear earlier bindings, if any + bind --erase --all --preset # clear earlier bindings, if any if test "$fish_key_bindings" != "fish_hybrid_key_bindings" # Allow the user to set the variable universally diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index c701cf6e6..2c83e7195 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -14,7 +14,7 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' set rebind false set -e argv[1] else - bind --erase --all # clear earlier bindings, if any + bind --erase --all --preset # clear earlier bindings, if any end # Silence warnings about unavailable keys. See #4431, 4188 @@ -61,217 +61,217 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' __fish_shared_key_bindings -M $mode end - bind $argv insert \r execute - bind $argv insert \n execute + bind --preset $argv insert \r execute + bind --preset $argv insert \n execute - bind $argv insert "" self-insert + bind --preset $argv insert "" self-insert # Add way to kill current command line while in insert mode. - bind $argv insert \cc __fish_cancel_commandline + bind --preset $argv insert \cc __fish_cancel_commandline # Add a way to switch from insert to normal (command) mode. # Note if we are paging, we want to stay in insert mode # See #2871 - bind $argv insert \e "if commandline -P; commandline -f cancel; else; set fish_bind_mode default; commandline -f backward-char force-repaint; end" + bind --preset $argv insert \e "if commandline -P; commandline -f cancel; else; set fish_bind_mode default; commandline -f backward-char force-repaint; end" # Default (command) mode - bind :q exit - bind -m insert \cc __fish_cancel_commandline - bind $argv default h backward-char - bind $argv default l forward-char - bind -m insert \n execute - bind -m insert \r execute - bind -m insert i force-repaint - bind -m insert I beginning-of-line force-repaint - bind -m insert a forward-char force-repaint - bind -m insert A end-of-line force-repaint - bind -m visual v begin-selection force-repaint + bind --preset :q exit + bind --preset -m insert \cc __fish_cancel_commandline + bind --preset $argv default h backward-char + bind --preset $argv default l forward-char + bind --preset -m insert \n execute + bind --preset -m insert \r execute + bind --preset -m insert i force-repaint + bind --preset -m insert I beginning-of-line force-repaint + bind --preset -m insert a forward-char force-repaint + bind --preset -m insert A end-of-line force-repaint + bind --preset -m visual v begin-selection force-repaint - #bind -m insert o "commandline -a \n" down-line force-repaint - #bind -m insert O beginning-of-line "commandline -i \n" up-line force-repaint # doesn't work + #bind --preset -m insert o "commandline -a \n" down-line force-repaint + #bind --preset -m insert O beginning-of-line "commandline -i \n" up-line force-repaint # doesn't work - bind gg beginning-of-buffer - bind G end-of-buffer + bind --preset gg beginning-of-buffer + bind --preset G end-of-buffer for key in $eol_keys - bind $key end-of-line + bind --preset $key end-of-line end for key in $bol_keys - bind $key beginning-of-line + bind --preset $key beginning-of-line end - bind u history-search-backward - bind \cr history-search-forward + bind --preset u history-search-backward + bind --preset \cr history-search-forward - bind [ history-token-search-backward - bind ] history-token-search-forward + bind --preset [ history-token-search-backward + bind --preset ] history-token-search-forward - bind k up-or-search - bind j down-or-search - bind b backward-word - bind B backward-bigword - bind ge backward-word - bind gE backward-bigword - bind w forward-word forward-char - bind W forward-bigword forward-char - bind e forward-char forward-word backward-char - bind E forward-bigword backward-char + bind --preset k up-or-search + bind --preset j down-or-search + bind --preset b backward-word + bind --preset B backward-bigword + bind --preset ge backward-word + bind --preset gE backward-bigword + bind --preset w forward-word forward-char + bind --preset W forward-bigword forward-char + bind --preset e forward-char forward-word backward-char + bind --preset E forward-bigword backward-char # OS X SnowLeopard doesn't have these keys. Don't show an annoying error message. # Vi/Vim doesn't support these keys in insert mode but that seems silly so we do so anyway. - bind $argv insert -k home beginning-of-line 2>/dev/null - bind $argv default -k home beginning-of-line 2>/dev/null - bind $argv insert -k end end-of-line 2>/dev/null - bind $argv default -k end end-of-line 2>/dev/null + bind --preset $argv insert -k home beginning-of-line 2>/dev/null + bind --preset $argv default -k home beginning-of-line 2>/dev/null + bind --preset $argv insert -k end end-of-line 2>/dev/null + bind --preset $argv default -k end end-of-line 2>/dev/null # Vi moves the cursor back if, after deleting, it is at EOL. # To emulate that, move forward, then backward, which will be a NOP # if there is something to move forward to. - bind $argv default x delete-char forward-char backward-char - bind $argv default X backward-delete-char - bind $argv insert -k dc delete-char forward-char backward-char - bind $argv default -k dc delete-char forward-char backward-char + bind --preset $argv default x delete-char forward-char backward-char + bind --preset $argv default X backward-delete-char + bind --preset $argv insert -k dc delete-char forward-char backward-char + bind --preset $argv default -k dc delete-char forward-char backward-char # Backspace deletes a char in insert mode, but not in normal/default mode. - bind $argv insert -k backspace backward-delete-char - bind $argv default -k backspace backward-char - bind $argv insert \ch backward-delete-char - bind $argv default \ch backward-char - bind $argv insert \x7f backward-delete-char - bind $argv default \x7f backward-char - bind $argv insert \e\[3\;2~ backward-delete-char # Mavericks Terminal.app shift-ctrl-delete - bind $argv default \e\[3\;2~ backward-delete-char # Mavericks Terminal.app shift-ctrl-delete + bind --preset $argv insert -k backspace backward-delete-char + bind --preset $argv default -k backspace backward-char + bind --preset $argv insert \ch backward-delete-char + bind --preset $argv default \ch backward-char + bind --preset $argv insert \x7f backward-delete-char + bind --preset $argv default \x7f backward-char + bind --preset $argv insert \e\[3\;2~ backward-delete-char # Mavericks Terminal.app shift-ctrl-delete + bind --preset $argv default \e\[3\;2~ backward-delete-char # Mavericks Terminal.app shift-ctrl-delete - bind dd kill-whole-line - bind D kill-line - bind d\$ kill-line - bind d\^ backward-kill-line - bind dw kill-word - bind dW kill-bigword - bind diw forward-char forward-char backward-word kill-word - bind diW forward-char forward-char backward-bigword kill-bigword - bind daw forward-char forward-char backward-word kill-word - bind daW forward-char forward-char backward-bigword kill-bigword - bind de kill-word - bind dE kill-bigword - bind db backward-kill-word - bind dB backward-kill-bigword - bind dge backward-kill-word - bind dgE backward-kill-bigword - bind df begin-selection forward-jump kill-selection end-selection - bind dt begin-selection forward-jump backward-char kill-selection end-selection - bind dF begin-selection backward-jump kill-selection end-selection - bind dT begin-selection backward-jump forward-char kill-selection end-selection + bind --preset dd kill-whole-line + bind --preset D kill-line + bind --preset d\$ kill-line + bind --preset d\^ backward-kill-line + bind --preset dw kill-word + bind --preset dW kill-bigword + bind --preset diw forward-char forward-char backward-word kill-word + bind --preset diW forward-char forward-char backward-bigword kill-bigword + bind --preset daw forward-char forward-char backward-word kill-word + bind --preset daW forward-char forward-char backward-bigword kill-bigword + bind --preset de kill-word + bind --preset dE kill-bigword + bind --preset db backward-kill-word + bind --preset dB backward-kill-bigword + bind --preset dge backward-kill-word + bind --preset dgE backward-kill-bigword + bind --preset df begin-selection forward-jump kill-selection end-selection + bind --preset dt begin-selection forward-jump backward-char kill-selection end-selection + bind --preset dF begin-selection backward-jump kill-selection end-selection + bind --preset dT begin-selection backward-jump forward-char kill-selection end-selection - bind -m insert s delete-char force-repaint - bind -m insert S kill-whole-line force-repaint - bind -m insert cc kill-whole-line force-repaint - bind -m insert C kill-line force-repaint - bind -m insert c\$ kill-line force-repaint - bind -m insert c\^ backward-kill-line force-repaint - bind -m insert cw kill-word force-repaint - bind -m insert cW kill-bigword force-repaint - bind -m insert ciw forward-char forward-char backward-word kill-word force-repaint - bind -m insert ciW forward-char forward-char backward-bigword kill-bigword force-repaint - bind -m insert caw forward-char forward-char backward-word kill-word force-repaint - bind -m insert caW forward-char forward-char backward-bigword kill-bigword force-repaint - bind -m insert ce kill-word force-repaint - bind -m insert cE kill-bigword force-repaint - bind -m insert cb backward-kill-word force-repaint - bind -m insert cB backward-kill-bigword force-repaint - bind -m insert cge backward-kill-word force-repaint - bind -m insert cgE backward-kill-bigword force-repaint + bind --preset -m insert s delete-char force-repaint + bind --preset -m insert S kill-whole-line force-repaint + bind --preset -m insert cc kill-whole-line force-repaint + bind --preset -m insert C kill-line force-repaint + bind --preset -m insert c\$ kill-line force-repaint + bind --preset -m insert c\^ backward-kill-line force-repaint + bind --preset -m insert cw kill-word force-repaint + bind --preset -m insert cW kill-bigword force-repaint + bind --preset -m insert ciw forward-char forward-char backward-word kill-word force-repaint + bind --preset -m insert ciW forward-char forward-char backward-bigword kill-bigword force-repaint + bind --preset -m insert caw forward-char forward-char backward-word kill-word force-repaint + bind --preset -m insert caW forward-char forward-char backward-bigword kill-bigword force-repaint + bind --preset -m insert ce kill-word force-repaint + bind --preset -m insert cE kill-bigword force-repaint + bind --preset -m insert cb backward-kill-word force-repaint + bind --preset -m insert cB backward-kill-bigword force-repaint + bind --preset -m insert cge backward-kill-word force-repaint + bind --preset -m insert cgE backward-kill-bigword force-repaint - bind '~' capitalize-word - bind gu downcase-word - bind gU upcase-word + bind --preset '~' capitalize-word + bind --preset gu downcase-word + bind --preset gU upcase-word - bind J end-of-line delete-char - bind K 'man (commandline -t) 2>/dev/null; or echo -n \a' + bind --preset J end-of-line delete-char + bind --preset K 'man (commandline -t) 2>/dev/null; or echo -n \a' - bind yy kill-whole-line yank - bind Y kill-whole-line yank - bind y\$ kill-line yank - bind y\^ backward-kill-line yank - bind yw kill-word yank - bind yW kill-bigword yank - bind yiw forward-char forward-char backward-word kill-word yank - bind yiW forward-char forward-char backward-bigword kill-bigword yank - bind yaw forward-char forward-char backward-word kill-word yank - bind yaW forward-char forward-char backward-bigword kill-bigword yank - bind ye kill-word yank - bind yE kill-bigword yank - bind yb backward-kill-word yank - bind yB backward-kill-bigword yank - bind yge backward-kill-word yank - bind ygE backward-kill-bigword yank + bind --preset yy kill-whole-line yank + bind --preset Y kill-whole-line yank + bind --preset y\$ kill-line yank + bind --preset y\^ backward-kill-line yank + bind --preset yw kill-word yank + bind --preset yW kill-bigword yank + bind --preset yiw forward-char forward-char backward-word kill-word yank + bind --preset yiW forward-char forward-char backward-bigword kill-bigword yank + bind --preset yaw forward-char forward-char backward-word kill-word yank + bind --preset yaW forward-char forward-char backward-bigword kill-bigword yank + bind --preset ye kill-word yank + bind --preset yE kill-bigword yank + bind --preset yb backward-kill-word yank + bind --preset yB backward-kill-bigword yank + bind --preset yge backward-kill-word yank + bind --preset ygE backward-kill-bigword yank - bind f forward-jump - bind F backward-jump - bind t forward-jump-till - bind T backward-jump-till - bind ';' repeat-jump - bind , repeat-jump-reverse + bind --preset f forward-jump + bind --preset F backward-jump + bind --preset t forward-jump-till + bind --preset T backward-jump-till + bind --preset ';' repeat-jump + bind --preset , repeat-jump-reverse # in emacs yank means paste - bind p yank - bind P backward-char yank - bind gp yank-pop + bind --preset p yank + bind --preset P backward-char yank + bind --preset gp yank-pop - bind '"*p' "commandline -i ( xsel -p; echo )[1]" - bind '"*P' backward-char "commandline -i ( xsel -p; echo )[1]" + bind --preset '"*p' "commandline -i ( xsel -p; echo )[1]" + bind --preset '"*P' backward-char "commandline -i ( xsel -p; echo )[1]" # # Lowercase r, enters replace_one mode # - bind -m replace_one r force-repaint - bind $argv replace_one -m default '' delete-char self-insert backward-char force-repaint - bind $argv replace_one -m default \e cancel force-repaint + bind --preset -m replace_one r force-repaint + bind --preset $argv replace_one -m default '' delete-char self-insert backward-char force-repaint + bind --preset $argv replace_one -m default \e cancel force-repaint # # visual mode # - bind $argv visual h backward-char - bind $argv visual l forward-char + bind --preset $argv visual h backward-char + bind --preset $argv visual l forward-char - bind $argv visual k up-line - bind $argv visual j down-line + bind --preset $argv visual k up-line + bind --preset $argv visual j down-line - bind $argv visual b backward-word - bind $argv visual B backward-bigword - bind $argv visual ge backward-word - bind $argv visual gE backward-bigword - bind $argv visual w forward-word - bind $argv visual W forward-bigword - bind $argv visual e forward-word - bind $argv visual E forward-bigword - bind $argv visual o swap-selection-start-stop force-repaint + bind --preset $argv visual b backward-word + bind --preset $argv visual B backward-bigword + bind --preset $argv visual ge backward-word + bind --preset $argv visual gE backward-bigword + bind --preset $argv visual w forward-word + bind --preset $argv visual W forward-bigword + bind --preset $argv visual e forward-word + bind --preset $argv visual E forward-bigword + bind --preset $argv visual o swap-selection-start-stop force-repaint - bind $argv visual f forward-jump - bind $argv visual t forward-jump-till - bind $argv visual F backward-jump - bind $argv visual T backward-jump-till + bind --preset $argv visual f forward-jump + bind --preset $argv visual t forward-jump-till + bind --preset $argv visual F backward-jump + bind --preset $argv visual T backward-jump-till for key in $eol_keys - bind $argv visual $key end-of-line + bind --preset $argv visual $key end-of-line end for key in $bol_keys - bind $argv visual $key beginning-of-line + bind --preset $argv visual $key beginning-of-line end - bind $argv visual -m insert c kill-selection end-selection force-repaint - bind $argv visual -m default d kill-selection end-selection force-repaint - bind $argv visual -m default x kill-selection end-selection force-repaint - bind $argv visual -m default X kill-whole-line end-selection force-repaint - bind $argv visual -m default y kill-selection yank end-selection force-repaint - bind $argv visual -m default '"*y' "commandline -s | xsel -p; commandline -f end-selection force-repaint" + bind --preset $argv visual -m insert c kill-selection end-selection force-repaint + bind --preset $argv visual -m default d kill-selection end-selection force-repaint + bind --preset $argv visual -m default x kill-selection end-selection force-repaint + bind --preset $argv visual -m default X kill-whole-line end-selection force-repaint + bind --preset $argv visual -m default y kill-selection yank end-selection force-repaint + bind --preset $argv visual -m default '"*y' "commandline -s | xsel -p; commandline -f end-selection force-repaint" - bind $argv visual -m default \cc end-selection force-repaint - bind $argv visual -m default \e end-selection force-repaint + bind --preset $argv visual -m default \cc end-selection force-repaint + bind --preset $argv visual -m default \e end-selection force-repaint # Make it easy to turn an unexecuted command into a comment in the shell history. Also, remove # the commenting chars so the command can be further edited then executed. - bind $argv default \# __fish_toggle_comment_commandline - bind $argv visual \# __fish_toggle_comment_commandline + bind --preset $argv default \# __fish_toggle_comment_commandline + bind --preset $argv visual \# __fish_toggle_comment_commandline # Set the cursor shape # After executing once, this will have defined functions listening for the variable. diff --git a/src/builtin_bind.cpp b/src/builtin_bind.cpp index 179194cf8..533d48399 100644 --- a/src/builtin_bind.cpp +++ b/src/builtin_bind.cpp @@ -26,6 +26,10 @@ struct bind_cmd_opts_t { bool print_help = false; bool silent = false; bool use_terminfo = false; + bool have_user = false; + bool user = false; + bool have_preset = false; + bool preset = false; int mode = BIND_INSERT; const wchar_t *bind_mode = DEFAULT_BIND_MODE; const wchar_t *sets_bind_mode = L""; @@ -47,18 +51,21 @@ struct bind_cmd_opts_t { /// List a single key binding. /// Returns false if no binding with that sequence and mode exists. -bool builtin_bind_t::list_one(const wcstring &seq, const wcstring &bind_mode, +bool builtin_bind_t::list_one(const wcstring &seq, const wcstring &bind_mode, bool user, io_streams_t &streams) { std::vector ecmds; wcstring sets_mode; - if (!input_mapping_get(seq, bind_mode, &ecmds, &sets_mode)) { + if (!input_mapping_get(seq, bind_mode, &ecmds, user, &sets_mode)) { return false; } streams.out.append(L"bind"); // Append the mode flags if applicable. + if (!user) { + streams.out.append(L" --preset"); + } if (bind_mode != DEFAULT_BIND_MODE) { const wcstring emode = escape_string(bind_mode, ESCAPE_ALL); streams.out.append(L" -M "); @@ -93,16 +100,30 @@ bool builtin_bind_t::list_one(const wcstring &seq, const wcstring &bind_mode, return true; } +// Overload with both kinds of bindings. +// Returns false only if neither exists. +bool builtin_bind_t::list_one(const wcstring &seq, const wcstring &bind_mode, bool user, bool preset, + io_streams_t &streams) { + bool retval = false; + if (preset) { + retval |= list_one(seq, bind_mode, false, streams); + } + if (user) { + retval |= list_one(seq, bind_mode, true, streams); + } + return retval; +} + /// List all current key bindings. -void builtin_bind_t::list(const wchar_t *bind_mode, io_streams_t &streams) { - const std::vector lst = input_mapping_get_names(); +void builtin_bind_t::list(const wchar_t *bind_mode, bool user, io_streams_t &streams) { + const std::vector lst = input_mapping_get_names(user); for (const input_mapping_name_t &binding : lst) { if (bind_mode && bind_mode != binding.mode) { continue; } - list_one(binding.seq, binding.mode, streams); + list_one(binding.seq, binding.mode, user, streams); } } @@ -110,7 +131,7 @@ void builtin_bind_t::list(const wchar_t *bind_mode, io_streams_t &streams) { /// /// \param all if set, all terminfo key binding names will be printed. If not set, only ones that /// are defined for this terminal are printed. -void builtin_bind_t::key_names(int all, io_streams_t &streams) { +void builtin_bind_t::key_names(bool all, io_streams_t &streams) { const wcstring_list_t names = input_terminfo_get_names(!all); for (size_t i = 0; i < names.size(); i++) { const wcstring &name = names.at(i); @@ -152,18 +173,18 @@ bool builtin_bind_t::get_terminfo_sequence(const wchar_t *seq, wcstring *out_seq /// Add specified key binding. bool builtin_bind_t::add(const wchar_t *seq, const wchar_t *const *cmds, size_t cmds_len, - const wchar_t *mode, const wchar_t *sets_mode, int terminfo, + const wchar_t *mode, const wchar_t *sets_mode, bool terminfo, bool user, io_streams_t &streams) { if (terminfo) { wcstring seq2; if (get_terminfo_sequence(seq, &seq2, streams)) { - input_mapping_add(seq2.c_str(), cmds, cmds_len, mode, sets_mode); + input_mapping_add(seq2.c_str(), cmds, cmds_len, mode, sets_mode, user); } else { return true; } } else { - input_mapping_add(seq, cmds, cmds_len, mode, sets_mode); + input_mapping_add(seq, cmds, cmds_len, mode, sets_mode, user); } return false; @@ -181,17 +202,10 @@ bool builtin_bind_t::add(const wchar_t *seq, const wchar_t *const *cmds, size_t /// @param use_terminfo /// Whether to look use terminfo -k name /// -bool builtin_bind_t::erase(wchar_t **seq, int all, const wchar_t *mode, int use_terminfo, +bool builtin_bind_t::erase(wchar_t **seq, bool all, const wchar_t *mode, bool use_terminfo, bool user, io_streams_t &streams) { if (all) { - const std::vector lst = input_mapping_get_names(); - for (std::vector::const_iterator it = lst.begin(), end = lst.end(); - it != end; ++it) { - if (mode == NULL || mode == it->mode) { - input_mapping_erase(it->seq, it->mode); - } - } - + input_mapping_clear(mode, user); return false; } @@ -202,12 +216,12 @@ bool builtin_bind_t::erase(wchar_t **seq, int all, const wchar_t *mode, int use_ if (use_terminfo) { wcstring seq2; if (get_terminfo_sequence(*seq++, &seq2, streams)) { - input_mapping_erase(seq2, mode); + input_mapping_erase(seq2, mode, user); } else { res = true; } } else { - input_mapping_erase(*seq++, mode); + input_mapping_erase(*seq++, mode, user); } } @@ -219,8 +233,30 @@ bool builtin_bind_t::insert(int optind, int argc, wchar_t **argv, wchar_t *cmd = argv[0]; int arg_count = argc - optind; + if (arg_count < 2) { + // If we get both or neither preset/user, we list both. + if (!opts->have_preset && !opts->have_user) { + opts->preset = true; + opts->user = true; + } + } else { + // Inserting both on the other hand makes no sense. + if (opts->have_preset && opts->have_user) { + streams.err.append_format(BUILTIN_ERR_COMBO2, cmd, + L"--preset and --user can not be used together when inserting bindings."); + return true; + } + } + if (arg_count == 0) { - list(opts->bind_mode_given ? opts->bind_mode : NULL, streams); + // We don't overload this with user and def because we want them to be grouped. + // First the presets, then the users (because of scrolling). + if (opts->preset) { + list(opts->bind_mode_given ? opts->bind_mode : NULL, false, streams); + } + if (opts->user) { + list(opts->bind_mode_given ? opts->bind_mode : NULL, true, streams); + } } else if (arg_count == 1) { wcstring seq; if (opts->use_terminfo) { @@ -232,7 +268,7 @@ bool builtin_bind_t::insert(int optind, int argc, wchar_t **argv, seq = argv[optind]; } - if (!list_one(seq, opts->bind_mode, streams)) { + if (!list_one(seq, opts->bind_mode, opts->user, opts->preset, streams)) { wcstring eseq = escape_string(argv[optind], 0); if (!opts->silent) { if (opts->use_terminfo) { @@ -246,8 +282,9 @@ bool builtin_bind_t::insert(int optind, int argc, wchar_t **argv, return true; } } else { + // Actually insert! if (add(argv[optind], argv + (optind + 1), argc - (optind + 1), opts->bind_mode, - opts->sets_bind_mode, opts->use_terminfo, streams)) { + opts->sets_bind_mode, opts->use_terminfo, opts->user, streams)) { return true; } } @@ -257,7 +294,9 @@ bool builtin_bind_t::insert(int optind, int argc, wchar_t **argv, /// List all current bind modes. void builtin_bind_t::list_modes(io_streams_t &streams) { - const std::vector lst = input_mapping_get_names(); + // List all known modes, even if they are only in preset bindings. + const std::vector lst = input_mapping_get_names(true); + const std::vector preset_lst = input_mapping_get_names(false); // A set accomplishes two things for us here: // - It removes duplicates (no twenty "default" entries). // - It sorts it, which makes it nicer on the user. @@ -266,6 +305,9 @@ void builtin_bind_t::list_modes(io_streams_t &streams) { for (const input_mapping_name_t &binding : lst) { modes.insert(binding.mode); } + for (const input_mapping_name_t &binding : preset_lst) { + modes.insert(binding.mode); + } for (const auto &mode : modes) { streams.out.append_format(L"%ls\n", mode.c_str()); } @@ -283,8 +325,10 @@ int parse_cmd_opts(bind_cmd_opts_t &opts, int *optind, //!OCLINT(high ncss meth {L"key-names", no_argument, NULL, 'K'}, {L"list-modes", no_argument, NULL, 'L'}, {L"mode", required_argument, NULL, 'M'}, + {L"preset", no_argument, NULL, 'p'}, {L"sets-mode", required_argument, NULL, 'm'}, {L"silent", no_argument, NULL, 's'}, + {L"user", no_argument, NULL, 'u'}, {NULL, 0, NULL, 0}}; int opt; @@ -336,10 +380,20 @@ int parse_cmd_opts(bind_cmd_opts_t &opts, int *optind, //!OCLINT(high ncss meth opts.sets_bind_mode = w.woptarg; break; } + case L'p': { + opts.have_preset = true; + opts.preset = true; + break; + } case L's': { opts.silent = true; break; } + case L'u': { + opts.have_user = true; + opts.user = true; + break; + } case ':': { builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]); return STATUS_INVALID_ARGS; @@ -379,12 +433,23 @@ int builtin_bind_t::builtin_bind(parser_t &parser, io_streams_t &streams, wchar_ return STATUS_CMD_OK; } + // Default to user mode + if (!opts.have_preset && !opts.have_user) opts.user = true; switch (opts.mode) { case BIND_ERASE: { const wchar_t *bind_mode = opts.bind_mode_given ? opts.bind_mode : NULL; - if (erase(&argv[optind], opts.all, bind_mode, opts.use_terminfo, - streams)) { - return STATUS_CMD_ERROR; + // If we get both, we erase both. + if (opts.user) { + if (erase(&argv[optind], opts.all, bind_mode, opts.use_terminfo, /* user */ true, + streams)) { + return STATUS_CMD_ERROR; + } + } + if (opts.preset) { + if (erase(&argv[optind], opts.all, bind_mode, opts.use_terminfo, /* user */ false, + streams)) { + return STATUS_CMD_ERROR; + } } break; } diff --git a/src/builtin_bind.h b/src/builtin_bind.h index f1d61aaf7..12ba7cf49 100644 --- a/src/builtin_bind.h +++ b/src/builtin_bind.h @@ -16,19 +16,20 @@ class builtin_bind_t { private: bind_cmd_opts_t *opts; - void list(const wchar_t *bind_mode, io_streams_t &streams); - void key_names(int all, io_streams_t &streams); + void list(const wchar_t *bind_mode, bool user, io_streams_t &streams); + void key_names(bool all, io_streams_t &streams); void function_names(io_streams_t &streams); bool add(const wchar_t *seq, const wchar_t *const *cmds, size_t cmds_len, - const wchar_t *mode, const wchar_t *sets_mode, int terminfo, + const wchar_t *mode, const wchar_t *sets_mode, bool terminfo, bool user, io_streams_t &streams); - bool erase(wchar_t **seq, int all, const wchar_t *mode, int use_terminfo, + bool erase(wchar_t **seq, bool all, const wchar_t *mode, bool use_terminfo, bool user, io_streams_t &streams); bool get_terminfo_sequence(const wchar_t *seq, wcstring *out_seq, io_streams_t &streams); bool insert(int optind, int argc, wchar_t **argv, io_streams_t &streams); void list_modes(io_streams_t &streams); - bool list_one(const wcstring &seq, const wcstring &bind_mode, io_streams_t &streams); + bool list_one(const wcstring &seq, const wcstring &bind_mode, bool user, io_streams_t &streams); + bool list_one(const wcstring &seq, const wcstring &bind_mode, bool user, bool preset, io_streams_t &streams); }; inline int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv) { diff --git a/src/input.cpp b/src/input.cpp index 4d90df153..94b4f9cde 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -142,6 +142,7 @@ wcstring describe_char(wint_t c) { /// Mappings for the current input mode. static std::vector mapping_list; +static std::vector preset_mapping_list; /// Terminfo map list. static std::vector terminfo_mappings; @@ -205,15 +206,17 @@ static bool specification_order_is_less_than(const input_mapping_t &m1, const in /// Inserts an input mapping at the correct position. We sort them in descending order by length, so /// that we test longer sequences first. -static void input_mapping_insert_sorted(const input_mapping_t &new_mapping) { +static void input_mapping_insert_sorted(const input_mapping_t &new_mapping, bool user = true) { + auto& ml = user ? mapping_list : preset_mapping_list; + std::vector::iterator loc = std::lower_bound( - mapping_list.begin(), mapping_list.end(), new_mapping, length_is_greater_than); - mapping_list.insert(loc, new_mapping); + ml.begin(), ml.end(), new_mapping, length_is_greater_than); + ml.insert(loc, new_mapping); } /// Adds an input mapping. void input_mapping_add(const wchar_t *sequence, const wchar_t *const *commands, size_t commands_len, - const wchar_t *mode, const wchar_t *sets_mode) { + const wchar_t *mode, const wchar_t *sets_mode, bool user) { CHECK(sequence, ); CHECK(commands, ); CHECK(mode, ); @@ -226,8 +229,10 @@ void input_mapping_add(const wchar_t *sequence, const wchar_t *const *commands, // Remove existing mappings with this sequence. const wcstring_list_t commands_vector(commands, commands + commands_len); - for (size_t i = 0; i < mapping_list.size(); i++) { - input_mapping_t &m = mapping_list.at(i); + auto& ml = user ? mapping_list : preset_mapping_list; + + for (size_t i = 0; i < ml.size(); i++) { + input_mapping_t &m = ml.at(i); if (m.seq == sequence && m.mode == mode) { m.commands = commands_vector; m.sets_mode = sets_mode; @@ -237,12 +242,12 @@ void input_mapping_add(const wchar_t *sequence, const wchar_t *const *commands, // Add a new mapping, using the next order. const input_mapping_t new_mapping = input_mapping_t(sequence, commands_vector, mode, sets_mode); - input_mapping_insert_sorted(new_mapping); + input_mapping_insert_sorted(new_mapping, user); } void input_mapping_add(const wchar_t *sequence, const wchar_t *command, const wchar_t *mode, - const wchar_t *sets_mode) { - input_mapping_add(sequence, &command, 1, mode, sets_mode); + const wchar_t *sets_mode, bool user) { + input_mapping_add(sequence, &command, 1, mode, sets_mode, user); } /// Handle interruptions to key reading by reaping finshed jobs and propagating the interrupt to the @@ -267,20 +272,20 @@ void init_input() { init_input_terminfo(); // If we have no keybindings, add a few simple defaults. - if (mapping_list.empty()) { - input_mapping_add(L"", L"self-insert"); - input_mapping_add(L"\n", L"execute"); - input_mapping_add(L"\r", L"execute"); - input_mapping_add(L"\t", L"complete"); - input_mapping_add(L"\x3", L"commandline ''"); - input_mapping_add(L"\x4", L"exit"); - input_mapping_add(L"\x5", L"bind"); - input_mapping_add(L"\x7f", L"backward-delete-char"); + if (preset_mapping_list.empty()) { + input_mapping_add(L"", L"self-insert", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false); + input_mapping_add(L"\n", L"execute", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false); + input_mapping_add(L"\r", L"execute", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false); + input_mapping_add(L"\t", L"complete", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false); + input_mapping_add(L"\x3", L"commandline ''", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false); + input_mapping_add(L"\x4", L"exit", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false); + input_mapping_add(L"\x5", L"bind", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false); + input_mapping_add(L"\x7f", L"backward-delete-char", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false); // Arrows - can't have functions, so *-or-search isn't available. - input_mapping_add(L"\x1B[A", L"up-line"); - input_mapping_add(L"\x1B[B", L"down-line"); - input_mapping_add(L"\x1B[C", L"forward-char"); - input_mapping_add(L"\x1B[D", L"backward-char"); + input_mapping_add(L"\x1B[A", L"up-line", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false); + input_mapping_add(L"\x1B[B", L"down-line", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false); + input_mapping_add(L"\x1B[C", L"forward-char", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false); + input_mapping_add(L"\x1B[D", L"backward-char", DEFAULT_BIND_MODE, DEFAULT_BIND_MODE, false); } input_initialized = true; @@ -415,16 +420,8 @@ static void input_mapping_execute_matching_or_generic(bool allow_commands) { const wcstring bind_mode = input_get_bind_mode(); - for (size_t i = 0; i < mapping_list.size(); i++) { - const input_mapping_t &m = mapping_list.at(i); - - // debug(0, L"trying mapping (%ls,%ls,%ls)\n", escape_string(m.seq.c_str(), - // ESCAPE_ALL).c_str(), - // m.mode.c_str(), m.sets_mode.c_str()); - + for (auto& m : mapping_list) { if (m.mode != bind_mode) { - // debug(0, L"skipping mapping because mode %ls != %ls\n", m.mode.c_str(), - // input_get_bind_mode().c_str()); continue; } @@ -436,6 +433,21 @@ static void input_mapping_execute_matching_or_generic(bool allow_commands) { } } + // HACK: This is ugly duplication. + for (auto& m : preset_mapping_list) { + if (m.mode != bind_mode) { + continue; + } + + if (m.seq.length() == 0) { + // Only use this generic if the user list didn't have one. + if (!generic) generic = &m; + } else if (input_mapping_is_match(m)) { + input_mapping_execute(m, allow_commands); + return; + } + } + if (generic) { input_mapping_execute(*generic, allow_commands); } else { @@ -515,10 +527,10 @@ wint_t input_readch(bool allow_commands) { } } -std::vector input_mapping_get_names() { +std::vector input_mapping_get_names(bool user) { // Sort the mappings by the user specification order, so we can return them in the same order // that the user specified them in. - std::vector local_list = mapping_list; + std::vector local_list = user ? mapping_list : preset_mapping_list; std::sort(local_list.begin(), local_list.end(), specification_order_is_less_than); std::vector result; result.reserve(local_list.size()); @@ -530,14 +542,25 @@ std::vector input_mapping_get_names() { return result; } -bool input_mapping_erase(const wcstring &sequence, const wcstring &mode) { +void input_mapping_clear(const wchar_t *mode, bool user) { + auto& ml = user ? mapping_list : preset_mapping_list; + for (std::vector::iterator it = ml.begin(), end = ml.end(); + it != end; ++it) { + if (mode == NULL || mode == it->mode) { + ml.erase(it); + } + } +} + +bool input_mapping_erase(const wcstring &sequence, const wcstring &mode, bool user) { ASSERT_IS_MAIN_THREAD(); bool result = false; - for (std::vector::iterator it = mapping_list.begin(), end = mapping_list.end(); + auto& ml = user ? mapping_list : preset_mapping_list; + for (std::vector::iterator it = ml.begin(), end = ml.end(); it != end; ++it) { if (sequence == it->seq && mode == it->mode) { - mapping_list.erase(it); + ml.erase(it); result = true; break; } @@ -545,11 +568,12 @@ bool input_mapping_erase(const wcstring &sequence, const wcstring &mode) { return result; } -bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_list_t *out_cmds, +bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_list_t *out_cmds, bool user, wcstring *out_sets_mode) { bool result = false; - for (std::vector::const_iterator it = mapping_list.begin(), - end = mapping_list.end(); + auto& ml = user ? mapping_list : preset_mapping_list; + for (std::vector::const_iterator it = ml.begin(), + end = ml.end(); it != end; ++it) { if (sequence == it->seq && mode == it->mode) { *out_cmds = it->commands; diff --git a/src/input.h b/src/input.h index c1df12acd..553a68b19 100644 --- a/src/input.h +++ b/src/input.h @@ -48,11 +48,11 @@ void input_queue_ch(wint_t ch); /// \param command an input function that will be run whenever the key sequence occurs void input_mapping_add(const wchar_t *sequence, const wchar_t *command, const wchar_t *mode = DEFAULT_BIND_MODE, - const wchar_t *new_mode = DEFAULT_BIND_MODE); + const wchar_t *new_mode = DEFAULT_BIND_MODE, bool user = true); void input_mapping_add(const wchar_t *sequence, const wchar_t *const *commands, size_t commands_len, const wchar_t *mode = DEFAULT_BIND_MODE, - const wchar_t *new_mode = DEFAULT_BIND_MODE); + const wchar_t *new_mode = DEFAULT_BIND_MODE, bool user = true); struct input_mapping_name_t { wcstring seq; @@ -60,14 +60,17 @@ struct input_mapping_name_t { }; /// Returns all mapping names and modes. -std::vector input_mapping_get_names(); +std::vector input_mapping_get_names(bool user = true); + +/// Erase all bindings +void input_mapping_clear(const wchar_t *mode = NULL, bool user = true); /// Erase binding for specified key sequence. -bool input_mapping_erase(const wcstring &sequence, const wcstring &mode = DEFAULT_BIND_MODE); +bool input_mapping_erase(const wcstring &sequence, const wcstring &mode = DEFAULT_BIND_MODE, bool user = true); /// Gets the command bound to the specified key sequence in the specified mode. Returns true if it /// exists, false if not. -bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_list_t *out_cmds, +bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_list_t *out_cmds, bool user, wcstring *out_new_mode); /// Return the current bind mode. diff --git a/tests/bind.err b/tests/bind.err index 68fd269ef..8b777ec7b 100644 --- a/tests/bind.err +++ b/tests/bind.err @@ -2,3 +2,4 @@ bind: mode name 'bad bind mode' is not valid. See `help identifiers`. # Verify that an invalid bind mode target is rejected. bind: mode name 'bind-mode' is not valid. See `help identifiers`. +bind: No binding found for sequence '\t' diff --git a/tests/bind.in b/tests/bind.in index 7fbf373ad..17c7e7bb8 100644 --- a/tests/bind.in +++ b/tests/bind.in @@ -9,3 +9,25 @@ bind -M bind-mode \cX true # This should succeed and result in a success, zero, status. bind -M bind_mode \cX true + +### HACK: All full bind listings need to have the \x7f -> backward-delete-char +# binding explicitly removed, because on some systems that's backspace, on others not. +echo \# Listing bindings +bind | string match -v '*backward-delete-char' +bind --user --preset | string match -v '*backward-delete-char' +echo \# Preset only +bind --preset | string match -v '*backward-delete-char' +echo \# User only +bind --user | string match -v '*backward-delete-char' + +echo \# Adding bindings +bind \t 'echo banana' +bind | string match -v '*backward-delete-char' +echo \# Erasing bindings +bind --erase \t +bind \t +bind \t 'echo wurst' +bind --erase --user --preset \t +bind \t + +exit 0 diff --git a/tests/bind.out b/tests/bind.out index e69de29bb..39053cc6c 100644 --- a/tests/bind.out +++ b/tests/bind.out @@ -0,0 +1,55 @@ +# Listing bindings +bind --preset '' self-insert +bind --preset \n execute +bind --preset \r execute +bind --preset \t complete +bind --preset \cc commandline\ \'\' +bind --preset \cd exit +bind --preset \ce bind +bind --preset \e\[A up-line +bind --preset \e\[B down-line +bind --preset \e\[C forward-char +bind --preset \e\[D backward-char +bind -M bind_mode \cx true +bind --preset '' self-insert +bind --preset \n execute +bind --preset \r execute +bind --preset \t complete +bind --preset \cc commandline\ \'\' +bind --preset \cd exit +bind --preset \ce bind +bind --preset \e\[A up-line +bind --preset \e\[B down-line +bind --preset \e\[C forward-char +bind --preset \e\[D backward-char +bind -M bind_mode \cx true +# Preset only +bind --preset '' self-insert +bind --preset \n execute +bind --preset \r execute +bind --preset \t complete +bind --preset \cc commandline\ \'\' +bind --preset \cd exit +bind --preset \ce bind +bind --preset \e\[A up-line +bind --preset \e\[B down-line +bind --preset \e\[C forward-char +bind --preset \e\[D backward-char +# User only +bind -M bind_mode \cx true +# Adding bindings +bind --preset '' self-insert +bind --preset \n execute +bind --preset \r execute +bind --preset \t complete +bind --preset \cc commandline\ \'\' +bind --preset \cd exit +bind --preset \ce bind +bind --preset \e\[A up-line +bind --preset \e\[B down-line +bind --preset \e\[C forward-char +bind --preset \e\[D backward-char +bind -M bind_mode \cx true +bind \t 'echo banana' +# Erasing bindings +bind --preset \t complete