Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "reload" action for dynamically updating the input list #1750

junegunn opened this issue Nov 11, 2019 · 5 comments

Add "reload" action for dynamically updating the input list #1750

junegunn opened this issue Nov 11, 2019 · 5 comments


Copy link

junegunn commented Nov 11, 2019

See devel branch for the progress.


Add bindable reload action that can start an arbitrary program and dynamically replace the input list of fzf with its result without restarting fzf.


fzf was designed to be a Unix filter that consumes input only once. However, due to the interactive nature of it, some users want to update the input without restarting fzf altogether (using esoteric --no-clear option).

For example,

  • Input comes from a REST API that generates dynamic content, and you want to see the updated list by pressing a special key such as CTRL-R (R for refresh or reload).
  • You want to press a set of keys to dynamically switch between different sets of inputs; CTRL-F for a list of files, and CTRL-D for a list of directories.
  • You use fzf as the secondary filter to the result of a primary filter program such as ripgrep or silver searcher. And you want to restart the primary filter program with an updated query string you typed on fzf because they are much more efficient than fzf for searching through the file contents.
    • You may even want to restart the primary filter program every time you change the query string on fzf. In this case, you probably want to use fzf only as a selector interface rather than a secondary "fuzzy filter", especially because of the incompatible search syntax. The search is completely done by the primary filter as you use the new --phony option (No-filter search option #1723).

Instead of introducing a separate mode of execution as suggested in #751 and #1736. I'd like to add a special action called reload that can be bound to a key or change event using the good old --bind.


1. Update the list of processes by pressing CTRL-R

ps -ef | fzf --bind 'ctrl-r:reload(ps -ef)' --header 'Press CTRL-R to reload' \
             --header-lines=1 --layout=reverse

2. Switch between sources by pressing CTRL-D or CTRL-F

find . -type f |
  fzf --bind 'ctrl-d:reload(find . -type d),ctrl-f:reload(find . -type f)'

There are two problems here:

  • We're repeating find . -type f command twice
  • The initial find process may take a long time to finish. Since fzf cannot kill the process behind the standard input, the process will keep running even after we hit CTRL-D.

To work around the issues, we set $FZF_DEFAULT_COMMAND to the initial find command, so fzf can start the process and kill it when it has to.

FZF_DEFAULT_COMMAND='find . -type f' fzf \
  --bind 'ctrl-d:reload(find . -type d),ctrl-f:reload($FZF_DEFAULT_COMMAND)'

3. Ripgrep integration

The following example uses fzf as the selector interface for ripgrep. We bound reload action to change event, so every time you type on fzf, ripgrep process will restart with the updated query string denoted by the placeholder expression {q}. Also, note that we used --phony option so that fzf doesn't perform any secondary filtering.

RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
  fzf --bind "change:reload:$RG_PREFIX {q} || true" \
      --ansi --phony --query "$INITIAL_QUERY"

If ripgrep doesn't find any matches, it will exit with a non-zero exit status, and fzf will warn you about it. To suppress the warning message, we added || true to the command, so that it always exits with 0.

4. Ripgrep integration with fzf.vim

function! RipgrepFzf(query, fullscreen)
  let command_fmt = 'rg --column --line-number --no-heading --color=always --smart-case %s || true'
  let initial_command = printf(command_fmt, shellescape(a:query))
  let reload_command = printf(command_fmt, '{q}')
  let spec = {'options': ['--phony', '--query', a:query, '--bind', 'change:reload:'.reload_command]}
  call fzf#vim#grep(initial_command, 1, fzf#vim#with_preview(spec), a:fullscreen)

command! -nargs=* -bang Rg call RipgrepFzf(<q-args>, <bang>0)
Copy link
Owner Author

0.19.0 released.

@junegunn junegunn unpinned this issue Nov 15, 2019
junegunn added a commit to junegunn/fzf.vim that referenced this issue Nov 17, 2019
Related #907
Also junegunn/fzf#1750

function! RipgrepFzf(query, fullscreen)
  let command_fmt = 'rg --column --line-number --no-heading --color=always --smart-case %s || true'
  let initial_command = printf(command_fmt, shellescape(a:query))
  let reload_command = printf(command_fmt, '{q}')
  let options = {'options': ['--phony', '--query', a:query, '--bind', 'change:reload:'.reload_command]}
  call fzf#vim#grep(initial_command, 1, options, a:fullscreen)

command! -nargs=* -bang RF call RipgrepFzf(<q-args>, <bang>0)
Copy link

lanespade commented Aug 9, 2020

Hey @junegunn I am trying to bind the Ripgrep Integration to Ctrl-f (in zsh), and I don't want to change FZF_DEFAULT_COMMAND so my other settings are affected, I have come up with the following...

RG_PREFIX='rg --column --line-number --no-heading --color=always --smart-case '
bindkey -s '^f' "$RG_PREFIX '$INITIAL_QUERY' | fzf --bind \"change:reload:$RG_PREFIX {q} || true\" --ansi --phony --query \"$INITIAL_QUERY\"^M"

...which works, but a couple things...

  1. How can I get the file printed back to my current terminal command (similar to how Ctrl-t works)? It seem I have to bind to enter with execute and something like cut... but I haven't been able to get it to work yet...
  2. I am frequently seeing that exiting the command takes several seconds, regardless of whether an entry was accepted or I exited via Ctrl-c, any insights into why this may be and how to improve it?

Copy link
Owner Author

  1. I'm not a zsh user, so I can't give you the best advice. You might want to study the code in
  2. That's why we're temporarily setting FZF_DEFAULT_COMMAND in the above example. By doing so, fzf launches the process and it can kill it when it exits.

Copy link

Thanks for the tips @junegunn! I solved it by adding the following to my .zshrc, in case others are interested...

# Ctrl-F (Find in Files)
RG_PREFIX='rg --column --line-number --no-heading --color=always --smart-case '

__fif() {
    fzf --bind "change:reload:$RG_PREFIX {q} $HOME || true" --ansi --phony --query "$INITIAL_QUERY" | cut -d ':' -f1

find-in-files() {
  local ret=$?
  zle reset-prompt
  return $ret

zle -N find-in-files
bindkey '^f' find-in-files

...note that I added $HOME in a couple places as I usually want to search beyond just the current directory (and use ~/.ignore or alternatives to maintain speed).

Copy link

Is it possible to get this working with -1, so once a selection is narrowed down to 1, it's picked?

netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this issue Oct 13, 2020

- Added `--preview-window` options for disabling flags
    - `nocycle`
    - `nohidden`
    - `nowrap`
    - `default`
- Built with Go 1.14.9 due to performance regression
    - golang/go#40727

- Support preview scroll offset relative to window height
  git grep --line-number '' |
    fzf --delimiter : \
        --preview 'bat --style=numbers --color=always --highlight-line {2} {1}' \
        --preview-window +{2}-/2
- Added `--preview-window` option for sharp edges (`--preview-window sharp`)
- Added `--preview-window` option for cyclic scrolling (`--preview-window cycle`)
- Reduced vertical padding around the preview window when `--preview-window
  noborder` is used
- Added actions for preview window
    - `preview-half-page-up`
    - `preview-half-page-down`
- Vim
    - Popup width and height can be given in absolute integer values
    - Added `fzf#exec()` function for getting the path of fzf executable
        - It also downloads the latest binary if it's not available by running
          `./install --bin`
- Built with Go 1.15.2
    - We no longer provide 32-bit binaries

- Added more options for `--bind`
    - `backward-eof` event
      # Aborts when you delete backward when the query prompt is already empty
      fzf --bind backward-eof:abort
    - `refresh-preview` action
      # Rerun preview command when you hit '?'
      fzf --preview 'echo $RANDOM' --bind '?:refresh-preview'
    - `preview` action
      # Default preview command with an extra preview binding
      fzf --preview 'file {}' --bind '?:preview:cat {}'

      # A preview binding with no default preview command
      # (Preview window is initially empty)
      fzf --bind '?:preview:cat {}'

      # Preview window hidden by default, it appears when you first hit '?'
      fzf --bind '?:preview:cat {}' --preview-window hidden
- Added preview window option for setting the initial scroll offset
  # Initial scroll offset is set to the line number of each line of
  # git grep output *minus* 5 lines
  git grep --line-number '' |
    fzf --delimiter : --preview 'nl {1}' --preview-window +{2}-5
- Added support for ANSI colors in `--prompt` string
- Smart match of accented characters
    - An unaccented character in the query string will match both accented and
      unaccented characters, while an accented character will only match
      accented characters. This is similar to how "smart-case" match works.
- Vim plugin
    - `tmux` layout option for using fzf-tmux
      let g:fzf_layout = { 'tmux': '-p90%,60%' }

- Shell extension
    - CTRL-R will remove duplicate commands
- fzf-tmux
    - Supports tmux popup window (require tmux 3.2 or above)
        - ```sh
          # 50% width and height
          fzf-tmux -p

          # 80% width and height
          fzf-tmux -p 80%

          # 80% width and 40% height
          fzf-tmux -p 80%,40%
          fzf-tmux -w 80% -h 40%

          # Window position
          fzf-tmux -w 80% -h 40% -x 0 -y 0
          fzf-tmux -w 80% -h 40% -y 1000

          # Write ordinary fzf options after --
          fzf-tmux -p -- --reverse --info=inline --margin 2,4 --border
        - On macOS, you can build the latest tmux from the source with
          `brew install tmux --HEAD`
- Bug fixes
    - Fixed Windows file traversal not to include directories
    - Fixed ANSI colors with `--keep-right`
    - Fixed _fzf_complete for zsh
- Built with Go 1.14.1

- `--height` option is now available on Windows as well (@kelleyma49)
- Added `--pointer` and `--marker` options
- Added `--keep-right` option that keeps the right end of the line visible
  when it's too long
- Style changes
    - `--border` will now print border with rounded corners around the
      finder instead of printing horizontal lines above and below it.
      The previous style is available via `--border=horizontal`
    - Unicode spinner
- More keys and actions for `--bind`
- Added PowerShell script for downloading Windows binary
- Vim plugin: Built-in floating windows support
  let g:fzf_layout = { 'window': { 'width': 0.9, 'height': 0.6 } }
- bash: Various improvements in key bindings (CTRL-T, CTRL-R, ALT-C)
    - CTRL-R will start with the current command-line as the initial query
    - CTRL-R properly supports multi-line commands
- Fuzzy completion API changed
  # Previous: fzf arguments given as a single string argument
  # - This style is still supported, but it's deprecated
  _fzf_complete "--multi --reverse --prompt=\"doge> \"" "$@" < <(
    echo foo

  # New API: multiple fzf arguments before "--"
  # - Easier to write multiple options
  _fzf_complete --multi --reverse --prompt="doge> " -- "$@" < <(
    echo foo
- Bug fixes and improvements

- Customizable preview window color (`preview-fg` and `preview-bg` for `--color`)
  fzf --preview 'cat {}' \
      --color 'fg:#bbccdd,fg+:#ddeeff,bg:#334455,preview-bg:#223344,border:#778899' \
      --border --height 20 --layout reverse --info inline
- Removed the immediate flicking of the screen on `reload` action.
  : | fzf --bind 'change:reload:seq {q}' --phony
- Added `clear-query` and `clear-selection` actions for `--bind`
- It is now possible to split a composite bind action over multiple `--bind`
  expressions by prefixing the later ones with `+`.
  fzf --bind 'ctrl-a:up+up'

  # Can be now written as
  fzf --bind 'ctrl-a:up' --bind 'ctrl-a:+up'

  # This is useful when you need to write special execute/reload form (i.e. `execute:...`)
  # to avoid parse errors and add more actions to the same key
  fzf --multi --bind 'ctrl-l:select-all+execute:less {+f}' --bind 'ctrl-l:+deselect-all'
- Fixed parse error of `--bind` expression where concatenated execute/reload
  action contains `+` character.
  fzf --multi --bind 'ctrl-l:select-all+execute(less {+f})+deselect-all'
- Fixed bugs of reload action
    - Not triggered when there's no match even when the command doesn't have
      any placeholder expressions
    - Screen not properly cleared when `--header-lines` not filled on reload


- Added `--phony` option which completely disables search functionality.
  Useful when you want to use fzf only as a selector interface. See below.
- Added "reload" action for dynamically updating the input list without
  restarting fzf. See junegunn/fzf#1750 to learn
  more about it.
  # Using fzf as the selector interface for ripgrep
  RG_PREFIX="rg --column --line-number --no-heading --color=always --smart-case "
    fzf --bind "change:reload:$RG_PREFIX {q} || true" \
        --ansi --phony --query "$INITIAL_QUERY"
- `--multi` now takes an optional integer argument which indicates the maximum
  number of items that can be selected
  seq 100 | fzf --multi 3 --reverse --height 50%
- If a placeholder expression for `--preview` and `execute` action (and the
  new `reload` action) contains `f` flag, it is replaced to the
  path of a temporary file that holds the evaluated list. This is useful
  when you multi-select a large number of items and the length of the
  evaluated string may exceed [`ARG_MAX`][argmax].
  # Press CTRL-A to select 100K items and see the sum of all the numbers
  seq 100000 | fzf --multi --bind ctrl-a:select-all \
                   --preview "awk '{sum+=\$1} END {print sum}' {+f}"
- `deselect-all` no longer deselects unmatched items. It is now consistent
  with `select-all` and `toggle-all` in that it only affects matched items.
- Due to the limitation of bash, fuzzy completion is enabled by default for
  a fixed set of commands. A helper function for easily setting up fuzzy
  completion for any command is now provided.
  # usage: _fzf_setup_completion path|dir COMMANDS...
  _fzf_setup_completion path git kubectl
- Info line style can be changed by `--info=STYLE`
    - `--info=default`
    - `--info=inline` (same as old `--inline-info`)
    - `--info=hidden`
- Preview window border can be disabled by adding `noborder` to
- When you transform the input with `--with-nth`, the trailing white spaces
  are removed.
- `ctrl-\`, `ctrl-]`, `ctrl-^`, and `ctrl-/` can now be used with `--bind`
- See for more details

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
None yet

No branches or pull requests

3 participants