From a0f6bac7318e99fe734b5a0fd0c7dc645123c42c Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Tue, 20 Oct 2026 14:09:55 +0900 Subject: [PATCH] transform-(with-)nth: use whole output, no cycling - Rotation of '|'-separated values applied to transform and bg-transform variants, where a.a is the command template - Output '1|2' turned the binding into '2|1', run as a shell command on the next key press - Output was also cut at '|', breaking --with-nth templates like '{2}|{1}' - Split and rotate only for change-nth and change-with-nth --- CHANGELOG.md | 4 ++++ man/man1/fzf.1 | 4 ++-- src/terminal.go | 12 +++++++++--- test/test_core.rb | 19 +++++++++++++++++++ 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bb13ccd..8d5c65a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ CHANGELOG ------ - Fixed `--with-shell` on Windows not detecting cmd, PowerShell, and pwsh, which made fzf quote commands and entries for a POSIX shell (#4936) (@vimkim) - e.g. With `--with-shell "cmd.exe /d /c"`, `--preview '"C:\Program Files\nu\bin\nu.exe" --version'` failed +- `transform-nth` and `transform-with-nth` (and their `bg-` variants) no longer split the output at `|` + - Only `change-nth` and `change-with-nth` rotate through `|`-separated values + - Fixed the bound command being replaced with the output. e.g. With `transform-nth(echo '1|2')`, the next key press ran `2|1` as a shell command. + - A `--with-nth` template in the output can now contain `|`. e.g. `{2}|{1}` was cut to `{2}`. - nushell - The completion integration now requires nushell 0.116.1 or above, where an interactive completer can take the terminal through the `@interactive` attribute (#4934) (@sim590) diff --git a/man/man1/fzf.1 b/man/man1/fzf.1 index c5a54277..a9996262 100644 --- a/man/man1/fzf.1 +++ b/man/man1/fzf.1 @@ -2166,8 +2166,8 @@ A key or an event can be bound to one or more of the following actions. \fBtransform\-header\-label(...)\fR (transform header label using an external command) \fBtransform\-input\-label(...)\fR (transform input label using an external command) \fBtransform\-list\-label(...)\fR (transform list label using an external command) - \fBtransform\-nth(...)\fR (transform nth using an external command) - \fBtransform\-with\-nth(...)\fR (transform with-nth using an external command) + \fBtransform\-nth(...)\fR (transform nth using an external command; no rotation, output is used as a whole) + \fBtransform\-with\-nth(...)\fR (transform with-nth using an external command; no rotation, output is used as a whole) \fBtransform\-pointer(...)\fR (transform pointer using an external command) \fBtransform\-preview\-label(...)\fR (transform preview label using an external command) \fBtransform\-prompt(...)\fR (transform prompt string using an external command) diff --git a/src/terminal.go b/src/terminal.go index 21a2967b..39e8bb01 100644 --- a/src/terminal.go +++ b/src/terminal.go @@ -7304,8 +7304,11 @@ func (t *Terminal) Loop() error { req(reqList, reqInfo) case actChangeNth, actTransformNth, actBgTransformNth: capture(true, func(expr string) { - // Split nth expression - tokens := strings.Split(expr, "|") + // Only change-nth cycles through '|'-separated values + tokens := []string{expr} + if a.t == actChangeNth { + tokens = strings.Split(expr, "|") + } if nth, err := splitNth(tokens[0]); err == nil || len(expr) == 0 { // Changed newNth = &nth @@ -7328,7 +7331,10 @@ func (t *Terminal) Loop() error { break Action } capture(true, func(expr string) { - tokens := strings.Split(expr, "|") + tokens := []string{expr} + if a.t == actChangeWithNth { + tokens = strings.Split(expr, "|") + } withNthExpr := tokens[0] if len(tokens) > 1 { a.a = strings.Join(append(tokens[1:], tokens[0]), "|") diff --git a/test/test_core.rb b/test/test_core.rb index 9cf399f7..daae0509 100644 --- a/test/test_core.rb +++ b/test/test_core.rb @@ -2374,6 +2374,25 @@ class TestCore < TestInteractive tmux.until { |lines| assert lines.any_include?('a b c') || lines.any_include?('d e f') } end + def test_transform_nth_output_does_not_replace_command + # '|' in the output must not be treated as a list to cycle through + File.write(tempname, '') + tmux.send_keys %(echo 'a b' | #{FZF} --with-nth .. --bind "space:transform-nth(echo >> #{tempname}; echo '1|2')+transform-with-nth(echo >> #{tempname}; echo '1|2')"), :Enter + tmux.until { |lines| assert_equal 1, lines.item_count } + 3.times do |i| + tmux.send_keys :Space + wait { assert_equal 2 * (i + 1), File.readlines(tempname).length } + end + end + + def test_transform_with_nth_output_with_pipe + File.write(tempname, '{2}|{1}') + tmux.send_keys %(echo 'a b' | #{FZF} --with-nth .. --bind 'space:transform-with-nth(cat #{tempname})'), :Enter + tmux.until { |lines| assert lines.any_include?('a b') } + tmux.send_keys :Space + tmux.until { |lines| assert lines.any_include?('b|a') } + end + # Regression: actions emitted by bg-transform must affect the iteration that # processes the async result, not the (no-longer-active) iteration that # scheduled the transform. Covers reload (newCommand) and exclude (denylist).