vim-devicons icon indicating copy to clipboard operation
vim-devicons copied to clipboard

FZF Support?

Open mhartington opened this issue 8 years ago • 50 comments

FZF has a vim interface for fast file searching.

https://github.com/junegunn/fzf

Similar to ctrlp/unite. Would be interested in have some icons in there too :grin:

mhartington avatar Aug 24 '15 21:08 mhartington

That's a cool idea :+1: . Just quickly looked at the code and I am not sure how much would be involved but definitely would like to try at some point :smile:

Thanks

ryanoasis avatar Aug 25 '15 12:08 ryanoasis

:+1:

danihodovic avatar Dec 13 '15 16:12 danihodovic

I really like this idea and have wanted this for a while as well. I think that the only way to achieve this is create a custom FZF source that appends the correct file icons in front of the filenames.

The problem with this approach is that we limit the user to using a specific FZF custom source and not have the devicons in all there already defined sources.

I'm willing to take this up and look into this further.

alexanderjeurissen avatar Apr 27 '16 10:04 alexanderjeurissen

Ok so I've investigated it some more, and what I found so far is: Creating a custom source where we execute the default FZF_DEFAULT_COMMAND and prepend the icon for each resulting line seems like a really cumbersome way to do this.

nonetheless altough it took me a full afternoon (I'm a vimscript novice) I got it done (adding icons to fzf) see below screenshot:

screen shot 2016-04-27 at 16 43 01

The only part left is getting FZF to actually edit the file because now it tries to edit (icon + filename) as a path.

alexanderjeurissen avatar Apr 27 '16 14:04 alexanderjeurissen

@alexanderjeurissen Nice! Very cool. So this is for FZF in the terminal (executable)? Your previous comment I thought it was just the vim plugin of FZF.

The only part left is getting FZF to actually edit the file because now it tries to edit (icon + filename) as a path.

Ah yup, I had a similar struggle with CtrlP (eventually sending a PR to more easily do some things).

Not sure if it will be of any help to your particular case but this is how I was stripping out the glyphs for CtrlP: https://github.com/ryanoasis/vim-devicons/blob/master/plugin/webdevicons.vim#L505-L509

ryanoasis avatar Apr 27 '16 15:04 ryanoasis

@ryanoasis DONE.

code:

" Files + devicons
function! Fzf_dev()
  function! s:files()
    let files = split(system($FZF_DEFAULT_COMMAND), '\n')
    return s:prepend_icon(files)
  endfunction

  function! s:prepend_icon(candidates)
    let result = []
    for candidate in a:candidates
      let filename = fnamemodify(candidate, ':p:t')
      let icon = WebDevIconsGetFileTypeSymbol(filename, isdirectory(filename))
      call add(result, printf("%s %s", icon, candidate))
    endfor

    return result
  endfunction

  function! s:edit_file(item)
    let parts = split(a:item, ' ')
    let file_path = get(parts, 1, '')
    execute 'silent e' file_path
  endfunction

  call fzf#run({
        \ 'source': <sid>files(),
        \ 'sink':   function('s:edit_file'),
        \ 'options': '-m -x +s',
        \ 'down':    '40%' })
endfunction

alexanderjeurissen avatar Apr 27 '16 15:04 alexanderjeurissen

as I said the only downside of this approach is that people have to use this function.. as we require control over the source and sink options 😢 But it makes use of the $FZF_DEFAULT_COMMAND which is nice because that is where people specify if they want to use find or ag etc.

this function however doesn't include buffer files and MRU, not sure if that's a deal breaker for a lot of people. We could add them ofc but then we have to offer multiple variants or options ;)

And to answer your question: Yes this is only for using fzf in vim, the standalone fzf.vim plugin isn't required though.

alexanderjeurissen avatar Apr 27 '16 15:04 alexanderjeurissen

