Skip to content

Commit 765ef84

Browse files
committed
ALEFindReferences -fzf: improvements
- add support for opening in splits, tabs and for adding matches quickfix - add support for -relative - add fzf preview `--highlight-line` option - cleanup fzf options
1 parent 34e43a7 commit 765ef84

File tree

1 file changed

+90
-68
lines changed

1 file changed

+90
-68
lines changed

autoload/ale/references.vim

Lines changed: 90 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
let g:ale_default_navigation = get(g:, 'ale_default_navigation', 'buffer')
22
let g:ale_references_show_contents = get(g:, 'ale_references_show_contents', 1)
3+
let g:ale_references_use_fzf = get(g:, 'ale_references_use_fzf', 0)
34

45
let s:references_map = {}
56

@@ -82,25 +83,23 @@ function! ale#references#FormatLSPResponseItem(response_item, options) abort
8283
endtry
8384
endif
8485

86+
if get(a:options, 'use_fzf') == 1
87+
let l:filename = ale#util#ToResource(a:response_item.uri)
88+
let l:nline = a:response_item.range.start.line + 1
89+
let l:ncol = a:response_item.range.start.character + 1
90+
91+
" grep-style output (filename:line:col:text) so that fzf can properly
92+
" show matches and previews using ':' as delimiter
93+
return l:filename . ':' . l:nline . ":" . l:ncol . ":" . l:line_text
94+
endif
95+
8596
if get(a:options, 'open_in') is# 'quickfix'
8697
return {
8798
\ 'filename': l:filename,
8899
\ 'lnum': a:response_item.range.start.line + 1,
89100
\ 'col': a:response_item.range.start.character + 1,
90101
\ 'text': l:line_text,
91102
\}
92-
elseif get(a:options, 'open_in') is# 'fzf'
93-
" grep-style output (filename:line:col:text) so that fzf can properly
94-
" show matches and previews.
95-
" See note below in ShowInFzf about returning json encoded values here
96-
" (and commented return below)
97-
return l:filename . ':' . (a:response_item.range.start.line + 1) . ":" . (a:response_item.range.start.character + 1) . ":" . l:line_text
98-
" return json_encode({
99-
" \ 'filename': l:filename,
100-
" \ 'lnum': a:response_item.range.start.line + 1,
101-
" \ 'col': a:response_item.range.start.character + 1,
102-
" \ 'text': l:line_text,
103-
" \})
104103
else
105104
return {
106105
\ 'filename': l:filename,
@@ -111,57 +110,88 @@ function! ale#references#FormatLSPResponseItem(response_item, options) abort
111110
endif
112111
endfunction
113112

114-
function! ale#references#ShowInFzf(item_list) abort
115-
let l:name = "LSP References"
116-
let l:capname = "References"
113+
function! ale#references#diocane() abort
114+
echomsg "breakhere"
115+
endfunction
116+
117+
function! ale#references#ShowInFzf(item_list, options) abort
118+
let name = "LSP References"
119+
let capname = "References"
120+
let items = copy(a:item_list)
121+
let cwd = getcwd() " no-custom-checks
122+
let sep = has('win32') ? '\' : '/'
123+
124+
function! s:relative_paths(line) closure
125+
return substitute(a:line, '^' . cwd . sep, '', '')
126+
endfunction
127+
128+
if get(a:options, 'use_relative_paths')
129+
let items = map(filter(items, 'len(v:val)'), 's:relative_paths(v:val)')
130+
endif
117131

118-
" start with an empty query to let user filter
119-
let l:start_query = ''
120-
let l:opts = {
121-
\ 'source': a:item_list,
122-
\ 'options': ['--ansi', '--prompt', l:name.'> ', '--query', l:start_query,
123-
\ '--disabled',
132+
let start_query = ''
133+
let fzf_options = {
134+
\ 'source': items,
135+
\ 'options': ['--prompt', name.'> ', '--query', start_query,
124136
\ '--multi', '--bind', 'alt-a:select-all,alt-d:deselect-all',
125137
\ '--delimiter', ':', '--preview-window', '+{2}/2']
126138
\}
127139

128-
function! s:action(key, file_info, ...)
129-
" FIXME: temporarily only using edit, need to add more methods
130-
silent keepjumps keepalt execute "edit " . fnameescape(a:file_info['filename'])
131-
execute a:file_info.lnum
132-
call cursor(0, a:file_info.col)
133-
endfunction
140+
call add(fzf_options['options'], '--highlight-line') " this only works for more recent fzf versions (TODO: handle version check?)
134141

142+
" wrap with #with_preview and #fzfwrap before adding the sinklist,
143+
" otherwise --expect options are not added
144+
let opts_with_preview = fzf#vim#with_preview(fzf_options)
145+
let bang = 0 " TODO: handle bang
146+
let wrapped = fzf#wrap(name, opts_with_preview, bang)
135147

136-
function! s:references_to_qf(line)
137-
" mimics ag_to_qf in fzf.vim
138-
" NOTE: it's kinda annoying that we have to format each match as
139-
" filename:linenum:column:text, but that's how we can easily display
140-
" matches (and previews) in fzf, and then we have to parse it again here.
141-
"
142-
" TODO: figure out a way pass json to fzf and use that for matches.
143-
" We could get rid of this code and just have FormatLSPResponseItem
144-
" return json_encode(<match info>) and then here:
145-
" let l:dict = json_decode(a:line)
146-
"
147-
let parts = matchlist(a:line, '\(.\{-}\)\s*:\s*\(\d\+\)\%(\s*:\s*\(\d\+\)\)\?\%(\s*:\(.*\)\)\?')
148-
let file = &acd ? fnamemodify(parts[1], ':p') : parts[1]
149-
150-
return {'filename': file, 'lnum': parts[2], 'col': parts[3], 'text': parts[4]}
151-
endfunction
148+
call remove(wrapped, 'sink*') " remove the default sinklist to add in our custom sinklist
152149

153-
function! opts.sinklist(lines) closure
154-
let l:references = map(filter(a:lines, 'len(v:val)'), 's:references_to_qf(v:val)')
150+
function! wrapped.sinklist(lines) closure
151+
if len(a:lines) <2
152+
return
153+
endif
154+
let cmd = a:lines[0]
155+
function! s:references_to_qf(line) closure
156+
call ale#references#diocane()
157+
" mimics ag_to_qf in fzf.vim
158+
let parts = matchlist(a:line, '\(.\{-}\)\s*:\s*\(\d\+\)\%(\s*:\s*\(\d\+\)\)\?\%(\s*:\(.*\)\)\?')
159+
let filename = &acd ? fnamemodify(parts[1], ':p') : parts[1]
160+
return {'filename': filename, 'lnum': parts[2], 'col': parts[3], 'text': parts[4]}
161+
endfunction
162+
163+
let references = map(filter(a:lines[1:], 'len(v:val)'), 's:references_to_qf(v:val)')
164+
165+
if empty(references)
166+
return
167+
endif
155168

156-
for ref in l:references
157-
call s:action("dummykey", l:ref)
158-
endfor
159-
endfunction
169+
if get(a:options, 'open_in') is# 'quickfix'
170+
call setqflist([], 'r')
171+
call setqflist(references, 'a')
172+
echomsg 'a:lines[1]=' . string(a:lines[1:])
173+
call ale#util#Execute('cc 1')
174+
endif
160175

161-
let l:opts_with_preview = fzf#vim#with_preview(l:opts)
162-
let l:fullscreen = 1 " FIXME: decide what to do with this
176+
function! s:action(key, file)
177+
" copied from fzf.vim
178+
let default_action = {
179+
\ 'ctrl-t': 'tab split',
180+
\ 'ctrl-x': 'split',
181+
\ 'ctrl-v': 'vsplit' }
182+
183+
let fzf_actions = get(g:, 'fzf_action', default_action)
184+
let Cmd = get(fzf_actions, a:key, 'edit')
185+
186+
let cursor_cmd = escape("call cursor(" . a:file['lnum'] . "," . a:file['col'] . ")", ' ')
187+
let fullcmd = Cmd . " +" . cursor_cmd . " " . fnameescape(a:file['filename'])
188+
silent keepjumps keepalt execute fullcmd
189+
endfunction
190+
191+
return map(references, 's:action(cmd, v:val)')
192+
endfunction
163193

164-
call fzf#run(fzf#wrap(l:name, l:opts_with_preview, l:fullscreen))
194+
call fzf#run(wrapped)
165195
endfunction
166196

167197
function! ale#references#HandleLSPResponse(conn_id, response) abort
@@ -186,25 +216,16 @@ function! ale#references#HandleLSPResponse(conn_id, response) abort
186216
if empty(l:item_list)
187217
call ale#util#Execute('echom ''No references found.''')
188218
else
189-
if get(l:options, 'open_in') is# 'quickfix'
190-
call setqflist([], 'r')
191-
call setqflist(l:item_list, 'a')
192-
call ale#util#Execute('cc 1')
193-
elseif get(l:options, 'open_in') is# 'fzf'
194-
close " close the buffer that's been opened, we're not going to use it
195-
" when showing results through fzf
196-
" TODO: use 'use_fzf' instead of 'open_in', then pass 'open_in' to
197-
" ShowInFzf in order to use as default open method
198-
219+
if get(l:options, 'use_fzf') == 1
199220
if !exists('*fzf#run')
200221
throw "fzf#run function not found. You also need Vim plugin from the main fzf repository (i.e. junegunn/fzf *and* junegunn/fzf.vim)"
201222
endif
202223

203-
" if !exists('*fzf#vim#references')
204-
" throw "fzf#vim#references function not found. You need to upgrade to the latest fzf.vim version"
205-
" endif
206-
207-
call ale#references#ShowInFzf(l:item_list)
224+
call ale#references#ShowInFzf(l:item_list, l:options)
225+
elseif get(l:options, 'open_in') is# 'quickfix'
226+
call setqflist([], 'r')
227+
call setqflist(l:item_list, 'a')
228+
call ale#util#Execute('cc 1')
208229
else
209230
call ale#preview#ShowSelection(l:item_list, l:options)
210231
endif
@@ -246,6 +267,7 @@ function! s:OnReady(line, column, options, linter, lsp_details) abort
246267
\ 'use_relative_paths': has_key(a:options, 'use_relative_paths') ? a:options.use_relative_paths : 0,
247268
\ 'open_in': get(a:options, 'open_in', 'current-buffer'),
248269
\ 'show_contents': a:options.show_contents,
270+
\ 'use_fzf': get(a:options, 'use_fzf', g:ale_references_use_fzf),
249271
\}
250272
endfunction
251273

@@ -265,7 +287,7 @@ function! ale#references#Find(...) abort
265287
elseif l:option is? '-quickfix'
266288
let l:options.open_in = 'quickfix'
267289
elseif l:option is? '-fzf'
268-
let l:options.open_in = 'fzf'
290+
let l:options.use_fzf = 1
269291
endif
270292
endfor
271293
endif

0 commit comments

Comments
 (0)