Add separation of "preset" bindings

This allows for marking certain bindings as part of a preset, which allows us to

- only erase those when switching presets
- go back to the preset binding when erasing a user binding
- only show user customization if requested
- make bare bind statements in config.fish work (!!!11elf!!!)

Fixes #5191.
Fixes #3699.
This commit is contained in:
Fabian Homborg 2018-09-18 11:52:25 +02:00
parent 36a149337b
commit 444f9f8715
12 changed files with 528 additions and 353 deletions

View file

@ -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 <a href="index.html#initialization">config.fish</a> 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 <a href="tutorial.html#tut_autoload">autoloaded</a>.
To save custom keybindings, put the `bind` statements into <a href="index.html#initialization">config.fish</a>. 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:

View file

@ -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'

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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<wcstring> 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<input_mapping_name_t> lst = input_mapping_get_names();
void builtin_bind_t::list(const wchar_t *bind_mode, bool user, io_streams_t &streams) {
const std::vector<input_mapping_name_t> 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<input_mapping_name_t> lst = input_mapping_get_names();
for (std::vector<input_mapping_name_t>::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<input_mapping_name_t> lst = input_mapping_get_names();
// List all known modes, even if they are only in preset bindings.
const std::vector<input_mapping_name_t> lst = input_mapping_get_names(true);
const std::vector<input_mapping_name_t> 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,13 +433,24 @@ 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,
// 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;
}
case BIND_INSERT: {

View file

@ -16,19 +16,20 @@ public:
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) {

View file

@ -142,6 +142,7 @@ wcstring describe_char(wint_t c) {
/// Mappings for the current input mode.
static std::vector<input_mapping_t> mapping_list;
static std::vector<input_mapping_t> preset_mapping_list;
/// Terminfo map list.
static std::vector<terminfo_mapping_t> 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<input_mapping_t>::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_name_t> input_mapping_get_names() {
std::vector<input_mapping_name_t> 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<input_mapping_t> local_list = mapping_list;
std::vector<input_mapping_t> local_list = user ? mapping_list : preset_mapping_list;
std::sort(local_list.begin(), local_list.end(), specification_order_is_less_than);
std::vector<input_mapping_name_t> result;
result.reserve(local_list.size());
@ -530,14 +542,25 @@ std::vector<input_mapping_name_t> 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<input_mapping_t>::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<input_mapping_t>::iterator it = mapping_list.begin(), end = mapping_list.end();
auto& ml = user ? mapping_list : preset_mapping_list;
for (std::vector<input_mapping_t>::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<input_mapping_t>::const_iterator it = mapping_list.begin(),
end = mapping_list.end();
auto& ml = user ? mapping_list : preset_mapping_list;
for (std::vector<input_mapping_t>::const_iterator it = ml.begin(),
end = ml.end();
it != end; ++it) {
if (sequence == it->seq && mode == it->mode) {
*out_cmds = it->commands;

View file

@ -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_name_t> input_mapping_get_names();
std::vector<input_mapping_name_t> 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.

View file

@ -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'

View file

@ -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

View file

@ -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