mirror of
https://github.com/dense-analysis/ale.git
synced 2026-02-07 18:31:04 +08:00
ALEFindReferences: add -fzf flag to show output in fzf (#5018)
* references: add ALEFindReferences -fzf option Allows using -fzf to show previews using fzf.vim. Includes: - add support for opening in bufers, splits, tabs and for adding matches quickfix - add support for -relative - add fzf preview `--highlight-line` option - add fzf.vim autoload module * tests: fix references tests for fzf support update
This commit is contained in:
85
autoload/ale/fzf.vim
Normal file
85
autoload/ale/fzf.vim
Normal file
@@ -0,0 +1,85 @@
|
||||
" Author: bretello https://github.com/bretello
|
||||
" Description: Functions for integrating with fzf
|
||||
|
||||
" Handle references found with ALEFindReferences using fzf
|
||||
function! ale#fzf#ShowReferences(item_list, options) abort
|
||||
let l:name = 'LSP References'
|
||||
let l:capname = 'References'
|
||||
let l:items = copy(a:item_list)
|
||||
let l:cwd = getcwd() " no-custom-checks
|
||||
let l:sep = has('win32') ? '\' : '/'
|
||||
|
||||
function! s:relative_paths(line) closure abort
|
||||
return substitute(a:line, '^' . l:cwd . l:sep, '', '')
|
||||
endfunction
|
||||
|
||||
if get(a:options, 'use_relative_paths')
|
||||
let l:items = map(filter(l:items, 'len(v:val)'), 's:relative_paths(v:val)')
|
||||
endif
|
||||
|
||||
let l:start_query = ''
|
||||
let l:fzf_options = {
|
||||
\ 'source': items,
|
||||
\ 'options': ['--prompt', l:name.'> ', '--query', l:start_query,
|
||||
\ '--multi', '--bind', 'alt-a:select-all,alt-d:deselect-all',
|
||||
\ '--delimiter', ':', '--preview-window', '+{2}/2']
|
||||
\}
|
||||
|
||||
call add(l:fzf_options['options'], '--highlight-line') " this only works for more recent fzf versions (TODO: handle version check?)
|
||||
|
||||
" wrap with #with_preview and #fzfwrap before adding the sinklist,
|
||||
" otherwise --expect options are not added
|
||||
let l:opts_with_preview = fzf#vim#with_preview(l:fzf_options)
|
||||
let l:bang = 0 " TODO: handle bang
|
||||
let l:wrapped = fzf#wrap(l:name, l:opts_with_preview, l:bang)
|
||||
|
||||
call remove(l:wrapped, 'sink*') " remove the default sinklist to add in our custom sinklist
|
||||
|
||||
function! l:wrapped.sinklist(lines) closure abort
|
||||
if len(a:lines) <2
|
||||
return
|
||||
endif
|
||||
|
||||
let l:cmd = a:lines[0]
|
||||
|
||||
function! s:references_to_qf(line) closure abort
|
||||
" mimics ag_to_qf in junegunn/fzf.vim
|
||||
let l:parts = matchlist(a:line, '\(.\{-}\)\s*:\s*\(\d\+\)\%(\s*:\s*\(\d\+\)\)\?\%(\s*:\(.*\)\)\?')
|
||||
let l:filename = &autochdir ? fnamemodify(l:parts[1], ':p') : l:parts[1]
|
||||
|
||||
return {'filename': l:filename, 'lnum': l:parts[2], 'col': l:parts[3], 'text': l:parts[4]}
|
||||
endfunction
|
||||
|
||||
let l:references = map(filter(a:lines[1:], 'len(v:val)'), 's:references_to_qf(v:val)')
|
||||
|
||||
if empty(l:references)
|
||||
return
|
||||
endif
|
||||
|
||||
if get(a:options, 'open_in') is# 'quickfix'
|
||||
call setqflist([], 'r')
|
||||
call setqflist(l:references, 'a')
|
||||
|
||||
call ale#util#Execute('cc 1')
|
||||
endif
|
||||
|
||||
function! s:action(key, file) abort
|
||||
" copied from fzf.vim
|
||||
let l:default_action = {
|
||||
\ 'ctrl-t': 'tab split',
|
||||
\ 'ctrl-x': 'split',
|
||||
\ 'ctrl-v': 'vsplit' }
|
||||
|
||||
let fzf_actions = get(g:, 'fzf_action', l:default_action)
|
||||
let l:Cmd = get(fzf_actions, a:key, 'edit')
|
||||
|
||||
let l:cursor_cmd = escape('call cursor(' . a:file['lnum'] . ',' . a:file['col'] . ')', ' ')
|
||||
let l:fullcmd = l:Cmd . ' +' . l:cursor_cmd . ' ' . fnameescape(a:file['filename'])
|
||||
silent keepjumps keepalt execute fullcmd
|
||||
endfunction
|
||||
|
||||
return map(l:references, 's:action(cmd, v:val)')
|
||||
endfunction
|
||||
|
||||
call fzf#run(l:wrapped)
|
||||
endfunction
|
||||
@@ -1,5 +1,6 @@
|
||||
let g:ale_default_navigation = get(g:, 'ale_default_navigation', 'buffer')
|
||||
let g:ale_references_show_contents = get(g:, 'ale_references_show_contents', 1)
|
||||
let g:ale_references_use_fzf = get(g:, 'ale_references_use_fzf', 0)
|
||||
|
||||
let s:references_map = {}
|
||||
|
||||
@@ -82,6 +83,16 @@ function! ale#references#FormatLSPResponseItem(response_item, options) abort
|
||||
endtry
|
||||
endif
|
||||
|
||||
if get(a:options, 'use_fzf') == 1
|
||||
let l:filename = ale#util#ToResource(a:response_item.uri)
|
||||
let l:nline = a:response_item.range.start.line + 1
|
||||
let l:ncol = a:response_item.range.start.character + 1
|
||||
|
||||
" grep-style output (filename:line:col:text) so that fzf can properly
|
||||
" show matches and previews using ':' as delimiter
|
||||
return l:filename . ':' . l:nline . ':' . l:ncol . ':' . l:line_text
|
||||
endif
|
||||
|
||||
if get(a:options, 'open_in') is# 'quickfix'
|
||||
return {
|
||||
\ 'filename': l:filename,
|
||||
@@ -100,32 +111,39 @@ function! ale#references#FormatLSPResponseItem(response_item, options) abort
|
||||
endfunction
|
||||
|
||||
function! ale#references#HandleLSPResponse(conn_id, response) abort
|
||||
if has_key(a:response, 'id')
|
||||
\&& has_key(s:references_map, a:response.id)
|
||||
let l:options = remove(s:references_map, a:response.id)
|
||||
if ! (has_key(a:response, 'id') && has_key(s:references_map, a:response.id))
|
||||
return
|
||||
endif
|
||||
|
||||
" The result can be a Dictionary item, a List of the same, or null.
|
||||
let l:result = get(a:response, 'result', [])
|
||||
let l:item_list = []
|
||||
let l:options = remove(s:references_map, a:response.id)
|
||||
|
||||
if type(l:result) is v:t_list
|
||||
for l:response_item in l:result
|
||||
call add(l:item_list,
|
||||
\ ale#references#FormatLSPResponseItem(l:response_item, l:options)
|
||||
\)
|
||||
endfor
|
||||
endif
|
||||
" The result can be a Dictionary item, a List of the same, or null.
|
||||
let l:result = get(a:response, 'result', [])
|
||||
let l:item_list = []
|
||||
|
||||
if empty(l:item_list)
|
||||
call ale#util#Execute('echom ''No references found.''')
|
||||
else
|
||||
if get(l:options, 'open_in') is# 'quickfix'
|
||||
call setqflist([], 'r')
|
||||
call setqflist(l:item_list, 'a')
|
||||
call ale#util#Execute('cc 1')
|
||||
else
|
||||
call ale#preview#ShowSelection(l:item_list, l:options)
|
||||
if type(l:result) is v:t_list
|
||||
for l:response_item in l:result
|
||||
call add(l:item_list,
|
||||
\ ale#references#FormatLSPResponseItem(l:response_item, l:options)
|
||||
\)
|
||||
endfor
|
||||
endif
|
||||
|
||||
if empty(l:item_list)
|
||||
call ale#util#Execute('echom ''No references found.''')
|
||||
else
|
||||
if get(l:options, 'use_fzf') == 1
|
||||
if !exists('*fzf#run')
|
||||
throw 'fzf#run function not found. You also need Vim plugin from the main fzf repository (i.e. junegunn/fzf *and* junegunn/fzf.vim)'
|
||||
endif
|
||||
|
||||
call ale#fzf#ShowReferences(l:item_list, l:options)
|
||||
elseif get(l:options, 'open_in') is# 'quickfix'
|
||||
call setqflist([], 'r')
|
||||
call setqflist(l:item_list, 'a')
|
||||
call ale#util#Execute('cc 1')
|
||||
else
|
||||
call ale#preview#ShowSelection(l:item_list, l:options)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
@@ -165,6 +183,7 @@ function! s:OnReady(line, column, options, linter, lsp_details) abort
|
||||
\ 'use_relative_paths': has_key(a:options, 'use_relative_paths') ? a:options.use_relative_paths : 0,
|
||||
\ 'open_in': get(a:options, 'open_in', 'current-buffer'),
|
||||
\ 'show_contents': a:options.show_contents,
|
||||
\ 'use_fzf': get(a:options, 'use_fzf', g:ale_references_use_fzf),
|
||||
\}
|
||||
endfunction
|
||||
|
||||
@@ -185,6 +204,8 @@ function! ale#references#Find(...) abort
|
||||
let l:options.open_in = 'quickfix'
|
||||
elseif l:option is? '-contents'
|
||||
let l:options.show_contents = 1
|
||||
elseif l:option is? '-fzf'
|
||||
let l:options.use_fzf = 1
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
Reference in New Issue
Block a user