From a00df93e13280784ba6ba53a61f80509d3d0845a Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sat, 21 Mar 2026 21:41:22 +0900 Subject: [PATCH] bash: CTRL-R now supports multi-select and batch deletion - Changed +m to --multi to enable multi-select in CTRL-R - Changed exclude to exclude-multi and {1} to {+1} so shift-delete removes all selected entries at once --- CHANGELOG.md | 2 +- shell/key-bindings.bash | 4 ++-- test/test_shell_integration.rb | 15 ++++++++++----- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07f90e5d..bc38d8de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ CHANGELOG - Improved the cache structure, reducing memory footprint per entry by 86x. - With the reduced per-entry cost, the cache now has broader coverage. - Shell integration improvements - - bash: Press `shift-delete` to delete history entries during CTRL-R search (#4715) + - bash: CTRL-R now supports multi-select and `shift-delete` to delete history entries (#4715) - fish: Improved command history (CTRL-R) (#4703) (@bitraid) - `GET /` HTTP endpoint now includes `positions` field in each match entry, providing the indices of matched characters for external highlighting (#4726) - Bug fixes diff --git a/shell/key-bindings.bash b/shell/key-bindings.bash index 70726d94..8d6548cb 100644 --- a/shell/key-bindings.bash +++ b/shell/key-bindings.bash @@ -96,7 +96,7 @@ if command -v perl > /dev/null; then set +o pipefail builtin fc -lnr -2147483648 | last_hist=$(HISTTIMEFORMAT='' builtin history 1) command perl -n -l0 -e "$script" | - FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,alt-r:toggle-raw --wrap-sign '"$'\t'"↳ ' --highlight-line --bind 'shift-delete:execute-silent(echo {1} >> \"$deletefile\")+exclude' ${FZF_CTRL_R_OPTS-} +m --read0") \ + FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,alt-r:toggle-raw --wrap-sign '"$'\t'"↳ ' --highlight-line --bind 'shift-delete:execute-silent(echo {+1} >> \"$deletefile\")+exclude-multi' --multi ${FZF_CTRL_R_OPTS-} --read0") \ FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) --query "$READLINE_LINE" ) __fzf_history_delete "$deletefile" @@ -123,7 +123,7 @@ else # awk - fallback for POSIX systems set +o pipefail builtin fc -lnr -2147483648 2> /dev/null | # ( $'\t '$'\n' )* ; ::= [^\n]* ( $'\n' )* __fzf_exec_awk "$script" | # ( $'\t'$'\000' )* - FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,alt-r:toggle-raw --wrap-sign '"$'\t'"↳ ' --highlight-line --bind 'shift-delete:execute-silent(echo {1} >> \"$deletefile\")+exclude' ${FZF_CTRL_R_OPTS-} +m --read0") \ + FZF_DEFAULT_OPTS=$(__fzf_defaults "" "-n2..,.. --scheme=history --bind=ctrl-r:toggle-sort,alt-r:toggle-raw --wrap-sign '"$'\t'"↳ ' --highlight-line --bind 'shift-delete:execute-silent(echo {+1} >> \"$deletefile\")+exclude-multi' --multi ${FZF_CTRL_R_OPTS-} --read0") \ FZF_DEFAULT_OPTS_FILE='' $(__fzfcmd) --query "$READLINE_LINE" ) __fzf_history_delete "$deletefile" diff --git a/test/test_shell_integration.rb b/test/test_shell_integration.rb index 35e27d06..cc013ce0 100644 --- a/test/test_shell_integration.rb +++ b/test/test_shell_integration.rb @@ -840,20 +840,25 @@ class TestBash < TestBase tmux.prepare tmux.send_keys 'echo to-delete-2', :Enter tmux.prepare + tmux.send_keys 'echo to-delete-3', :Enter + tmux.prepare tmux.send_keys 'echo another-keeper', :Enter tmux.prepare - # Open Ctrl-R and delete two entries + # Open Ctrl-R and delete one entry tmux.send_keys 'C-r' tmux.until { |lines| assert_operator lines.match_count, :>, 0 } tmux.send_keys 'to-delete' - tmux.until { |lines| assert_equal 2, lines.match_count } - # Delete the first match + tmux.until { |lines| assert_equal 3, lines.match_count } tmux.send_keys 'S-Delete' - tmux.until { |lines| assert_equal 1, lines.match_count } - # Delete the second match + tmux.until { |lines| assert_equal 2, lines.match_count } + + # Multi-select remaining two and delete them at once + tmux.send_keys :BTab, :BTab + tmux.until { |lines| assert_includes lines[-2], '(2)' } tmux.send_keys 'S-Delete' tmux.until { |lines| assert_equal 0, lines.match_count } + # Exit without selecting tmux.send_keys :Escape tmux.prepare