Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions autoload/ale/fzf.vim
Original file line number Diff line number Diff line change
@@ -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
69 changes: 45 additions & 24 deletions autoload/ale/references.vim
Original file line number Diff line number Diff line change
@@ -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 = {}

Expand Down Expand Up @@ -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,
Expand All @@ -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)

" 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 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 ! (has_key(a:response, 'id') && has_key(s:references_map, a:response.id))
return
endif

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)
let l:options = remove(s:references_map, a:response.id)

" 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 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
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down
16 changes: 16 additions & 0 deletions doc/ale.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2280,6 +2280,16 @@ g:ale_references_show_contents
If set to `true` or `1`, matches found by `:ALEFindReferences` will be
shown with a preview of the matching line.

*ale-options.references_use_fzf*
*g:ale_references_use_fzf*
references_use_fzf
g:ale_references_use_fzf
Type: |Boolean| or |Number|
Default: `false`

If set to `true` or `1`, matches found by `:ALEFindReferences` will be
always shown using |fzf-vim| (https://github.com/junegunn/fzf.vim).

*ale-options.rename_tsserver_find_in_comments*
*g:ale_rename_tsserver_find_in_comments*
rename_tsserver_find_in_comments
Expand Down Expand Up @@ -4085,13 +4095,19 @@ documented in additional help files.
`:ALEFindReferences -vsplit` - Open the location in a vertical split.
`:ALEFindReferences -quickfix` - Put the locations into quickfix list.
`:ALEFindReferences -contents` - Show line contents for matches.
`:ALEFindReferences -fzf` - Show matches/previews using |fzf-vim|.

The default method used for navigating to a new location can be changed
by modifying |g:ale_default_navigation|.

The default behaviour on whether to show line content for matches can
be changed by modifying |g:ale_references_show_contents|.

The default behaviour on whether to use `fzf` to show matches/file previews
can be changed by modifying |g:ale_references_use_fzf|. `-fzf` can be combined
with `-tab`, `-split`, `-vsplit`, `-quickfix` and `-relative`, while line
contents/file previews are always shown.

You can add `-relative` to the command to view results with relatives paths,
instead of absolute paths. This option has no effect if `-quickfix` is used.

Expand Down
19 changes: 10 additions & 9 deletions test/test_find_references.vader
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ Execute(Results should be shown for tsserver responses):
\ 'ignorethis': 'x',
\ 'open_in': 'tab',
\ 'use_relative_paths': 1,
\ 'use_fzf': 0,
\ }
\ }
\)
Expand Down Expand Up @@ -283,7 +284,7 @@ Execute(tsserver reference requests should be sent):
\ [0, 'ts@references', {'file': expand('%:p'), 'line': 2, 'offset': 5}]
\ ],
\ g:message_list
AssertEqual {'42': {'show_contents': 1, 'open_in': 'current-buffer', 'use_relative_paths': 0}}, ale#references#GetMap()
AssertEqual {'42': {'show_contents': 1, 'open_in': 'current-buffer', 'use_relative_paths': 0, 'use_fzf': 0, }}, ale#references#GetMap()

Execute('-relative' argument should enable 'use_relative_paths' in HandleTSServerResponse):
runtime ale_linters/typescript/tsserver.vim
Expand All @@ -293,7 +294,7 @@ Execute('-relative' argument should enable 'use_relative_paths' in HandleTSServe

call g:InitCallback()

AssertEqual {'42': {'show_contents': 1, 'open_in': 'current-buffer', 'use_relative_paths': 1}}, ale#references#GetMap()
AssertEqual {'42': {'show_contents': 1, 'open_in': 'current-buffer', 'use_relative_paths': 1, 'use_fzf': 0}}, ale#references#GetMap()

Execute(`-tab` should display results in tabs):
runtime ale_linters/typescript/tsserver.vim
Expand All @@ -303,7 +304,7 @@ Execute(`-tab` should display results in tabs):

call g:InitCallback()

AssertEqual {'42': {'show_contents': 1, 'open_in': 'tab', 'use_relative_paths': 0}}, ale#references#GetMap()
AssertEqual {'42': {'show_contents': 1, 'open_in': 'tab', 'use_relative_paths': 0, 'use_fzf': 0}}, ale#references#GetMap()

Execute(The default navigation type should be used):
runtime ale_linters/typescript/tsserver.vim
Expand All @@ -314,7 +315,7 @@ Execute(The default navigation type should be used):

call g:InitCallback()

AssertEqual {'42': {'show_contents': 1, 'open_in': 'tab', 'use_relative_paths': 0}}, ale#references#GetMap()
AssertEqual {'42': {'show_contents': 1, 'open_in': 'tab', 'use_relative_paths': 0, 'use_fzf': 0}}, ale#references#GetMap()

Execute(`-split` should display results in splits):
runtime ale_linters/typescript/tsserver.vim
Expand All @@ -324,7 +325,7 @@ Execute(`-split` should display results in splits):

call g:InitCallback()

AssertEqual {'42': {'show_contents': 1, 'open_in': 'split', 'use_relative_paths': 0}}, ale#references#GetMap()
AssertEqual {'42': {'show_contents': 1, 'open_in': 'split', 'use_relative_paths': 0, 'use_fzf': 0}}, ale#references#GetMap()

Execute(`-vsplit` should display results in vsplits):
runtime ale_linters/typescript/tsserver.vim
Expand All @@ -334,7 +335,7 @@ Execute(`-vsplit` should display results in vsplits):

call g:InitCallback()

AssertEqual {'42': {'show_contents': 1, 'open_in': 'vsplit', 'use_relative_paths': 0}}, ale#references#GetMap()
AssertEqual {'42': {'show_contents': 1, 'open_in': 'vsplit', 'use_relative_paths': 0, 'use_fzf': 0}}, ale#references#GetMap()

Execute(`-quickfix` should display results in quickfix):
runtime ale_linters/typescript/tsserver.vim
Expand All @@ -344,7 +345,7 @@ Execute(`-quickfix` should display results in quickfix):

call g:InitCallback()

AssertEqual {'42': {'show_contents': 1, 'open_in': 'quickfix', 'use_relative_paths': 0}}, ale#references#GetMap()
AssertEqual {'42': {'show_contents': 1, 'open_in': 'quickfix', 'use_relative_paths': 0, 'use_fzf': 0}}, ale#references#GetMap()

Given python(Some Python file):
foo
Expand Down Expand Up @@ -627,7 +628,7 @@ Execute(LSP reference requests should be sent):
\ ],
\ g:message_list

AssertEqual {'42': {'show_contents': 1, 'open_in': 'current-buffer', 'use_relative_paths': 0}}, ale#references#GetMap()
AssertEqual {'42': {'show_contents': 1, 'open_in': 'current-buffer', 'use_relative_paths': 0, 'use_fzf': 0}}, ale#references#GetMap()

Execute('-relative' argument should enable 'use_relative_paths' in HandleLSPResponse):
runtime ale_linters/python/pylsp.vim
Expand All @@ -638,4 +639,4 @@ Execute('-relative' argument should enable 'use_relative_paths' in HandleLSPResp

call g:InitCallback()

AssertEqual {'42': {'show_contents': 1, 'open_in': 'current-buffer', 'use_relative_paths': 1}}, ale#references#GetMap()
AssertEqual {'42': {'show_contents': 1, 'open_in': 'current-buffer', 'use_relative_paths': 1, 'use_fzf': 0}}, ale#references#GetMap()