fzf icon indicating copy to clipboard operation
fzf copied to clipboard

Multiple fzf#run() with differentfzf_actions?

Open danijar opened this issue 2 years ago • 3 comments

  • [x] I have read through the manual page (man fzf)
  • [x] I have the latest version of fzf
  • [x] I have searched through the existing issues

Info

  • OS
    • [x] Linux
    • [ ] Mac OS X
    • [ ] Windows
    • [ ] Etc.
  • Shell
    • [ ] bash
    • [x] zsh
    • [ ] fish

Problem / Steps to reproduce

Thanks for this fantastic tool! I'm trying to define multiple FZF commands in my .vimrc that each use a different g:fzf_action option. I understand from the docs that fzf#wrap injects g:fzf_action into the config before we pass it to fzf#run. However, restricts us to using the same action spec for all wrapped commands. I could not find a documented option for specifying the actions mapping directly for fzf#run. Is this possible?

I'm using the following FZF configuration in my vimrc:

Plug 'junegunn/fzf'  # (not junegunn/fzf.vim)
let fzfcmd = "rg --files --hidden -g '!{.git,node_modules,__pycache__}'"
nnoremap <silent> <C-P> :call fzf#run(fzf#wrap({ 'source': fzfcmd }))<cr>
let g:fzf_history_dir = '/tmp/fzf_history'
let g:fzf_layout = {'window': 'new', 'down': '40%'}
let g:fzf_action = {
  \ 'return': 'drop',
  \ 'ctrl-t': 'tab drop',
  \ 'ctrl-x': 'split',
  \ 'ctrl-v': 'vsplit' }

danijar avatar Nov 21 '23 20:11 danijar

Is this possible?

No, it's not. g:fzf_action only applies to the default sinklist (link) implementation which is for opening files. Is there any reason you want to use different key bindings for different commands for opening files? Wouldn't it be confusing?


fzf#wrap injects --expect option to find out which key was pressed, and the default sink function uses the information to select an option from g:fzf_actions. So you would take the same strategy in your commands to support different sets of keys.

let s:spec = { 'source': 'seq 100', 'options': '--multi --expect=ctrl-a,ctrl-b' }
function s:spec.sinklist(lines)
  if len(a:lines) < 2
    echom 'Cancelled'
  endif
  let [key; rest] = a:lines
  let key = empty(key) ? 'enter' : key
  echom printf('You pressed %s to select %s', key, join(rest, ', '))
endfunction

command! MyNumbers call fzf#run(fzf#wrap(s:spec))

junegunn avatar Nov 23 '23 05:11 junegunn

Thanks, that's helpful! In my case, I would like one command to search for all filenames and another one to search inside all files. For both commands, I want to open the file (drop), to open in a tab (drop tab). However, for file content search, I want to not just open the file but also position the cursor at the match (I'm not quite sure how to do that yet). So even though the actions are the semantically same, they need to trigger different code.

danijar avatar Nov 24 '23 20:11 danijar

I see. The problem is that the built-in sink function used for g:fzf_actions wasn't really designed to handle more than just file paths. You can write your own sink function as I suggested above, but it can be tricky if you're not well-versed in VimScripting.

Have you tried using fzf.vim? It provides :Ag, :Rg, and :RG commands, and the underlying fzf#vim#grep and fzf#vim#grep2 functions, which I think will suit your needs.

junegunn avatar Nov 28 '23 02:11 junegunn

@danijar I have the same idea and need, as I am trying to implement a note taking system similar to Notational Velocity (i.e. both title and content of a text file note are meaningful).

This is working progress and it is incomplete, but I think I found a way to search both filenames and file content.

      # Now we setup the two (key) commands used for nv(): fd and ripgrep
      # Searching for filenames, ignoring certain files (.gitignore or .ignore) and searching in $SEARCHPATH only
      FDCMD="fd -e exe -e md --ignore --type file {q} '$SEARCHPATH'"
      # Searching inside the content of files, in $SEARCHPATH only and only for .md and .txt files
      RGCMD="rg -tmd --column --color=always -I -p -C 1 --no-max-columns-preview --sortr=created --heading --line-buffered --no-line-number --no-messages --smart-case -- {q} '$SEARCHPATH'"


      RELOAD="reload:($FDCMD & $RGCMD)|| :"
      OPENER='echo $FZF_SELECT_COUNT
            echo {1}
            echo {2}'
#      OPENER='if [[ $FZF_SELECT_COUNT -eq 0 ]]; then
#            #vim {1} +{2}     # No selection. Open the current line in Vim.
#            vim "+normal G$" +startinsert {1}
            #vi +"norm! go" {1}     #
#          else
#            vim +cw -q {+f}  # Build quickfix list for the selected items.
#          fi'
    fzf < /dev/null \
      --disabled --ansi \
      --bind "start:$RELOAD" --bind "change:$RELOAD" \
      --bind "enter:become:$OPENER" \
      --bind "ctrl-o:execute:$OPENER" \
      --bind 'alt-a:select-all,alt-d:deselect-all,ctrl-/:toggle-preview' \
      --delimiter : \
      --preview 'bat --style=header-filename --color=always --highlight-line {2} {1}' \
      --preview-window '~4,+{2}+4/3,<80(up)' \
      --query "$*"
)

It is incomplete. I just had the idea of using tmux. But I am possibly searching for an overkill.

mgiugliano avatar Jul 06 '24 23:07 mgiugliano