From 7c447bbdc7adb1fe0c848e171780f68cacf3e80e Mon Sep 17 00:00:00 2001 From: Jack Bates Date: Fri, 28 Feb 2020 02:47:13 -0700 Subject: [PATCH] [bash] Start C-r search with current command line (#1886) Restore the original line when search is aborted. Add --query "$READLINE_LINE" and fall back to the current behavior pre Bash 4. Co-authored-by: Junegunn Choi --- .travis.yml | 2 +- shell/key-bindings.bash | 67 +++++++++++++++++++++++------------------ test/test_go.rb | 49 +++++++++++++++--------------- 3 files changed, 64 insertions(+), 54 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2e9abfe..32b3b69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,4 +19,4 @@ script: # LC_ALL=C to avoid escape codes in # printf %q $'\355\205\214\354\212\244\355\212\270' on macOS. Bash on # macOS is built without HANDLE_MULTIBYTE? - - make install && ./install --all && LC_ALL=C tmux new-session -d && ruby test/test_go.rb + - make install && ./install --all && LC_ALL=C tmux new-session -d && ruby test/test_go.rb --verbose diff --git a/shell/key-bindings.bash b/shell/key-bindings.bash index 742a83a..fa485d2 100644 --- a/shell/key-bindings.bash +++ b/shell/key-bindings.bash @@ -51,47 +51,56 @@ __fzf_cd__() { dir=$(eval "$cmd" | FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} --reverse $FZF_DEFAULT_OPTS $FZF_ALT_C_OPTS" $(__fzfcmd) +m) && printf 'cd %q' "$dir" } -__fzf_history__() ( - local line - line=$( +__fzf_history__() { + local output + output=$( builtin fc -lnr -2147483648 | - perl -p -l0 -e 'BEGIN { getc; $/ = "\n\t" } s/^[ *]//; $_ = '"$1"' - $. . "\t$_"' | - FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS --tiebreak=index --bind=ctrl-r:toggle-sort $FZF_CTRL_R_OPTS +m --read0" $(__fzfcmd) - ) - echo "${line#*$'\t'}" -) + last_hist=$(HISTTIMEFORMAT='' builtin history 1) perl -p -l0 -e 'BEGIN { getc; $/ = "\n\t"; $HISTCMD = $ENV{last_hist} + 1 } s/^[ *]//; $_ = $HISTCMD - $. . "\t$_"' | + FZF_DEFAULT_OPTS="--height ${FZF_TMUX_HEIGHT:-40%} $FZF_DEFAULT_OPTS --tiebreak=index --bind=ctrl-r:toggle-sort $FZF_CTRL_R_OPTS +m --query $(printf %q "$READLINE_LINE") --read0" $(__fzfcmd) + ) || return + READLINE_LINE=${output#*$'\t'} + if [ -z "$READLINE_POINT" ]; then + echo "$READLINE_LINE" + else + READLINE_POINT=0x7fffffff + fi +} # Required to refresh the prompt after fzf bind -m emacs-standard '"\er": redraw-current-line' -# CTRL-T - Paste the selected file path into the command line -if [ $BASH_VERSINFO -gt 3 ]; then - bind -m emacs-standard -x '"\C-t": "fzf-file-widget"' -elif __fzf_use_tmux__; then - bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select_tmux__`\e\C-e\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"' -else - bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select__`\e\C-e\er\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"' -fi - -# CTRL-R - Paste the selected command from history into the command line -bind -m emacs-standard '"\C-r": "\C-e \C-u\C-y\ey\C-u__fzf_history__ $HISTCMD\e\C-e`"\C-a"`\C-e\e\C-e\er"' - -# ALT-C - cd into the selected directory -bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__fzf_cd__`\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d"' - bind -m vi-command '"\C-z": emacs-editing-mode' bind -m vi-insert '"\C-z": emacs-editing-mode' bind -m emacs-standard '"\C-z": vi-editing-mode' -# CTRL-T - Paste the selected file path into the command line -bind -m vi-command '"\C-t": "\C-z\C-t\C-z"' -bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"' +if [ "${BASH_VERSINFO[0]}" -lt 4 ]; then + # CTRL-T - Paste the selected file path into the command line + if __fzf_use_tmux__; then + bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select_tmux__`\e\C-e\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"' + else + bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__fzf_select__`\e\C-e\er\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"' + fi + bind -m vi-command '"\C-t": "\C-z\C-t\C-z"' + bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"' -# CTRL-R - Paste the selected command from history into the command line -bind -m vi-command '"\C-r": "\C-z\C-r\C-z"' -bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"' + # CTRL-R - Paste the selected command from history into the command line + bind -m emacs-standard '"\C-r": "\C-e \C-u\C-y\ey\C-u"$(__fzf_history__)"\e\C-e\er"' + bind -m vi-command '"\C-r": "\C-z\C-r\C-z"' + bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"' +else + # CTRL-T - Paste the selected file path into the command line + bind -m emacs-standard -x '"\C-t": fzf-file-widget' + bind -m vi-command -x '"\C-t": fzf-file-widget' + bind -m vi-insert -x '"\C-t": fzf-file-widget' + + # CTRL-R - Paste the selected command from history into the command line + bind -m emacs-standard -x '"\C-r": __fzf_history__' + bind -m vi-command -x '"\C-r": __fzf_history__' + bind -m vi-insert -x '"\C-r": __fzf_history__' +fi # ALT-C - cd into the selected directory +bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__fzf_cd__`\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d"' bind -m vi-command '"\ec": "\C-z\ec\C-z"' bind -m vi-insert '"\ec": "\C-z\ec\C-z"' diff --git a/test/test_go.rb b/test/test_go.rb index d2d3153..41aaf8d 100755 --- a/test/test_go.rb +++ b/test/test_go.rb @@ -84,8 +84,6 @@ class Shell end class Tmux - TEMPNAME = '/tmp/fzf-test.txt' - attr_reader :win def initialize(shell = :bash) @@ -129,12 +127,7 @@ class Tmux end def capture(pane = 0) - File.unlink TEMPNAME while File.exist? TEMPNAME - wait do - go("capture-pane -t #{win}.#{pane} \\; save-buffer #{TEMPNAME} 2> /dev/null") - $CHILD_STATUS.exitstatus.zero? - end - File.read(TEMPNAME).split($INPUT_RECORD_SEPARATOR).reverse.drop_while(&:empty?).reverse + go("capture-pane -p -t #{win}.#{pane}") end def until(refresh = false, pane = 0) @@ -1738,7 +1731,8 @@ end module TestShell def setup - super + @tmux = Tmux.new shell + tmux.prepare end def teardown @@ -1855,6 +1849,16 @@ module TestShell tmux.until { |lines| lines[-1] == '3rd' } end + def test_ctrl_r_abort + skip "doesn't restore the original line when search is aborted pre Bash 4" if shell == :bash && /(?<= version )\d+/.match(`#{Shell.bash} --version`).to_s.to_i < 4 + tmux.send_keys 'foo' + tmux.until { |lines| lines[-1].start_with? 'foo' } + tmux.send_keys 'C-r' + tmux.until { |lines| lines[-1].start_with? '>' } + tmux.send_keys 'C-g' + tmux.until { |lines| lines[-1].start_with? 'foo' } + end + def retries(times = 3) (times - 1).times do begin @@ -2044,17 +2048,16 @@ class TestBash < TestBase include TestShell include CompletionTest + def shell + :bash + end + def new_shell tmux.prepare tmux.send_keys "FZF_TMUX=1 #{Shell.bash}", :Enter tmux.prepare end - def setup - super - @tmux = Tmux.new :bash - end - def test_dynamic_completion_loader tmux.paste 'touch /tmp/foo; _fzf_completion_loader=1' tmux.paste '_completion_loader() { complete -o default fake; }' @@ -2077,20 +2080,23 @@ class TestZsh < TestBase include TestShell include CompletionTest + def shell + :zsh + end + def new_shell tmux.send_keys "FZF_TMUX=1 #{Shell.zsh}", :Enter tmux.prepare end - - def setup - super - @tmux = Tmux.new :zsh - end end class TestFish < TestBase include TestShell + def shell + :fish + end + def new_shell tmux.send_keys 'env FZF_TMUX=1 fish', :Enter tmux.send_keys 'function fish_prompt; end; clear', :Enter @@ -2102,11 +2108,6 @@ class TestFish < TestBase tmux.send_keys "set -g #{name} '#{val}'", :Enter tmux.prepare end - - def setup - super - @tmux = Tmux.new :fish - end end __END__