All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
1 let s:use_vim_textprops = lsp#utils#_has_textprops() && !has('nvim')
4 function! lsp#internal#document_highlight#_enable() abort
5 " don't event bother registering if the feature is disabled
6 if !g:lsp_document_highlight_enabled | return | endif
8 " Highlight group for references
9 if !hlexists('lspReference')
10 highlight link lspReference CursorColumn
14 " - update highlight references when CusorMoved or CursorHold
15 " - clear highlights when InsertEnter or BufLeave
16 " - debounce highlight requests
17 " - automatically switch to latest highlight request via switchMap()
18 " - cancel highlight request via takeUntil() when BufLeave
19 let s:Dispose = lsp#callbag#pipe(
21 \ lsp#callbag#fromEvent(['CursorMoved', 'CursorHold']),
23 \ lsp#callbag#fromEvent(['InsertEnter', 'BufLeave']),
24 \ lsp#callbag#tap({_ -> s:clear_highlights() }),
27 \ lsp#callbag#filter({_ -> g:lsp_document_highlight_enabled }),
28 \ lsp#callbag#debounceTime(g:lsp_document_highlight_delay),
29 \ lsp#callbag#map({_->{'bufnr': bufnr('%'), 'curpos': getcurpos()[0:2], 'changedtick': b:changedtick }}),
30 \ lsp#callbag#distinctUntilChanged({a,b -> a['bufnr'] == b['bufnr'] && a['curpos'] == b['curpos'] && a['changedtick'] == b['changedtick']}),
31 \ lsp#callbag#filter({_->mode() is# 'n' && getbufvar(bufnr('%'), '&buftype') !=# 'terminal' }),
32 \ lsp#callbag#switchMap({_->
34 \ s:send_highlight_request(),
35 \ lsp#callbag#materialize(),
36 \ lsp#callbag#filter({x->lsp#callbag#isNextNotification(x)}),
37 \ lsp#callbag#map({x->x['value']}),
38 \ lsp#callbag#takeUntil(
39 \ lsp#callbag#fromEvent('BufLeave')
43 \ lsp#callbag#filter({_->mode() is# 'n'}),
44 \ lsp#callbag#subscribe({x->s:set_highlights(x)}),
48 function! lsp#internal#document_highlight#_disable() abort
49 if exists('s:Dispose')
55 function! s:send_highlight_request() abort
56 let l:capability = 'lsp#capabilities#has_document_highlight_provider(v:val)'
57 let l:servers = filter(lsp#get_allowed_servers(), l:capability)
60 return lsp#callbag#empty()
63 return lsp#request(l:servers[0], {
64 \ 'method': 'textDocument/documentHighlight',
66 \ 'textDocument': lsp#get_text_document_identifier(),
67 \ 'position': lsp#get_position(),
72 function! s:set_highlights(data) abort
73 let l:bufnr = bufnr('%')
75 call s:clear_highlights()
77 if mode() !=# 'n' | return | endif
79 if lsp#client#is_error(a:data['response']) | return | endif
81 " Get references from the response
82 let l:reference_list = a:data['response']['result']
83 if empty(l:reference_list)
87 " Convert references to vim positions
88 let l:position_list = []
89 for l:reference in l:reference_list
90 call extend(l:position_list, lsp#utils#range#lsp_to_vim(l:bufnr, l:reference['range']))
93 call sort(l:position_list, function('s:compare_positions'))
95 " Ignore response if the cursor is not over a reference anymore
96 if s:in_reference(l:position_list) == -1 | return | endif
99 if s:use_vim_textprops
100 let b:lsp_reference_positions = l:position_list
101 let b:lsp_reference_matches = []
103 let w:lsp_reference_positions = l:position_list
104 let w:lsp_reference_matches = []
107 " Apply highlights to the buffer
108 call s:init_reference_highlight(l:bufnr)
109 if s:use_vim_textprops
110 for l:position in l:position_list
112 " TODO: need to check for valid range before calling prop_add
113 " See https://github.com/prabirshrestha/vim-lsp/pull/721
114 silent! call prop_add(l:position[0], l:position[1], {
117 \ 'length': l:position[2],
118 \ 'type': 'vim-lsp-reference-highlight'})
119 call add(b:lsp_reference_matches, l:position[0])
121 call lsp#log('document_highlight', 'set_highlights', v:exception, v:throwpoint)
125 for l:position in l:position_list
126 let l:match = matchaddpos('lspReference', [l:position], -5)
127 call add(w:lsp_reference_matches, l:match)
132 function! s:clear_highlights() abort
133 if s:use_vim_textprops
134 if exists('b:lsp_reference_matches')
135 let l:bufnr = bufnr('%')
136 for l:line in b:lsp_reference_matches
137 silent! call prop_remove(
140 \ 'all': v:true}, l:line)
142 unlet b:lsp_reference_matches
143 unlet b:lsp_reference_positions
146 if exists('w:lsp_reference_matches')
147 for l:match in w:lsp_reference_matches
148 silent! call matchdelete(l:match)
150 unlet w:lsp_reference_matches
151 unlet w:lsp_reference_positions
156 " Compare two positions
157 function! s:compare_positions(p1, p2) abort
158 let l:line_1 = a:p1[0]
159 let l:line_2 = a:p2[0]
160 if l:line_1 != l:line_2
161 return l:line_1 > l:line_2 ? 1 : -1
163 let l:col_1 = a:p1[1]
164 let l:col_2 = a:p2[1]
165 return l:col_1 - l:col_2
168 " If the cursor is over a reference, return its index in
169 " the array. Otherwise, return -1.
170 function! s:in_reference(reference_list) abort
171 let l:line = line('.')
172 let l:column = col('.')
174 for l:position in a:reference_list
175 if l:line == l:position[0] &&
176 \ l:column >= l:position[1] &&
177 \ l:column < l:position[1] + l:position[2]
185 function! s:init_reference_highlight(buf) abort
186 if s:use_vim_textprops
189 \ 'highlight': 'lspReference',
191 \ 'priority': lsp#internal#textprop#priority('document_highlight')
193 if prop_type_get('vim-lsp-reference-highlight', { 'bufnr': a:buf }) == {}
194 call prop_type_add('vim-lsp-reference-highlight', l:props)
199 " Cyclically move between references by `offset` occurrences.
200 function! lsp#internal#document_highlight#jump(offset) abort
201 if s:use_vim_textprops && !exists('b:lsp_reference_positions') ||
202 \ !s:use_vim_textprops && !exists('w:lsp_reference_positions')
204 echom 'References not available'
209 " Get index of reference under cursor
210 let l:index = s:use_vim_textprops ? s:in_reference(b:lsp_reference_positions) : s:in_reference(w:lsp_reference_positions)
215 let l:n = s:use_vim_textprops ? len(b:lsp_reference_positions) : len(w:lsp_reference_positions)
216 let l:index += a:offset
218 " Show a message when reaching TOP/BOTTOM of the file
221 echom 'search hit TOP, continuing at BOTTOM'
223 elseif l:index >= (s:use_vim_textprops ? len(b:lsp_reference_positions) : len(w:lsp_reference_positions))
225 echom 'search hit BOTTOM, continuing at TOP'
230 if l:index < 0 || l:index >= (s:use_vim_textprops ? len(b:lsp_reference_positions) : len(w:lsp_reference_positions))
231 let l:index = (l:index % l:n + l:n) % l:n
235 let l:target = (s:use_vim_textprops ? b:lsp_reference_positions : w:lsp_reference_positions)[l:index][0:1]
237 call cursor(l:target[0], l:target[1])