Skip to content

Commit 385cccd

Browse files
authored
fish: Command history improvements (#4672)
This change provides the following improvements: - Changes the view of the command history list, so that the script no longer depends on perl for performance. - Enables syntax color highlighting on fish v4.3.3 and newer. - Provides a preview window with the selected commands, and expanded view of the highlighted command if available. - Improves the delete functionality, by successfully handling very large numbers of selected commands. - Inserts commands in their formatted form with `<Alt-Enter>`. --- * fish: Change history list view The view of the command history is changed, so that no manipulation is performed on the output of history command: The history item count number is replaced by the Unix time of the command, multi-line display is disabled and line wrapping is enabled by default. On fish v4.3.3 and newer, the use of ANSI color codes for syntax highlighting is now possible, while the script no longer depends on perl for performance. Fixes #4661 * fish: Reformat selected history commands with ALT-ENTER * Add $FZF_WRAP environment variable The variable is set when line wrapping is enabled. It has the value `word` if word wrapping mode is set, otherwise it has the value `char`. * fish: Add command history preview The preview shows the highlighted command and any selected commands, after first being formatted and colorized by fish_indent. The timestamp of the highlighted command in the preview is converted to strftime format "%F %a %T" if the date command is available. The preview is hidden on start, and is displayed if more than 100 columns are available and one of the following conditions are met: - The highlighted item doesn't completely fit in list view (line wrapping is enabled for the preview and is now disabled for the list). - The highlighted item contains newlines (multi-line commands or strings). - The highlighted item contains chained commands in a single line, that can be broken down by the formatter for cleaner view. - One or more commands are marked as selected. * fish: Handle deletion of large number of selected history entries * fish: Change wrapping options for the preview-window
1 parent 4a684b6 commit 385cccd

File tree

6 files changed

+69
-20
lines changed

6 files changed

+69
-20
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ CHANGELOG
3535
- Added `--preview-wrap-sign` to set a different wrap indicator for the preview
3636
window
3737
- Added `alt-gutter` color option (#4602) (@hedgieinsocks)
38+
- Added `$FZF_WRAP` environment variable to child processes (`char` or `word`
39+
when wrapping is enabled) (#4672) (@bitraid)
40+
- fish: Improved command history (CTRL-R) (#4672) (@bitraid)
41+
- Enabled syntax highlighting in the list on fish 4.3.3+
42+
- Added syntax-highlighted preview window that auto-shows for long or
43+
multi-line commands
44+
- Added `ALT-ENTER` to reformat and insert selected commands
45+
- Improved handling of bulk deletion of selected history entries
46+
(`SHIFT-DELETE`)
3847
- Added fish completion support (#4605) (@lalvarezt)
3948
- zsh: Handle multi-line history selection (#4595) (@LangLangBart)
4049
- Bug fixes

man/man1/fzf.1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,6 +1390,8 @@ fzf exports the following environment variables to its child processes.
13901390
.br
13911391
.BR FZF_POS " Vertical position of the cursor in the list starting from 1"
13921392
.br
1393+
.BR FZF_WRAP " The line wrapping mode (char, word) when enabled"
1394+
.br
13931395
.BR FZF_QUERY " Current query string"
13941396
.br
13951397
.BR FZF_INPUT_STATE " Current input state (enabled, disabled, hidden)"

shell/key-bindings.fish

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
function fzf_key_bindings
2222

2323
# Check fish version
24-
set -l fish_ver (string match -r '^(\d+).(\d+)' $version 2> /dev/null; or echo 0\n0\n0)
25-
if test \( "$fish_ver[2]" -lt 3 \) -o \( "$fish_ver[2]" -eq 3 -a "$fish_ver[3]" -lt 1 \)
24+
if set -l -- fish_ver (string match -r '^(\d+)\.(\d+)' $version 2>/dev/null)
25+
and test "$fish_ver[2]" -lt 3 -o "$fish_ver[2]" -eq 3 -a "$fish_ver[3]" -lt 1
2626
echo "This script requires fish version 3.1b1 or newer." >&2
2727
return 1
2828
else if not type -q fzf
@@ -182,36 +182,58 @@ function fzf_key_bindings
182182
set -l -- total_lines (count $command_line)
183183
set -l -- fzf_query (string escape -- $command_line[$current_line])
184184

185-
set -lx FZF_DEFAULT_OPTS (__fzf_defaults '' \
186-
'--nth=2..,.. --scheme=history --multi --wrap-sign="\t↳ "' \
187-
'--bind=\'shift-delete:execute-silent(eval history delete --exact --case-sensitive -- (string escape -n -- {+} | string replace -r -a "^\d*\\\\\\t|(?<=\\\\\\n)\\\\\\t" ""))+reload(eval $FZF_DEFAULT_COMMAND)\'' \
185+
set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults '' \
186+
'--nth=2..,.. --scheme=history --multi --no-multi-line --no-wrap --wrap-sign="\t\t\t↳ " --preview-wrap-sign="↳ "' \
187+
'--bind=\'shift-delete:execute-silent(for i in (string split0 -- <{+f}); eval builtin history delete --exact --case-sensitive -- (string escape -n -- $i | string replace -r "^\d*\\\\\\t" ""); end)+reload(eval $FZF_DEFAULT_COMMAND)\'' \
188+
'--bind="alt-enter:become(string join0 -- (string collect -- {+2..} | fish_indent -i))"' \
188189
"--bind=ctrl-r:toggle-sort,alt-r:toggle-raw --highlight-line $FZF_CTRL_R_OPTS" \
189-
'--accept-nth=2.. --read0 --print0 --with-shell='(status fish-path)\\ -c)
190+
'--accept-nth=2.. --delimiter="\t" --tabstop=4 --read0 --print0 --with-shell='(status fish-path)\\ -c)
191+
192+
# Add dynamic preview options if preview command isn't already set by user
193+
if string match -qvr -- '--preview[= ]' "$FZF_DEFAULT_OPTS"
194+
# Convert the highlighted timestamp using the date command if available
195+
set -l -- date_cmd '{1}'
196+
if type -q date
197+
if date -d @0 '+%s' 2>/dev/null | string match -q 0
198+
# GNU date
199+
set -- date_cmd '(date -d @{1} \\"+%F %a %T\\")'
200+
else if date -r 0 '+%s' 2>/dev/null | string match -q 0
201+
# BSD date
202+
set -- date_cmd '(date -r {1} \\"+%F %a %T\\")'
203+
end
204+
end
205+
206+
# Prepend the options to allow user customizations
207+
set -p -- FZF_DEFAULT_OPTS \
208+
'--bind="focus,resize:bg-transform:if test \\"$FZF_COLUMNS\\" -gt 100 -a \\\\( \\"$FZF_SELECT_COUNT\\" -gt 0 -o \\\\( -z \\"$FZF_WRAP\\" -a (string length -- {}) -gt (math $FZF_COLUMNS - 4) \\\\) -o (string collect -- {2..} | fish_indent | count) -gt 1 \\\\); echo show-preview; else echo hide-preview; end"' \
209+
'--preview="string collect -- (test \\"$FZF_SELECT_COUNT\\" -gt 0; and string collect -- {+2..}) \\"\\n# \\"'$date_cmd' {2..} | fish_indent --ansi"' \
210+
'--preview-window="right,50%,wrap-word,follow,info,hidden"'
211+
end
190212

191213
set -lx FZF_DEFAULT_OPTS_FILE
192-
set -lx FZF_DEFAULT_COMMAND
193214

194-
if type -q perl
195-
set -a FZF_DEFAULT_OPTS '--tac'
196-
set FZF_DEFAULT_COMMAND 'builtin history -z --reverse | command perl -0 -pe \'s/^/$.\t/g; s/\n/\n\t/gm\''
197-
else
198-
set FZF_DEFAULT_COMMAND \
199-
'set -l h (builtin history -z --reverse | string split0);' \
200-
'for i in (seq (count $h) -1 1);' \
201-
'string join0 -- $i\t(string replace -a -- \n \n\t $h[$i] | string collect);' \
202-
'end'
215+
set -lx -- FZF_DEFAULT_COMMAND 'builtin history -z --show-time="%s%t"'
216+
217+
# Enable syntax highlighting colors on fish v4.3.3 and newer
218+
if set -l -- v (string match -r -- '^(\d+)\.(\d+)(?:\.(\d+))?' $version)
219+
and test "$v[2]" -gt 4 -o "$v[2]" -eq 4 -a \
220+
\( "$v[3]" -gt 3 -o "$v[3]" -eq 3 -a \
221+
\( -n "$v[4]" -a "$v[4]" -ge 3 \) \)
222+
223+
set -a -- FZF_DEFAULT_OPTS '--ansi'
224+
set -a -- FZF_DEFAULT_COMMAND '--color=always'
203225
end
204226

205227
# Merge history from other sessions before searching
206228
test -z "$fish_private_mode"; and builtin history merge
207229

208230
if set -l result (eval $FZF_DEFAULT_COMMAND \| (__fzfcmd) --query=$fzf_query | string split0)
209231
if test "$total_lines" -eq 1
210-
commandline -- (string replace -a -- \n\t \n $result)
232+
commandline -- $result
211233
else
212234
set -l a (math $current_line - 1)
213235
set -l b (math $current_line + 1)
214-
commandline -- $command_line[1..$a] (string replace -a -- \n\t \n $result)
236+
commandline -- $command_line[1..$a] $result
215237
commandline -a -- '' $command_line[$b..-1]
216238
end
217239
end

src/terminal.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,6 +1356,13 @@ func (t *Terminal) environImpl(forPreview bool) []string {
13561356
} else if t.paused {
13571357
inputState = "disabled"
13581358
}
1359+
if t.wrap {
1360+
if t.wrapWord {
1361+
env = append(env, "FZF_WRAP=word")
1362+
} else {
1363+
env = append(env, "FZF_WRAP=char")
1364+
}
1365+
}
13591366
env = append(env, "FZF_INPUT_STATE="+inputState)
13601367
env = append(env, fmt.Sprintf("FZF_TOTAL_COUNT=%d", t.count))
13611368
env = append(env, fmt.Sprintf("FZF_MATCH_COUNT=%d", t.resultMerger.Length()))

test/lib/common.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ def prepare
163163
self.until(true) do |lines|
164164
message = "Prepare[#{tries}]"
165165
send_keys ' ', 'C-u', :Enter, message, :Left, :Right
166+
sleep(0.15)
166167
lines[-1] == message
167168
end
168169
rescue Minitest::Assertion

test/test_shell_integration.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def test_ctrl_r_multiline
138138
tmux.send_keys 'C-r'
139139
tmux.until { |lines| assert_equal '>', lines[-1] }
140140
tmux.send_keys 'foo bar'
141-
tmux.until { |lines| assert_includes lines[-4], '"foo' } unless shell == :zsh
141+
tmux.until { |lines| assert_includes lines[-4], '"foo' } if shell == :bash
142142
tmux.until { |lines| assert lines[-3]&.match?(/bar"␊?/) }
143143
tmux.send_keys :Enter
144144
tmux.until { |lines| assert lines[-1]&.match?(/bar"␊?/) }
@@ -1020,15 +1020,23 @@ def test_ctrl_r_multi
10201020
tmux.send_keys 'echo "bar', :Enter, 'foo"', :Enter
10211021
tmux.prepare
10221022
tmux.send_keys 'C-l', 'C-r'
1023+
offset = -6
10231024
block = <<~BLOCK
10241025
echo "foo
10251026
bar"
10261027
echo "bar
10271028
foo"
10281029
BLOCK
1030+
if shell == :fish
1031+
offset = -4
1032+
block = <<~FISH
1033+
echo "foo␊bar"
1034+
echo "bar␊foo"
1035+
FISH
1036+
end
10291037
tmux.until do |lines|
10301038
block.lines.each_with_index do |line, idx|
1031-
assert_includes lines[-6 + idx], line.chomp
1039+
assert_includes lines[idx + offset], line.chomp
10321040
end
10331041
end
10341042
tmux.send_keys :BTab, :BTab

0 commit comments

Comments
 (0)