diff --git a/CHANGELOG.md b/CHANGELOG.md index daee60a4..258b6c9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,19 @@ CHANGELOG fzf --header '[src] [test]' --no-input --layout reverse \ --header-border bottom --input-border \ --bind 'click-header:transform-search:echo ${FZF_CLICK_HEADER_WORD:1:-1}' + + # Vim-like mode switch + fzf --layout reverse-list --no-input \ + --bind 'j:down,k:up,/:show-input+unbind(j,k)' \ + --bind 'enter,esc,ctrl-c:transform: + if [[ $FZF_INPUT_STATE = enabled ]]; then + echo "rebind(j,k)+hide-input" + elif [[ $FZF_KEY = enter ]]; then + echo accept + else + echo abort + fi + ' ``` - You can later show the input section using `show-input` or `toggle-input` action, and hide it again using `hide-input`, or `toggle-input`. - Extended `{q}` placeholder to support ranges. e.g. `{q:1}`, `{q:2..}`, etc. diff --git a/src/options.go b/src/options.go index 2b91fbf7..99d58634 100644 --- a/src/options.go +++ b/src/options.go @@ -885,7 +885,7 @@ func parseKeyChordsImpl(str string, message string) (map[tui.Event]string, error case "right": add(tui.Right) case "enter", "return": - add(tui.CtrlM) + add(tui.Enter) case "space": chords[tui.Key(' ')] = key case "backspace", "bspace", "bs": @@ -3220,7 +3220,7 @@ func postProcessOptions(opts *Options) error { // If 'double-click' is left unbound, bind it to the action bound to 'enter' if _, prs := opts.Keymap[tui.DoubleClick.AsEvent()]; !prs { - opts.Keymap[tui.DoubleClick.AsEvent()] = opts.Keymap[tui.CtrlM.AsEvent()] + opts.Keymap[tui.DoubleClick.AsEvent()] = opts.Keymap[tui.Enter.AsEvent()] } // If we're not using extended search mode, --nth option becomes irrelevant diff --git a/src/options_test.go b/src/options_test.go index 40d2920b..5c9a789a 100644 --- a/src/options_test.go +++ b/src/options_test.go @@ -172,7 +172,7 @@ func TestParseKeys(t *testing.T) { if len(pairs) != 9 { t.Error(9) } - check(tui.CtrlM, "Return") + check(tui.Enter, "Return") checkEvent(tui.Key(' '), "space") check(tui.Tab, "tab") check(tui.ShiftTab, "btab") @@ -195,7 +195,7 @@ func TestParseKeys(t *testing.T) { check(tui.ShiftLeft, "shift-left") check(tui.ShiftRight, "shift-right") check(tui.ShiftTab, "shift-tab") - check(tui.CtrlM, "Enter") + check(tui.Enter, "Enter") check(tui.Backspace, "bspace") } diff --git a/src/terminal.go b/src/terminal.go index 175d82c4..820cc606 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -671,7 +671,7 @@ func defaultKeymap() map[tui.Event][]*action { add(tui.CtrlJ, actDown) add(tui.CtrlK, actUp) add(tui.CtrlL, actClearScreen) - add(tui.CtrlM, actAccept) + add(tui.Enter, actAccept) add(tui.CtrlN, actDown) add(tui.CtrlP, actUp) add(tui.CtrlU, actUnixLineDiscard) diff --git a/src/tui/eventtype_string.go b/src/tui/eventtype_string.go index a62ba073..df57ccc1 100644 --- a/src/tui/eventtype_string.go +++ b/src/tui/eventtype_string.go @@ -21,7 +21,7 @@ func _() { _ = x[CtrlJ-10] _ = x[CtrlK-11] _ = x[CtrlL-12] - _ = x[CtrlM-13] + _ = x[Enter-13] _ = x[CtrlN-14] _ = x[CtrlO-15] _ = x[CtrlP-16] @@ -110,7 +110,7 @@ func _() { _ = x[ClickHeader-99] } -const _EventType_name = "RuneCtrlACtrlBCtrlCCtrlDCtrlECtrlFCtrlGCtrlHTabCtrlJCtrlKCtrlLCtrlMCtrlNCtrlOCtrlPCtrlQCtrlRCtrlSCtrlTCtrlUCtrlVCtrlWCtrlXCtrlYCtrlZEscCtrlSpaceCtrlDeleteCtrlBackSlashCtrlRightBracketCtrlCaretCtrlSlashShiftTabBackspaceDeletePageUpPageDownUpDownLeftRightHomeEndInsertShiftUpShiftDownShiftLeftShiftRightShiftDeleteF1F2F3F4F5F6F7F8F9F10F11F12AltBackspaceAltUpAltDownAltLeftAltRightAltShiftUpAltShiftDownAltShiftLeftAltShiftRightAltCtrlAltInvalidFatalMouseDoubleClickLeftClickRightClickSLeftClickSRightClickScrollUpScrollDownSScrollUpSScrollDownPreviewScrollUpPreviewScrollDownResizeChangeBackwardEOFStartLoadFocusOneZeroResultJumpJumpCancelClickHeader" +const _EventType_name = "RuneCtrlACtrlBCtrlCCtrlDCtrlECtrlFCtrlGCtrlHTabCtrlJCtrlKCtrlLEnterCtrlNCtrlOCtrlPCtrlQCtrlRCtrlSCtrlTCtrlUCtrlVCtrlWCtrlXCtrlYCtrlZEscCtrlSpaceCtrlDeleteCtrlBackSlashCtrlRightBracketCtrlCaretCtrlSlashShiftTabBackspaceDeletePageUpPageDownUpDownLeftRightHomeEndInsertShiftUpShiftDownShiftLeftShiftRightShiftDeleteF1F2F3F4F5F6F7F8F9F10F11F12AltBackspaceAltUpAltDownAltLeftAltRightAltShiftUpAltShiftDownAltShiftLeftAltShiftRightAltCtrlAltInvalidFatalMouseDoubleClickLeftClickRightClickSLeftClickSRightClickScrollUpScrollDownSScrollUpSScrollDownPreviewScrollUpPreviewScrollDownResizeChangeBackwardEOFStartLoadFocusOneZeroResultJumpJumpCancelClickHeader" var _EventType_index = [...]uint16{0, 4, 9, 14, 19, 24, 29, 34, 39, 44, 47, 52, 57, 62, 67, 72, 77, 82, 87, 92, 97, 102, 107, 112, 117, 122, 127, 132, 135, 144, 154, 167, 183, 192, 201, 209, 218, 224, 230, 238, 240, 244, 248, 253, 257, 260, 266, 273, 282, 291, 301, 312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 333, 336, 339, 351, 356, 363, 370, 378, 388, 400, 412, 425, 428, 435, 442, 447, 452, 463, 472, 482, 492, 503, 511, 521, 530, 541, 556, 573, 579, 585, 596, 601, 605, 610, 613, 617, 623, 627, 637, 648} diff --git a/src/tui/tcell_test.go b/src/tui/tcell_test.go index 217ad048..63bcd8ce 100644 --- a/src/tui/tcell_test.go +++ b/src/tui/tcell_test.go @@ -82,9 +82,9 @@ func TestGetCharEventKey(t *testing.T) { {giveKey{tcell.KeyTab, rune(tcell.KeyTab), tcell.ModNone}, wantKey{Tab, 0, nil}}, // unhandled, actual "Tab" keystroke {giveKey{tcell.KeyTAB, rune(tcell.KeyTAB), tcell.ModNone}, wantKey{Tab, 0, nil}}, // fabricated, unhandled // KeyEnter is alias for KeyCR - {giveKey{tcell.KeyCtrlM, rune(tcell.KeyCtrlM), tcell.ModNone}, wantKey{CtrlM, 0, nil}}, // actual "Enter" keystroke - {giveKey{tcell.KeyCR, rune(tcell.KeyCR), tcell.ModNone}, wantKey{CtrlM, 0, nil}}, // fabricated, unhandled - {giveKey{tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone}, wantKey{CtrlM, 0, nil}}, // fabricated, unhandled + {giveKey{tcell.KeyCtrlM, rune(tcell.KeyCtrlM), tcell.ModNone}, wantKey{Enter, 0, nil}}, // actual "Enter" keystroke + {giveKey{tcell.KeyCR, rune(tcell.KeyCR), tcell.ModNone}, wantKey{Enter, 0, nil}}, // fabricated, unhandled + {giveKey{tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone}, wantKey{Enter, 0, nil}}, // fabricated, unhandled // Ctrl+Alt keys {giveKey{tcell.KeyCtrlA, rune(tcell.KeyCtrlA), tcell.ModCtrl | tcell.ModAlt}, wantKey{CtrlAlt, 'a', nil}}, // fabricated {giveKey{tcell.KeyCtrlA, rune(tcell.KeyCtrlA), tcell.ModCtrl | tcell.ModAlt | tcell.ModShift}, wantKey{CtrlAlt, 'a', nil}}, // fabricated @@ -233,7 +233,7 @@ Quick reference 10 1 KeyCtrlJ KeyLF = ^J CtrlJ 11 1 KeyCtrlK KeyVT = ^K CtrlK 12 1 KeyCtrlL KeyFF = ^L CtrlL -13 1 KeyCtrlM KeyCR = ^M KeyEnter CtrlM +13 1 KeyCtrlM KeyCR = ^M KeyEnter Enter 14 1 KeyCtrlN KeySO = ^N CtrlN 15 1 KeyCtrlO KeySI = ^O CtrlO 16 1 KeyCtrlP KeyDLE = ^P CtrlP diff --git a/src/tui/tui.go b/src/tui/tui.go index 8423c631..265d3cff 100644 --- a/src/tui/tui.go +++ b/src/tui/tui.go @@ -28,7 +28,7 @@ const ( CtrlJ CtrlK CtrlL - CtrlM + Enter CtrlN CtrlO CtrlP @@ -160,6 +160,9 @@ func (e Event) KeyName() string { switch e.Type { case Rune: + if e.Char == ' ' { + return "space" + } return string(e.Char) case Alt: return "alt-" + string(e.Char) diff --git a/test/test_core.rb b/test/test_core.rb index 4b8a2cbc..bcd6e9d1 100644 --- a/test/test_core.rb +++ b/test/test_core.rb @@ -1607,4 +1607,49 @@ class TestCore < TestInteractive assert_equal 4, lines.match_count end end + + def test_env_vars + def to_vars(lines) + lines.select { it.start_with?('FZF_') }.to_h do + key, val = it.split('=', 2) + [key.to_sym, val] + end + end + + tmux.send_keys %(seq 100 | #{FZF} --multi --reverse --preview-window up,noborder --preview 'env | grep ^FZF_ | sort' --no-input --bind enter:show-input+refresh-preview,space:disable-search+refresh-preview), :Enter + expected = { + FZF_TOTAL_COUNT: '100', + FZF_MATCH_COUNT: '100', + FZF_SELECT_COUNT: '0', + FZF_ACTION: 'start', + FZF_KEY: '', + FZF_POS: '1', + FZF_QUERY: '', + FZF_PROMPT: '>', + FZF_INPUT_STATE: 'hidden' + } + tmux.until do |lines| + assert_equal expected, to_vars(lines).slice(*expected.keys) + end + tmux.send_keys :Enter + tmux.until do |lines| + expected.merge!(FZF_INPUT_STATE: 'enabled', FZF_ACTION: 'show-input', FZF_KEY: 'enter') + assert_equal expected, to_vars(lines).slice(*expected.keys) + end + tmux.send_keys :Tab, :Tab + tmux.until do |lines| + expected.merge!(FZF_ACTION: 'toggle-down', FZF_KEY: 'tab', FZF_POS: '3', FZF_SELECT_COUNT: '2') + assert_equal expected, to_vars(lines).slice(*expected.keys) + end + tmux.send_keys '99' + tmux.until do |lines| + expected.merge!(FZF_ACTION: 'char', FZF_KEY: '9', FZF_QUERY: '99', FZF_MATCH_COUNT: '1', FZF_POS: '1') + assert_equal expected, to_vars(lines).slice(*expected.keys) + end + tmux.send_keys :Space + tmux.until do |lines| + expected.merge!(FZF_INPUT_STATE: 'disabled', FZF_ACTION: 'disable-search', FZF_KEY: 'space') + assert_equal expected, to_vars(lines).slice(*expected.keys) + end + end end