diff --git a/src/options.go b/src/options.go index cfbc857e..85a21144 100644 --- a/src/options.go +++ b/src/options.go @@ -10,7 +10,6 @@ import ( "strconv" "strings" "time" - "unicode" "github.com/junegunn/fzf/src/algo" "github.com/junegunn/fzf/src/tui" @@ -1734,10 +1733,10 @@ Loop: return masked } -func parseSingleActionList(str string) ([]*action, error) { +func parseSingleActionList(str string, putAllowed bool) ([]*action, error) { // We prepend a colon to satisfy argActionRegexp and remove it later masked := maskActionContents(":" + str)[1:] - return parseActionList(masked, str, []*action{}, false) + return parseActionList(masked, str, []*action{}, putAllowed) } func parseActionList(masked string, original string, prevActions []*action, putAllowed bool) ([]*action, error) { @@ -2043,8 +2042,7 @@ func parseKeymap(keymap map[tui.Event][]*action, str string) error { } key = firstKey(keys) } - putAllowed := key.Type == tui.Rune && unicode.IsGraphic(key.Char) - keymap[key], err = parseActionList(pair[1], origPairStr[len(pair[0])+1:], keymap[key], putAllowed) + keymap[key], err = parseActionList(pair[1], origPairStr[len(pair[0])+1:], keymap[key], key.Printable()) if err != nil { return err } diff --git a/src/options_test.go b/src/options_test.go index 7e23215d..a699954c 100644 --- a/src/options_test.go +++ b/src/options_test.go @@ -572,7 +572,7 @@ func TestValidateSign(t *testing.T) { } func TestParseSingleActionList(t *testing.T) { - actions, _ := parseSingleActionList("Execute@foo+bar,baz@+up+up+reload:down+down") + actions, _ := parseSingleActionList("Execute@foo+bar,baz@+up+up+reload:down+down", false) if len(actions) != 4 { t.Errorf("Invalid number of actions parsed:%d", len(actions)) } @@ -588,7 +588,7 @@ func TestParseSingleActionList(t *testing.T) { } func TestParseSingleActionListError(t *testing.T) { - _, err := parseSingleActionList("change-query(foobar)baz") + _, err := parseSingleActionList("change-query(foobar)baz", false) if err == nil { t.Errorf("Failed to detect error") } diff --git a/src/server.go b/src/server.go index 33e33755..a2080d37 100644 --- a/src/server.go +++ b/src/server.go @@ -240,7 +240,7 @@ Loop: } body = body[:contentLength] - actions, err := parseSingleActionList(strings.Trim(string(body), "\r\n")) + actions, err := parseSingleActionList(strings.Trim(string(body), "\r\n"), false) if err != nil { return bad(err.Error()) } diff --git a/src/terminal.go b/src/terminal.go index 3599f822..1b3a60b2 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -6973,7 +6973,8 @@ func (t *Terminal) Loop() error { }) case actTransform, actBgTransform: capture(false, func(body string) { - if actions, err := parseSingleActionList(strings.Trim(body, "\r\n")); err == nil { + // Allow 'put' if the triggering key is a printable character + if actions, err := parseSingleActionList(strings.Trim(body, "\r\n"), event.Printable()); err == nil { // NOTE: We're not properly passing the return value here doActions(actions) } diff --git a/src/tui/tui.go b/src/tui/tui.go index 97ba2065..09ee9454 100644 --- a/src/tui/tui.go +++ b/src/tui/tui.go @@ -4,6 +4,7 @@ import ( "strconv" "strings" "time" + "unicode" "github.com/junegunn/fzf/src/util" "github.com/rivo/uniseg" @@ -252,6 +253,12 @@ func (e Event) Comparable() Event { return Event{e.Type, e.Char, nil} } +// Printable returns true if the event is a printable character that can be +// inserted into the query (e.g. via the 'put' action). +func (e Event) Printable() bool { + return e.Type == Rune && unicode.IsGraphic(e.Char) +} + func (e Event) KeyName() string { if me := e.MouseEvent; me != nil { return me.Name() diff --git a/test/test_core.rb b/test/test_core.rb index 119b94cd..91eac672 100644 --- a/test/test_core.rb +++ b/test/test_core.rb @@ -971,6 +971,24 @@ class TestCore < TestInteractive tmux.until { |lines| assert_includes lines[1], ' aabravo/aabravo' } end + def test_transform_put + tmux.send_keys %(seq 1000 | #{FZF} --bind 'a:transform:echo put'), :Enter + tmux.until { |lines| assert_equal 1000, lines.match_count } + tmux.send_keys :a + tmux.until { |lines| assert_equal '> a', lines.last } + tmux.send_keys :b + tmux.until { |lines| assert_equal '> ab', lines.last } + end + + # The async callback runs in a later iteration, but 'put' must still insert + # the key that triggered the bg-transform (snapshot of the scheduling event). + def test_bg_transform_put + tmux.send_keys %(seq 1000 | #{FZF} --bind 'a:bg-transform:sleep 0.5; echo put'), :Enter + tmux.until { |lines| assert_equal 1000, lines.match_count } + tmux.send_keys 'ab' + tmux.until { |lines| assert_equal '> ba', lines.last } + end + def test_accept_non_empty tmux.send_keys %(seq 1000 | #{fzf('--print-query --bind enter:accept-non-empty')}), :Enter tmux.until { |lines| assert_equal 1000, lines.match_count }