]> git.madduck.net Git - etc/vim.git/blob - .vim/bundle/vim-lsp/autoload/lsp/internal/document_highlight.vim

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

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.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Do not set EDITOR/VISUAL for shell
[etc/vim.git] / .vim / bundle / vim-lsp / autoload / lsp / internal / document_highlight.vim
1 let s:use_vim_textprops = lsp#utils#_has_textprops() && !has('nvim')
2 let s:prop_id = 11
3
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
7
8     " Highlight group for references
9     if !hlexists('lspReference')
10         highlight link lspReference CursorColumn
11     endif
12
13     " Note:
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(
20         \ lsp#callbag#merge(
21         \   lsp#callbag#fromEvent(['CursorMoved', 'CursorHold']),
22         \   lsp#callbag#pipe(
23         \       lsp#callbag#fromEvent(['InsertEnter', 'BufLeave']),
24         \       lsp#callbag#tap({_ -> s:clear_highlights() }),
25         \   )
26         \ ),
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({_->
33         \   lsp#callbag#pipe(
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')
40         \       )
41         \   )
42         \ }),
43         \ lsp#callbag#filter({_->mode() is# 'n'}),
44         \ lsp#callbag#subscribe({x->s:set_highlights(x)}),
45         \)
46 endfunction
47
48 function! lsp#internal#document_highlight#_disable() abort
49     if exists('s:Dispose')
50         call s:Dispose()
51         unlet s:Dispose
52     endif
53 endfunction
54
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)
58
59     if empty(l:servers)
60         return lsp#callbag#empty()
61     endif
62
63     return lsp#request(l:servers[0], {
64         \ 'method': 'textDocument/documentHighlight',
65         \ 'params': {
66         \   'textDocument': lsp#get_text_document_identifier(),
67         \   'position': lsp#get_position(),
68         \  },
69         \ })
70 endfunction
71
72 function! s:set_highlights(data) abort
73     let l:bufnr = bufnr('%')
74
75     call s:clear_highlights()
76
77     if mode() !=# 'n' | return | endif
78
79     if lsp#client#is_error(a:data['response']) | return | endif
80
81     " Get references from the response
82     let l:reference_list = a:data['response']['result']
83     if empty(l:reference_list)
84         return
85     endif
86
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']))
91     endfor
92
93     call sort(l:position_list, function('s:compare_positions'))
94
95     " Ignore response if the cursor is not over a reference anymore
96     if s:in_reference(l:position_list) == -1 | return | endif
97
98     " Store references
99     if s:use_vim_textprops
100         let b:lsp_reference_positions = l:position_list
101         let b:lsp_reference_matches = []
102     else
103         let w:lsp_reference_positions = l:position_list
104         let w:lsp_reference_matches = []
105     endif
106
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
111             try
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], {
115                     \ 'id': s:prop_id,
116                     \ 'bufnr': l:bufnr,
117                     \ 'length': l:position[2],
118                     \ 'type': 'vim-lsp-reference-highlight'})
119                 call add(b:lsp_reference_matches, l:position[0])
120             catch
121                 call lsp#log('document_highlight', 'set_highlights', v:exception, v:throwpoint)
122             endtry
123         endfor
124     else
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)
128         endfor
129     endif
130 endfunction
131
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(
138                 \   {'id': s:prop_id,
139                 \    'bufnr': l:bufnr,
140                 \    'all': v:true}, l:line)
141             endfor
142             unlet b:lsp_reference_matches
143             unlet b:lsp_reference_positions
144         endif
145     else
146         if exists('w:lsp_reference_matches')
147             for l:match in w:lsp_reference_matches
148                 silent! call matchdelete(l:match)
149             endfor
150             unlet w:lsp_reference_matches
151             unlet w:lsp_reference_positions
152         endif
153     endif
154 endfunction
155
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
162     endif
163     let l:col_1 = a:p1[1]
164     let l:col_2 = a:p2[1]
165     return l:col_1 - l:col_2
166 endfunction
167
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('.')
173     let l:index = 0
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]
178             return l:index
179         endif
180         let l:index += 1
181     endfor
182     return -1
183 endfunction
184
185 function! s:init_reference_highlight(buf) abort
186     if s:use_vim_textprops
187         let l:props = {
188             \   'bufnr': a:buf,
189             \   'highlight': 'lspReference',
190             \   'combine': v:true,
191             \   'priority': lsp#internal#textprop#priority('document_highlight')
192             \ }
193         if prop_type_get('vim-lsp-reference-highlight', { 'bufnr': a:buf }) == {}
194             call prop_type_add('vim-lsp-reference-highlight', l:props)
195         endif
196     endif
197 endfunction
198
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')
203         echohl WarningMsg
204         echom 'References not available'
205         echohl None
206         return
207     endif
208
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)
211     if l:index < 0
212         return
213     endif
214
215     let l:n = s:use_vim_textprops ? len(b:lsp_reference_positions) : len(w:lsp_reference_positions)
216     let l:index += a:offset
217
218     " Show a message when reaching TOP/BOTTOM of the file
219     if l:index < 0
220         echohl WarningMsg
221         echom 'search hit TOP, continuing at BOTTOM'
222         echohl None
223     elseif l:index >= (s:use_vim_textprops ? len(b:lsp_reference_positions) : len(w:lsp_reference_positions))
224         echohl WarningMsg
225         echom 'search hit BOTTOM, continuing at TOP'
226         echohl None
227     endif
228
229     " Wrap index
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
232     endif
233
234     " Jump
235     let l:target = (s:use_vim_textprops ? b:lsp_reference_positions : w:lsp_reference_positions)[l:index][0:1]
236     normal! m`
237     call cursor(l:target[0], l:target[1])
238 endfunction