X-Git-Url: https://git.madduck.net/etc/vim.git/blobdiff_plain/5a4872f466ebd76ddd532bdf2798554421c53df4..fe3919e725e156d751069662d11e38f7b4791de1:/.vim/bundle/vim-lsp/autoload/lsp/internal/document_highlight.vim diff --git a/.vim/bundle/vim-lsp/autoload/lsp/internal/document_highlight.vim b/.vim/bundle/vim-lsp/autoload/lsp/internal/document_highlight.vim new file mode 100644 index 00000000..a6022e03 --- /dev/null +++ b/.vim/bundle/vim-lsp/autoload/lsp/internal/document_highlight.vim @@ -0,0 +1,238 @@ +let s:use_vim_textprops = lsp#utils#_has_textprops() && !has('nvim') +let s:prop_id = 11 + +function! lsp#internal#document_highlight#_enable() abort + " don't event bother registering if the feature is disabled + if !g:lsp_document_highlight_enabled | return | endif + + " Highlight group for references + if !hlexists('lspReference') + highlight link lspReference CursorColumn + endif + + " Note: + " - update highlight references when CusorMoved or CursorHold + " - clear highlights when InsertEnter or BufLeave + " - debounce highlight requests + " - automatically switch to latest highlight request via switchMap() + " - cancel highlight request via takeUntil() when BufLeave + let s:Dispose = lsp#callbag#pipe( + \ lsp#callbag#merge( + \ lsp#callbag#fromEvent(['CursorMoved', 'CursorHold']), + \ lsp#callbag#pipe( + \ lsp#callbag#fromEvent(['InsertEnter', 'BufLeave']), + \ lsp#callbag#tap({_ -> s:clear_highlights() }), + \ ) + \ ), + \ lsp#callbag#filter({_ -> g:lsp_document_highlight_enabled }), + \ lsp#callbag#debounceTime(g:lsp_document_highlight_delay), + \ lsp#callbag#map({_->{'bufnr': bufnr('%'), 'curpos': getcurpos()[0:2], 'changedtick': b:changedtick }}), + \ lsp#callbag#distinctUntilChanged({a,b -> a['bufnr'] == b['bufnr'] && a['curpos'] == b['curpos'] && a['changedtick'] == b['changedtick']}), + \ lsp#callbag#filter({_->mode() is# 'n' && getbufvar(bufnr('%'), '&buftype') !=# 'terminal' }), + \ lsp#callbag#switchMap({_-> + \ lsp#callbag#pipe( + \ s:send_highlight_request(), + \ lsp#callbag#materialize(), + \ lsp#callbag#filter({x->lsp#callbag#isNextNotification(x)}), + \ lsp#callbag#map({x->x['value']}), + \ lsp#callbag#takeUntil( + \ lsp#callbag#fromEvent('BufLeave') + \ ) + \ ) + \ }), + \ lsp#callbag#filter({_->mode() is# 'n'}), + \ lsp#callbag#subscribe({x->s:set_highlights(x)}), + \) +endfunction + +function! lsp#internal#document_highlight#_disable() abort + if exists('s:Dispose') + call s:Dispose() + unlet s:Dispose + endif +endfunction + +function! s:send_highlight_request() abort + let l:capability = 'lsp#capabilities#has_document_highlight_provider(v:val)' + let l:servers = filter(lsp#get_allowed_servers(), l:capability) + + if empty(l:servers) + return lsp#callbag#empty() + endif + + return lsp#request(l:servers[0], { + \ 'method': 'textDocument/documentHighlight', + \ 'params': { + \ 'textDocument': lsp#get_text_document_identifier(), + \ 'position': lsp#get_position(), + \ }, + \ }) +endfunction + +function! s:set_highlights(data) abort + let l:bufnr = bufnr('%') + + call s:clear_highlights() + + if mode() !=# 'n' | return | endif + + if lsp#client#is_error(a:data['response']) | return | endif + + " Get references from the response + let l:reference_list = a:data['response']['result'] + if empty(l:reference_list) + return + endif + + " Convert references to vim positions + let l:position_list = [] + for l:reference in l:reference_list + call extend(l:position_list, lsp#utils#range#lsp_to_vim(l:bufnr, l:reference['range'])) + endfor + + call sort(l:position_list, function('s:compare_positions')) + + " Ignore response if the cursor is not over a reference anymore + if s:in_reference(l:position_list) == -1 | return | endif + + " Store references + if s:use_vim_textprops + let b:lsp_reference_positions = l:position_list + let b:lsp_reference_matches = [] + else + let w:lsp_reference_positions = l:position_list + let w:lsp_reference_matches = [] + endif + + " Apply highlights to the buffer + call s:init_reference_highlight(l:bufnr) + if s:use_vim_textprops + for l:position in l:position_list + try + " TODO: need to check for valid range before calling prop_add + " See https://github.com/prabirshrestha/vim-lsp/pull/721 + silent! call prop_add(l:position[0], l:position[1], { + \ 'id': s:prop_id, + \ 'bufnr': l:bufnr, + \ 'length': l:position[2], + \ 'type': 'vim-lsp-reference-highlight'}) + call add(b:lsp_reference_matches, l:position[0]) + catch + call lsp#log('document_highlight', 'set_highlights', v:exception, v:throwpoint) + endtry + endfor + else + for l:position in l:position_list + let l:match = matchaddpos('lspReference', [l:position], -5) + call add(w:lsp_reference_matches, l:match) + endfor + endif +endfunction + +function! s:clear_highlights() abort + if s:use_vim_textprops + if exists('b:lsp_reference_matches') + let l:bufnr = bufnr('%') + for l:line in b:lsp_reference_matches + silent! call prop_remove( + \ {'id': s:prop_id, + \ 'bufnr': l:bufnr, + \ 'all': v:true}, l:line) + endfor + unlet b:lsp_reference_matches + unlet b:lsp_reference_positions + endif + else + if exists('w:lsp_reference_matches') + for l:match in w:lsp_reference_matches + silent! call matchdelete(l:match) + endfor + unlet w:lsp_reference_matches + unlet w:lsp_reference_positions + endif + endif +endfunction + +" Compare two positions +function! s:compare_positions(p1, p2) abort + let l:line_1 = a:p1[0] + let l:line_2 = a:p2[0] + if l:line_1 != l:line_2 + return l:line_1 > l:line_2 ? 1 : -1 + endif + let l:col_1 = a:p1[1] + let l:col_2 = a:p2[1] + return l:col_1 - l:col_2 +endfunction + +" If the cursor is over a reference, return its index in +" the array. Otherwise, return -1. +function! s:in_reference(reference_list) abort + let l:line = line('.') + let l:column = col('.') + let l:index = 0 + for l:position in a:reference_list + if l:line == l:position[0] && + \ l:column >= l:position[1] && + \ l:column < l:position[1] + l:position[2] + return l:index + endif + let l:index += 1 + endfor + return -1 +endfunction + +function! s:init_reference_highlight(buf) abort + if s:use_vim_textprops + let l:props = { + \ 'bufnr': a:buf, + \ 'highlight': 'lspReference', + \ 'combine': v:true, + \ 'priority': lsp#internal#textprop#priority('document_highlight') + \ } + if prop_type_get('vim-lsp-reference-highlight', { 'bufnr': a:buf }) == {} + call prop_type_add('vim-lsp-reference-highlight', l:props) + endif + endif +endfunction + +" Cyclically move between references by `offset` occurrences. +function! lsp#internal#document_highlight#jump(offset) abort + if s:use_vim_textprops && !exists('b:lsp_reference_positions') || + \ !s:use_vim_textprops && !exists('w:lsp_reference_positions') + echohl WarningMsg + echom 'References not available' + echohl None + return + endif + + " Get index of reference under cursor + let l:index = s:use_vim_textprops ? s:in_reference(b:lsp_reference_positions) : s:in_reference(w:lsp_reference_positions) + if l:index < 0 + return + endif + + let l:n = s:use_vim_textprops ? len(b:lsp_reference_positions) : len(w:lsp_reference_positions) + let l:index += a:offset + + " Show a message when reaching TOP/BOTTOM of the file + if l:index < 0 + echohl WarningMsg + echom 'search hit TOP, continuing at BOTTOM' + echohl None + elseif l:index >= (s:use_vim_textprops ? len(b:lsp_reference_positions) : len(w:lsp_reference_positions)) + echohl WarningMsg + echom 'search hit BOTTOM, continuing at TOP' + echohl None + endif + + " Wrap index + if l:index < 0 || l:index >= (s:use_vim_textprops ? len(b:lsp_reference_positions) : len(w:lsp_reference_positions)) + let l:index = (l:index % l:n + l:n) % l:n + endif + + " Jump + let l:target = (s:use_vim_textprops ? b:lsp_reference_positions : w:lsp_reference_positions)[l:index][0:1] + normal! m` + call cursor(l:target[0], l:target[1]) +endfunction