I'll tackle some of the problems mentioned in my previous comment and refine the code. Expect a PullRequest somewhere this weekend. In the mean time people like ( @mhartington ) can use the snippet above ;-)

alexanderjeurissen avatar Apr 27 '16 21:04 alexanderjeurissen

@alexanderjeurissen Any movement on this by chance?

michaelmior avatar Aug 03 '16 15:08 michaelmior

@michaelmior the code snippet that i shared in a previous comment still works. However it is required that the user specifies this in their vimrc.

I so far haven't found time to work on implementing this in the devicons vim plugin. I don't expect to have spare time to work on this till september. So if people think it's a high priority feature, they can use the code I provided as a starting point for the implementation.

alexanderjeurissen avatar Aug 04 '16 10:08 alexanderjeurissen

Another fzf user here. I tried the snippet from @alexanderjeurissen without much success. Anything else needed to get it working?

dvcrn avatar Nov 15 '16 03:11 dvcrn

@dvcrn @alexanderjeurissen's snippet works on my setup. Just calling :FZF won't work, you need to issue :call Fzf_dev() once you've copied the snippet to your .vimrc.

pyrho avatar Jul 24 '17 23:07 pyrho

If you want preview you can install the rouge gem: gem install rouge and use this code

" Files + devicons
function! Fzf_dev()
  let l:fzf_files_options = '--preview "rougify {2..-1} | head -'.&lines.'"'

  function! s:files()
    let l:files = split(system($FZF_DEFAULT_COMMAND), '\n')
    return s:prepend_icon(l:files)
  endfunction

  function! s:prepend_icon(candidates)
    let l:result = []
    for l:candidate in a:candidates
      let l:filename = fnamemodify(l:candidate, ':p:t')
      let l:icon = WebDevIconsGetFileTypeSymbol(l:filename, isdirectory(l:filename))
      call add(l:result, printf('%s %s', l:icon, l:candidate))
    endfor

    return l:result
  endfunction

  function! s:edit_file(item)
    let l:pos = stridx(a:item, ' ')
    let l:file_path = a:item[pos+1:-1]
    execute 'silent e' l:file_path
  endfunction

  call fzf#run({
        \ 'source': <sid>files(),
        \ 'sink':   function('s:edit_file'),
        \ 'options': '-m ' . l:fzf_files_options,
        \ 'down':    '40%' })
endfunction
image

dkarter avatar Jan 01 '18 00:01 dkarter

@dkarter great solution! There is a simple way to use the 'internal' fzf.vim preview ( fzf#vim#with_preview)? Trying it without success.

maxdevjs avatar Jan 24 '18 18:01 maxdevjs

I've tried using the two Fzf_dev() commands proposed here (nvim 0.2.3-dev on macos) and it produces no results. I suspect $FZF_DEFAULT_COMMAND might be part of my problem (it's not defined in my env) ? How can I debug an empty file list being returned? Standard FZF works fine, but is (obviously) missing the devicons.

mellery451 avatar Mar 07 '18 18:03 mellery451

Follow-up: looks like installing fd via homebrew and then setting FZF_DEFAULT_COMMAND to fd --type f as described in the FZF docs allows the snippets to work for me. I don't think FZF_DEFAULT_COMMAND is strictly required for normal fzf use, but I'm fine with this change.

mellery451 avatar Mar 07 '18 19:03 mellery451

I use ag aka The Silver Searcher as my FZF_DEFAULT_COMMAND, which IMO is great because:

  1. It's built for speed
  2. It respects .gitignore files in your project
  3. Allows tweaking global ignore patterns via ~/.agignore

Ag

If you want the same setup install via homebrew (macOS):

brew install the_silver_searcher

and add this to your .vimrc:

let $FZF_DEFAULT_COMMAND = 'ag --hidden -l -g ""'

RipGrep

If you want it to be even faster - use ripgrep

brew install ripgrep

and add this to your .vimrc:

let $FZF_DEFAULT_COMMAND = 'rg --hidden -l ""'

