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