Repository: junegunn/fzf
File: test/test_shell_integration.rb (Ruby)

Code before:
    prepare_ctrl_r_test
    tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
    tmux.send_keys :BTab, :BTab, :BTab
    tmux.until { |lines| assert_includes lines[-2], '(3)' }
    tmux.send_keys :Enter
    tmux.until do |lines|
      assert_equal ['cat <<EOF | wc -c', 'qux thud', 'EOF', 'echo "trailing_space "', 'echo "bar', 'foo"'], lines[-6..]
    end
  end

  test_perl_and_awk 'ctrl_r_no_multi_selection' do
    set_var('FZF_CTRL_R_OPTS', '--no-multi')
    prepare_ctrl_r_test
    tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
    tmux.send_keys :BTab, :BTab, :BTab
    tmux.until { |lines| refute_includes lines[-2], '(3)' }
    tmux.send_keys :Enter
    tmux.until do |lines|
      assert_equal ['cat <<EOF | wc -c', 'qux thud', 'EOF'], lines[-3..]
    end
  end
end

class TestFish < TestBase
  include TestShell

  def shell
    :fish
  end

  def new_shell
    tmux.send_keys 'env FZF_TMUX=1 FZF_DEFAULT_OPTS=--no-scrollbar fish', :Enter
    tmux.send_keys 'function fish_prompt; end; clear', :Enter
    tmux.until { |lines| assert_empty lines }
  end

  def set_var(name, val)
    tmux.prepare
    tmux.send_keys "set -g #{name} '#{val}'", :Enter
    tmux.prepare
  end

  def test_ctrl_r_multi
    tmux.send_keys ':', :Enter
    tmux.send_keys 'echo "foo', :Enter, 'bar"', :Enter
    tmux.prepare
    tmux.send_keys 'echo "bar', :Enter, 'foo"', :Enter
    tmux.prepare
    tmux.send_keys 'C-l', 'C-r'
    block = <<~BLOCK
      echo "foo

Review comment (security):
Just as a reminder

We can't use `Perl/fc` because, unlike bash, zsh escapes actual newlines as `\n`.

### bash: fc preserves actual newlines

```bash
printf 'foo\n
bar\n'
# foo

# bar

fc -rl -1
# 1        printf 'foo\n
# bar\n'
```

### zsh: fc escapes newlines to \n

```bash
printf 'foo\n
bar\n'
# foo

# bar

fc -rl -1
    # 1  printf 'foo\n\nbar\n'

printf '%s\t%s\n' "${(kv)history[@]}"
# 1       printf 'foo\n
# bar\n'
```

---

...and trying to patch the issue by running `fc -RI` before calling the
`$history` array caused problems the last time we tried.


Code after:
    prepare_ctrl_r_test
    tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
    tmux.send_keys :BTab, :BTab, :BTab
    tmux.until { |lines| assert_includes lines[-2], '(3)' }
    tmux.send_keys :Enter
    tmux.until do |lines|
      assert_equal ['cat <<EOF | wc -c', 'qux thud', 'EOF', 'echo "trailing_space "', 'echo "bar', 'foo"'], lines[-6..]
    end
  end

  test_perl_and_awk 'ctrl_r_no_multi_selection' do
    set_var('FZF_CTRL_R_OPTS', '--no-multi')
    prepare_ctrl_r_test
    tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
    tmux.send_keys :BTab, :BTab, :BTab
    tmux.until { |lines| refute_includes lines[-2], '(3)' }
    tmux.send_keys :Enter
    tmux.until do |lines|
      assert_equal ['cat <<EOF | wc -c', 'qux thud', 'EOF'], lines[-3..]
    end
  end

  # NOTE: 'Perl/$history' won't see foreign cmds immediately, unlike 'awk/fc'.
  # Perl passes only because another cmd runs between mocking and triggering C-r
  # https://github.com/junegunn/fzf/issues/4061
  # https://zsh.org/mla/users/2024/msg00692.html
  test_perl_and_awk 'ctrl_r_foreign_commands' do
    histfile = "#{tempname}-foreign-hist"
    tmux.send_keys "HISTFILE=#{histfile}", :Enter
    tmux.prepare
    # SHARE_HISTORY picks up foreign commands; marked with * in fc
    tmux.send_keys 'setopt SHARE_HISTORY', :Enter
    tmux.prepare
    tmux.send_keys 'fzf_cmd_local', :Enter
    tmux.prepare
    # Mock foreign command (for testing only; don't edit your HISTFILE this way)
    tmux.send_keys "echo ': 0:0;fzf_cmd_foreign' >> $HISTFILE", :Enter
    tmux.prepare
    # Verify fc shows foreign command with asterisk
    tmux.send_keys 'fc -rl -1', :Enter
    tmux.until { |lines| assert lines.any? { |l| l.match?(/^\s*\d+\* fzf_cmd_foreign/) } }
    tmux.prepare
    # Test ctrl-r correctly extracts the foreign command
    tmux.send_keys 'C-r'
    tmux.until { |lines| assert_operator lines.match_count, :>, 0 }
    tmux.send_keys '^fzf_cmd_'
    tmux.until { |lines| assert_equal 2, lines.match_count }
    tmux.send_keys :BTab, :BTab
    tmux.until { |lines| assert_includes lines[-2], '(2)' }
    tmux.send_keys :Enter
    tmux.until do |lines|