Compared with fd the above are many orders of magnitude faster in my experiments.

dkarter avatar Mar 07 '18 22:03 dkarter

@dkarter Good solution. The preview command can be simplified into this:

let l:fzf_files_options = '--preview "rougify {2..} | head -'.&lines.'"'

Here 2.. means columns from 2nd to last, which compose the expected filename.

wfxr avatar May 27 '18 14:05 wfxr

@dkarter These lines in edit_file(item) function has problem when the filename contains white spaces.

let l:parts = split(a:item, ' ')
let l:file_path = get(l:parts, 1, '')

Suggest replacing it by the following:

let l:pos = stridx(a:item, ' ')
let l:file_path = a:item[pos+1:]

wfxr avatar May 27 '18 15:05 wfxr

Thanks @wfxr! I've updated my comment above to include your improvements.

dkarter avatar May 27 '18 18:05 dkarter

@dkarter also, to remove the dependency of the rouge gem, you can use this:

let l:fzf_file_options = '--preview "[[ \$(file --mime {2..-1}) =~ binary ]] && echo {2..-1} is a binary file || (highlight -O ansi -l {2..-1} || coderay {2..-1} || rougify {2..-1} || cat {2..-1}) 2> /dev/null | head -'.&lines.'"'

which tries the different supported highlighting programs and finally defaults to cat.

see here: https://github.com/junegunn/fzf#preview-window

idoa01 avatar May 29 '18 10:05 idoa01

You can take advantage of the neovim async api with the following:

function! Fzf_dev(no_git) abort
  let s:file_list = ['']
  let s:callbacks = {
    \ 'on_stdout': 'OnEvent',
    \ 'on_exit': 'OnExit'
    \ }

  if !a:no_git
    call jobstart([ 'rg',  '--files' ], s:callbacks)
  else
    call jobstart([ 'rg', '-L', '-i', '--no-ignore', '--files' ], s:callbacks)
  endif

  function! OnEvent(job_id, data, event)
    let s:file_list[-1] .= a:data[0]
    call extend(s:file_list, a:data[1:])
  endfunction

  function! OnExit(job_id, data, event)
      call fzf#run({
        \ 'source': s:prepend_icon(s:file_list),
        \ 'sink':   function('s:edit_file'),
        \ 'options': '-m',
        \ 'down': '40%'
        \ })
  endfunction

  function! s:prepend_icon(candidates)
    let l:result = []
    for l:candidate in a:candidates
      let l:filename = fnamemodify(l:candidate, ':p:t')
      let l:icon = WebDevIconsGetFileTypeSymbol(l:filename, isdirectory(l:filename))
      call add(l:result, printf('%s %s', l:icon, l:candidate))
    endfor
    return l:result
  endfunction

  function! s:edit_file(item)
    let l:pos = stridx(a:item, ' ')
    let l:file_path = a:item[l:pos+1:-1]
    execute 'silent e' l:file_path
  endfunction
endfunction

Then it can be called with a 1 or 0 if you want to search only the files tracked by git, or all files in the project

jamestthompson3 avatar Oct 22 '18 18:10 jamestthompson3

Used @dkarter's comment as a starting point, and made some performance improvements and added some git diff support as well.

TLDR: Wrote a tool to do the devicon lookup in Rust (so it actually doesn't depend on this repo at all), and used bat for the previewing. The Rust lookup is about 10x faster than the VIM only implementation, and allows streaming before all results are populated. Directories that took ~4.5 seconds with the VIM implementation, finished near instantaneously with the Rust version.

You can install both with

cargo install bat
cargo install devicon-lookup

And here is the VIM config (also includes support for emulating :GFiles?):

" Files + devicons
function! Fzf_files_with_dev_icons(command)
  let l:fzf_files_options = '--preview "bat --color always --style numbers {2..} | head -'.&lines.'"'
   function! s:edit_devicon_prepended_file(item)
    let l:file_path = a:item[4:-1]
    execute 'silent e' l:file_path
  endfunction
   call fzf#run({
        \ 'source': a:command.' | devicon-lookup',
        \ 'sink':   function('s:edit_devicon_prepended_file'),
        \ 'options': '-m ' . l:fzf_files_options,
        \ 'down':    '40%' })
