From 9ce2c2179f5d7fc04bd24ccbb56553e4eaa9dd57 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sat, 26 Nov 2016 14:10:16 +0900 Subject: [PATCH] Add fzf#vim#with_preview function for previewing search result Close #225 --- README.md | 30 ++++++++++++++++++++++++++---- autoload/fzf/vim.vim | 40 +++++++++++++++++++++++++++++++++++++++- bin/preview.rb | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 5 deletions(-) create mode 100755 bin/preview.rb diff --git a/README.md b/README.md index 42b2d87..552c12e 100644 --- a/README.md +++ b/README.md @@ -152,18 +152,40 @@ let g:fzf_tags_command = 'ctags -R' let g:fzf_commands_expect = 'alt-enter,ctrl-x' ``` -#### Advanced customization using autoload functions +#### Advanced customization You can use autoload functions to define your own commands. ```vim -" git grep +" Command for git grep +" - fzf#vim#grep(command, with_column, [options], [fullscreen]) command! -bang -nargs=* GGrep \ call fzf#vim#grep('git grep --line-number '.shellescape(), 0, 0) " We use VimEnter event so that the code is run after fzf.vim is loaded -autocmd VimEnter * command! Colors - \ call fzf#vim#colors({'left': '15%', 'options': '--reverse --margin 30%,0'}) +autocmd VimEnter * command! -bang Colors + \ call fzf#vim#colors({'left': '15%', 'options': '--reverse --margin 30%,0'}, 0) + +" Augmenting Ag command using fzf#vim#with_preview function +" * fzf#vim#with_preview([[options], preview window, [toggle keys...]]) +" * Preview script requires Ruby +" * Install Highlight or CodeRay to enable syntax highlighting +" +" :Ag - Start fzf with hidden preview window that can be enabled with "?" key +" :Ag! - Start fzf in fullscreen and display the preview window above +autocmd VimEnter * command! -bang -nargs=* Ag + \ call fzf#vim#ag(, + \ 0 ? fzf#vim#with_preview('up:60%') + \ : fzf#vim#with_preview('right:50%:hidden', '?'), + \ 0) + +" Similarly, we can apply it to fzf#vim#grep. To use ripgrep instead of ag: +command! -bang -nargs=* Rg + \ call fzf#vim#grep( + \ 'rg --column --line-number --no-heading --color=always '.shellescape(), 1, + \ 0 ? fzf#vim#with_preview('up:60%') + \ : fzf#vim#with_preview('right:50%:hidden', '?'), + \ 0) ``` Mappings diff --git a/autoload/fzf/vim.vim b/autoload/fzf/vim.vim index 59de63a..1fca535 100644 --- a/autoload/fzf/vim.vim +++ b/autoload/fzf/vim.vim @@ -29,9 +29,47 @@ set cpo&vim " ------------------------------------------------------------------ let s:layout_keys = ['window', 'up', 'down', 'left', 'right'] +let s:bin = { 'preview': expand(':h:h:h').'/bin/preview.rb' } let s:TYPE = {'dict': type({}), 'funcref': type(function('call')), 'string': type('')} -function s:remove_layout(opts) +" [[options to wrap], preview window expression, [toggle-preview keys...]] +function! fzf#vim#with_preview(...) + " Default options + let options = {} + let window = 'right' + + let args = copy(a:000) + + " Options to wrap + if len(args) && type(args[0]) == s:TYPE.dict + let options = copy(args[0]) + call remove(args, 0) + endif + + " Preview window + if len(args) && type(args[0]) == s:TYPE.string + if args[0] !~# '^\(up\|down\|left\|right\)' + throw 'invalid preview window: '.args[0] + endif + let window = args[0] + call remove(args, 0) + endif + + if !executable('ruby') + return options + endif + + let preview = printf(' --preview-window %s --preview "%s"\ %s\ {}', + \ window, + \ shellescape(s:bin.preview), window =~ 'up\|down' ? '-v' : '') + if len(args) + let preview .= ' --bind '.join(map(args, 'v:val.":toggle-preview"'), ',') + endif + let options.options = get(options, 'options', '').preview + return options +endfunction + +function! s:remove_layout(opts) for key in s:layout_keys if has_key(a:opts, key) call remove(a:opts, key) diff --git a/bin/preview.rb b/bin/preview.rb new file mode 100755 index 0000000..6a3898b --- /dev/null +++ b/bin/preview.rb @@ -0,0 +1,40 @@ +#!/usr/bin/env ruby +# +# usage: ./preview.rb [-v] FILENAME[:LINE][:IGNORED] + +require 'shellwords' + +COMMAND = %[(highlight -O ansi -l {} || coderay {} || cat {}) 2> /dev/null] +ANSI = /\x1b\[[0-9;]*m/ +REVERSE = "\x1b[7m" +RESET = "\x1b[m" + +split = ARGV.delete('-v') + +if ARGV.empty? + puts "usage: #$0 [-v] FILENAME[:LINENO][:IGNORED]" + exit 1 +end + +file, center = ARGV.first.split(':') + +unless File.readable? file + puts "File not found: #{file}" + exit 1 +end + +center = (center || 0).to_i +height = File.readable?('/dev/tty') ? `stty size < /dev/tty`.split.first.to_i : 40 +height /= 2 if split +height -= 2 # preview border +offset = [1, center - height / 3].max + +IO.popen(['sh', '-c', COMMAND.gsub('{}', Shellwords.shellescape(file))]) do |io| + io.each_line.drop(offset - 1).take(height).each_with_index do |line, lno| + if lno + offset == center + puts REVERSE + line.chomp.gsub(ANSI) { |m| m + REVERSE } + RESET + else + puts line + end + end +end