junegunn / fzf

:cherry_blossom: A command-line fuzzy finder
https://junegunn.github.io/fzf/
MIT License
64.15k stars 2.37k forks source link

Trying to use filtered candidates in preview fails (e.g. `--preview='fzf -f {q}'`) #2740

Closed digitalsignalperson closed 2 years ago

digitalsignalperson commented 2 years ago

Info

Problem / Steps to reproduce

Problem

I'm trying to achieve seeing a preview of the items currently matching the query all concatenated together: fzf --preview='head (fzf -f "{q}")'

or even better --preview='head (fzf -f "{q}") | less -p{}' to jump to the section of the current line in the pager

But using fzf -f "{q}" doesn't work when called from the preview function.

Steps to reproduce:

First create some files in an empty folder

echo hi > hi.txt
echo hey > hey.txt
echo yo > yo.txt

The output of fzf -f h is as expected:

hi.txt
hey.txt

The output of head (fzf -f h) is as expected:

==> hi.txt <==
hi

==> hey.txt <==
hey

And that is what I expected to see in the preview window after I type "h" as my query with the command fzf --preview='head (fzf -f "{q}")'. But the preview window is empty.

If I do fzf --preview='fzf -f "{q}"', the preview is empty regardless of the query. If I do fzf --preview='echo "{q}" && fzf -f "{q}"' I can indeed see the query in the panel, so puzzled why no result from fzf.

If I create a preview.sh to try and debug this, I can see it is working in the current directory, but the fzf -f output always returns 1 (no result) even if I hard code a query.

Tried both bash and fish. Also tried wrapping the preview's fzf call in like env -i bash -l -c 'fzf -f {q}' in case some funky environment thing is happening, but no luck.

Further discussion

For the available substitutions for preview, there is no "list of currently filtered items". The closest thing is the list of selected items {+}. Theoretically I can get the "list of currently filtered items" via fzf -f {q} or feeding the query to fzf in script mode.

My use case would also probably also include --multi --bind "enter:select-all+accept" since my goal is to output the list of files matching the query after I'm satisfied with the preview.

While I could also do --preview='head {+}', I'd have to select all first, maybe with some keybinding like ctrl+a, but it breaks the real-time flow of seeing the concatenated view change as I change the query. I also noticed if I did ctrl+a then modified the query the original items are still selected. I'd rather just use the current matches to the query, like if a {q} option existed.

junegunn commented 2 years ago

This should work.

fzf --preview='fzf -f {q} < /dev/tty'

Or something like this

ls | fzf --preview='ls | fzf -f {q}'

Explanation:

With --preview, you're starting an fzf process in a non-interactive environment where the standard input of it is not attached to a TTY device (i.e. keyboard input). When the standard input is a TTY device, fzf will use the default find command (or $FZF_DEFAULT_COMMAND) to populate the list of candidates. This is to allow fzf without explicit input.

# STDIN is not a TTY device, so fzf just reads the output of `find .`
find . | fzf

# It can be tedious to always have to write `find . |` before fzf, so let's provide a shortcut.
# In this case, STDIN is a TTY device, which means fzf is probably not attached to an input process,
# so instead of reading STDIN, fzf starts a child process to populate the input list
export FZF_DEFAULT_COMMAND='find .'
fzf
digitalsignalperson commented 2 years ago

Ah thank you for the explanation! I'll share some of my results below in case it benefits anyone else.

Here's one way to see the concatenated results: fzf --preview='tail -n +1 (fzf -f {q} < /dev/tty)'

I tried piping that into less with a pattern option to jump to current line, but realize it's not interactive to use that way. E.g. fzf --preview='tail -n +1 (fzf -f {q} < /dev/tty) | less --pattern={}'

Ideally I could scroll to a specific point in the preview based on the current line, and it almost seems possible with --preview-window scroll option, but still scratching my head on that one. I don't know how to get the line number it would need.

This is a stopgap solution that approximately does it: ls | fzf --reverse --no-sort --preview='tail -n +1 (fzf -f {q} < /dev/tty | sort | grep -A5000 \'\b{}\b\')'

What it's doing

Example of running the full command:

>                                     ╭───────────────────────────────────╮
  4/4                                 │ ==> hey.txt <==              1/59 │
> hey.txt                             │ hey                               │
  hi.txt                              │                                   │
  large_hey.txt                       │ ==> hi.txt <==                    │
  yo.txt                              │ hi                                │
                                      │                                   │
                                      │ ==> large_hey.txt <==             │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      ╰───────────────────────────────────╯

press down arrow to "scroll down" in the concatenated document (see lines reduce from 59 to 56 and we've "scrolled" down to "hi.txt". Can further use mouse to scroll down in right panel, but obviously can't scroll up to see "hey.txt" contents again. For that press up arrow.

>                                     ╭───────────────────────────────────╮
  4/4                                 │ ==> hi.txt <==               1/56 │
  hey.txt                             │ hi                                │
> hi.txt                              │                                   │
  large_hey.txt                       │ ==> large_hey.txt <==             │
  yo.txt                              │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      │ hey                               │
                                      ╰───────────────────────────────────╯

above example after scrolled down with mouse wheel

>                                     ╭───────────────────────────────────╮
  4/4                                 │ hey                         48/56 │
  hey.txt                             │ hey                               │
> hi.txt                              │ hey                               │
  large_hey.txt                       │ hey                               │
  yo.txt                              │ hey                               │
                                      │                                   │
                                      │                                   │
                                      │ ==> yo.txt <==                    │
                                      │ yo                                │
                                      │                                   │
                                      │                                   │
                                      │                                   │
                                      │                                   │
                                      │                                   │
                                      │                                   │
                                      │                                   │
                                      │                                   │
                                      │                                   │
                                      │                                   │
                                      │                                   │
                                      ╰───────────────────────────────────╯

My use case or at least idea for this is for a sort of zettelkasten system. Imagining