endfunction
 function! Fzf_git_diff_files_with_dev_icons()
  let l:fzf_files_options = '--ansi --preview "sh -c \"(git diff --color=always -- {3..} | sed 1,4d; bat --color always --style numbers {3..}) | head -'.&lines.'\""'
   function! s:edit_devicon_prepended_file_diff(item)
    echom a:item
    let l:file_path = a:item[7:-1]
    echom l:file_path
    let l:first_diff_line_number = system("git diff -U0 ".l:file_path." | rg '^@@.*\+' -o | rg '[0-9]+' -o | head -1")
     execute 'silent e' l:file_path
    execute l:first_diff_line_number
  endfunction
   call fzf#run({
        \ 'source': 'git -c color.status=always status --short --untracked-files=all | devicon-lookup',
        \ 'sink':   function('s:edit_devicon_prepended_file_diff'),
        \ 'options': '-m ' . l:fzf_files_options,
        \ 'down':    '40%' })
endfunction
 " Open fzf Files
map <C-f> :call Fzf_files_with_dev_icons($FZF_DEFAULT_COMMAND)<CR> " :Files
map <C-d> :call Fzf_git_diff_files_with_dev_icons()<CR> " :GFiles?
map <C-g> :call Fzf_files_with_dev_icons("git ls-files \| uniq")<CR> " :GFiles

See my blog post for the long form write up! https://coreyja.com/blog/2018/11/17/vim-fzf-with-devicons.html

coreyja avatar Nov 18 '18 18:11 coreyja

@coreyja Looks great, but all icons show up as question marks. Any idea why?

jonasstenberg avatar Nov 20 '18 13:11 jonasstenberg

@jonasstenberg hmm do you have a nerd font compatible font installed? And do you see Dev icons elsewhere, assuming you have this, vim-devicons, plugin installed?

coreyja avatar Nov 20 '18 14:11 coreyja

@coreyja

@jonasstenberg hmm do you have a nerd font compatible font installed?

I installed these fonts using brew, no luck though.

And do you see Dev icons elsewhere

Not sure where to look if I can see them elsewhere?

assuming you have this, vim-devicons, plugin installed?

vim-devicons is installed.

Thanks for the quick reply!

jonasstenberg avatar Nov 20 '18 14:11 jonasstenberg

My bad, had to switch font in iTerm2 as well.

Awesome job with the devicons!

jonasstenberg avatar Nov 20 '18 15:11 jonasstenberg

@jonasstenberg Ahh! Glad you got it figured out! Hope you enjoy and let me know if you run into other issues!

coreyja avatar Nov 20 '18 16:11 coreyja

@coreyja this is very cool, but it seems to fail for .txt files on my machine...I'm guessing because devicon-lookup is not finding a symbol for that file type (although I believe there is as devicon for CMakeLists.txt, however). How should this snippet behave when the icon lookup fails? The failure manifests specifically as trying to open a (nonexistent) path with the first two chars of the path stripped off. This is on macos, if it matters.

mellery451 avatar Nov 28 '18 00:11 mellery451

@mellery451 ahh sorry you are running into issues. From your description of what happens it sounds like the code that is supposed to chop off the dev char is also chopping off some of your file name. Which is part of the vim script. Are you seeing this with the standard files view or with the git changed files version? I probably won't have time to look into this before the weekend but I'll try to take a look for ya!

Also I might move this little script to a repo, so that we could use the issue tracker and stop using this comment thread!

coreyja avatar Nov 28 '18 14:11 coreyja