fzf: 0.54.0

0.54.0 #

(July 2024)

Enabling line wrap with --wrap #

fzf can now be configured to wrap long lines instead of truncating them.

  • --wrap enables line wrapping
  • --wrap-sign=INDICATOR specifies the indicator for wrapped lines. The default is '↳ '.
  • You can use toogle-wrap action to dynamically toggle wrapping
    • fzf by default binds CTRL-/ and ALT-/ to toggle-wrap
declare -f | perl -0777 -pe 's/^}\n/}\0/gm' |
  bat --plain --language bash --color always |
  fzf --read0 --ansi --reverse --multi --highlight-line --wrap

I find this particularly useful when browsing log streams with fzf:

tail -f *.log | fzf --tail 100000 --tac --no-sort --exact --wrap

Or when browsing the command history via CTRL-R and there are long, truncated commands. Now you can press CTRL-/ or ALT-/ to see the full command.

Also, kill completion now enables line wrapping by default because it’s important to see the full command when you’re deciding which process to kill.

Empty pointer and marker signs #

fzf now accepts empty strings for --pointer and --marker options. You can use it to achieve a minimalistic look like this:

fzf --pointer '' --marker '' --prompt '' --info hidden --border --padding 1,2

Improved --sync behavior #

Prior to 0.54.0, --sync option would block the interface only until the input stream is complete, so for a large input with an initial query, you would notice fzf rendering the unfiltered list first, then replacing it with the filtered list after a while. This is no longer the case. You’ll only see the final result.

seq 10000000 | fzf --sync --query 1

--sync also waits until all associated actions are complete. This allows you to set up the initial state of the interface using start, load, or result events without exposing the intermediate states to the user. See the next section for an example.

GET endpoint is available in transform and execute #

With --listen option, fzf starts an HTTP server and you can get the current state in JSON format via GET / endpoint. However, accessing this endpoint from transform or execute action was not possible due to a lock conflict. This has been fixed in 0.54.0.

cat /usr/share/dict/words |
  want="banana" fzf --listen --sync --bind 'start:transform:
    pos=$(
      curl -s localhost:$FZF_PORT?limit=$FZF_MATCH_COUNT |
      jq --arg want "$want" \
         ".matches | map(.text) | to_entries | map(select(.value == \$want)) | first | .key // -1"
    )
    [[ $pos -ge 0 ]] && echo "pos($((pos + 1)))+offset-middle"
'
  • Find the offset of the word “banana” in the list and move the cursor to it.
  • We use the new offset-middle action to place the current item in the middle of the screen.
  • Thanks to the improved --sync behavior,
    • The initial filtering is already done when start event is triggered.
    • And you won’t see the intermediate state before actions bound to start event are complete.

Better handling of reload action on start event #

If reload (or reload-sync) action is bound to start event, fzf will not start the initial reader (built-in directory walker or $FZF_DEFAULT_COMMAND).

Previously, we would start fzf with an empty list to prevent it from doing the unnecessary work (i.e. : | fzf ... or fzf < dev/null ...). This is no longer necessary.

Not starting the reader also prevent extraneous load or result events from being triggered.

This is a simple example, but it wouldn’t work as expected before.

fzf --header 'Loading ...' --header-lines 1 --layout reverse \
    --bind 'start:reload:sleep 1; ps -ef' \
    --bind 'load:change-header:'
  • Unless we start fzf with an empty list, it would print an invalid header line, because the initial reader starts and passes the first read line as the --header-line.
  • Starting fzf with an empty list would fix the problem, but then an extraneous load event would fire and the Loading ... message would be removed before reload is complete.

Better cache management and improved rendering for --tail #

In the previous version, fzf would evict the cache too aggressively when using the --tail option. Also, it would clear each line before rendering the new line, causing flickering. This has been fixed in 0.54.0. You will notice the difference if you’re browsing a high-throughput stream.

Last modified: Jul 8, 2024
Copyright © 2024 Junegunn Choi