I broke up with neovim....vim is my best friend now
This commit is contained in:
411
dot_vim/plugged/vim-lsp/autoload/lsp/internal/semantic.vim
Normal file
411
dot_vim/plugged/vim-lsp/autoload/lsp/internal/semantic.vim
Normal file
@@ -0,0 +1,411 @@
|
||||
let s:use_vim_textprops = lsp#utils#_has_textprops() && !has('nvim')
|
||||
let s:use_nvim_highlight = lsp#utils#_has_nvim_buf_highlight()
|
||||
let s:textprop_cache = 'vim-lsp-semantic-cache'
|
||||
|
||||
if s:use_nvim_highlight
|
||||
let s:namespace_id = nvim_create_namespace('vim-lsp-semantic')
|
||||
endif
|
||||
|
||||
" Global functions {{{1
|
||||
function! lsp#internal#semantic#is_enabled() abort
|
||||
return g:lsp_semantic_enabled && (s:use_vim_textprops || s:use_nvim_highlight) ? v:true : v:false
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#semantic#_enable() abort
|
||||
if !lsp#internal#semantic#is_enabled() | return | endif
|
||||
|
||||
augroup lsp#internal#semantic
|
||||
autocmd!
|
||||
au User lsp_buffer_enabled call s:on_lsp_buffer_enabled()
|
||||
augroup END
|
||||
|
||||
let l:events = [['User', 'lsp_buffer_enabled'], 'TextChanged', 'TextChangedI']
|
||||
if exists('##TextChangedP')
|
||||
call add(l:events, 'TextChangedP')
|
||||
endif
|
||||
let s:Dispose = lsp#callbag#pipe(
|
||||
\ lsp#callbag#fromEvent(l:events),
|
||||
\ lsp#callbag#filter({_->lsp#internal#semantic#is_enabled()}),
|
||||
\ lsp#callbag#debounceTime(g:lsp_semantic_delay),
|
||||
\ lsp#callbag#filter({_->index(['help', 'terminal', 'prompt', 'popup'], getbufvar(bufnr('%'), '&buftype')) ==# -1}),
|
||||
\ lsp#callbag#filter({_->!lsp#utils#is_large_window(win_getid())}),
|
||||
\ lsp#callbag#switchMap({_->
|
||||
\ lsp#callbag#pipe(
|
||||
\ s:semantic_request(),
|
||||
\ lsp#callbag#materialize(),
|
||||
\ lsp#callbag#filter({x->lsp#callbag#isNextNotification(x)}),
|
||||
\ lsp#callbag#map({x->x['value']})
|
||||
\ )
|
||||
\ }),
|
||||
\ lsp#callbag#subscribe({x->s:handle_semantic_request(x)})
|
||||
\ )
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#semantic#_disable() abort
|
||||
augroup lsp#internal#semantic
|
||||
autocmd!
|
||||
augroup END
|
||||
|
||||
if exists('s:Dispose')
|
||||
call s:Dispose()
|
||||
unlet s:Dispose
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#semantic#get_legend(server) abort
|
||||
if !lsp#capabilities#has_semantic_tokens(a:server)
|
||||
return {'tokenTypes': [], 'tokenModifiers': []}
|
||||
endif
|
||||
|
||||
let l:capabilities = lsp#get_server_capabilities(a:server)
|
||||
return l:capabilities['semanticTokensProvider']['legend']
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#semantic#get_token_types() abort
|
||||
let l:capability = 'lsp#capabilities#has_semantic_tokens(v:val)'
|
||||
let l:servers = filter(lsp#get_allowed_servers(), l:capability)
|
||||
|
||||
if empty(l:servers)
|
||||
return []
|
||||
endif
|
||||
|
||||
let l:legend = lsp#internal#semantic#get_legend(l:servers[0])
|
||||
let l:token_types = l:legend['tokenTypes']
|
||||
call map(l:token_types, {_, type -> toupper(type[0]) . type[1:]})
|
||||
return l:token_types
|
||||
endfunction
|
||||
|
||||
function! lsp#internal#semantic#get_token_modifiers() abort
|
||||
let l:capability = 'lsp#capabilities#has_semantic_tokens(v:val)'
|
||||
let l:servers = filter(lsp#get_allowed_servers(), l:capability)
|
||||
|
||||
if empty(l:servers)
|
||||
return []
|
||||
endif
|
||||
|
||||
let l:legend = lsp#internal#semantic#get_legend(l:servers[0])
|
||||
let l:token_modifiers = l:legend['tokenModifiers']
|
||||
call map(l:token_modifiers, {_, modifier -> toupper(modifier[0]) . modifier[1:]})
|
||||
return l:token_modifiers
|
||||
endfunction
|
||||
|
||||
function! s:on_lsp_buffer_enabled() abort
|
||||
augroup lsp#internal#semantic
|
||||
if !exists('#BufUnload#<buffer>')
|
||||
execute 'au BufUnload <buffer> call setbufvar(' . bufnr() . ', ''lsp_semantic_previous_result_id'', '''')'
|
||||
endif
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
function! s:supports_full_semantic_request(server) abort
|
||||
if !lsp#capabilities#has_semantic_tokens(a:server)
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let l:capabilities = lsp#get_server_capabilities(a:server)['semanticTokensProvider']
|
||||
if !has_key(l:capabilities, 'full')
|
||||
return v:false
|
||||
endif
|
||||
|
||||
if type(l:capabilities['full']) ==# v:t_dict
|
||||
return v:true
|
||||
endif
|
||||
|
||||
return l:capabilities['full']
|
||||
endfunction
|
||||
|
||||
function! s:supports_delta_semantic_request(server) abort
|
||||
if !lsp#capabilities#has_semantic_tokens(a:server)
|
||||
return v:false
|
||||
endif
|
||||
|
||||
let l:capabilities = lsp#get_server_capabilities(a:server)['semanticTokensProvider']
|
||||
if !has_key(l:capabilities, 'full')
|
||||
return v:false
|
||||
endif
|
||||
|
||||
if type(l:capabilities['full']) !=# v:t_dict
|
||||
return v:false
|
||||
endif
|
||||
|
||||
if !has_key(l:capabilities['full'], 'delta')
|
||||
return v:false
|
||||
endif
|
||||
|
||||
return l:capabilities['full']['delta']
|
||||
endfunction
|
||||
|
||||
function! s:get_server() abort
|
||||
let l:capability = 's:supports_delta_semantic_request(v:val)'
|
||||
let l:servers = filter(lsp#get_allowed_servers(), l:capability)
|
||||
if empty(l:servers)
|
||||
let l:capability = 's:supports_full_semantic_request(v:val)'
|
||||
let l:servers = filter(lsp#get_allowed_servers(), l:capability)
|
||||
endif
|
||||
if empty(l:servers)
|
||||
return ''
|
||||
endif
|
||||
return l:servers[0]
|
||||
endfunction
|
||||
|
||||
function! s:semantic_request() abort
|
||||
let l:server = s:get_server()
|
||||
if l:server ==# ''
|
||||
return lsp#callbag#empty()
|
||||
endif
|
||||
|
||||
if (s:supports_delta_semantic_request(l:server)
|
||||
\ && getbufvar(bufnr(), 'lsp_semantic_previous_result_id') !=# '')
|
||||
return s:delta_semantic_request(l:server)
|
||||
else
|
||||
return s:full_semantic_request(l:server)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:full_semantic_request(server) abort
|
||||
return lsp#request(a:server, {
|
||||
\ 'method': 'textDocument/semanticTokens/full',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier()
|
||||
\ }})
|
||||
endfunction
|
||||
|
||||
function! s:delta_semantic_request(server) abort
|
||||
return lsp#request(a:server, {
|
||||
\ 'method': 'textDocument/semanticTokens/full/delta',
|
||||
\ 'params': {
|
||||
\ 'textDocument': lsp#get_text_document_identifier(),
|
||||
\ 'previousResultId': getbufvar(bufname(), 'lsp_semantic_previous_result_id', 0)
|
||||
\ }})
|
||||
endfunction
|
||||
|
||||
" Highlight helper functions {{{1
|
||||
function! s:handle_semantic_request(data) abort
|
||||
if lsp#client#is_error(a:data['response'])
|
||||
call lsp#log('Skipping semantic highlight: response is invalid')
|
||||
return
|
||||
endif
|
||||
|
||||
let l:server = a:data['server_name']
|
||||
let l:uri = a:data['request']['params']['textDocument']['uri']
|
||||
let l:path = lsp#utils#uri_to_path(l:uri)
|
||||
let l:bufnr = bufnr(l:path)
|
||||
|
||||
" Skip if the buffer doesn't exist. This might happen when a buffer is
|
||||
" opened and quickly deleted.
|
||||
if !bufloaded(l:bufnr) | return | endif
|
||||
|
||||
if has_key(a:data['response']['result'], 'data')
|
||||
call s:handle_semantic_tokens_response(l:server, l:bufnr, a:data['response']['result'])
|
||||
elseif has_key(a:data['response']['result'], 'edits')
|
||||
call s:handle_semantic_tokens_delta_response(l:server, l:bufnr, a:data['response']['result'])
|
||||
else
|
||||
" Don't update previous result ID if we could not update local copy
|
||||
call lsp#log('SemanticHighlight: unsupported semanticTokens method')
|
||||
return
|
||||
endif
|
||||
|
||||
if has_key(a:data['response']['result'], 'resultId')
|
||||
call setbufvar(l:bufnr, 'lsp_semantic_previous_result_id', a:data['response']['result']['resultId'])
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:handle_semantic_tokens_response(server, buf, result) abort
|
||||
let l:highlights = {}
|
||||
let l:legend = lsp#internal#semantic#get_legend(a:server)
|
||||
for l:token in s:decode_tokens(a:result['data'])
|
||||
let [l:key, l:value] = s:add_highlight(a:server, l:legend, a:buf, l:token)
|
||||
let l:highlights[l:key] = get(l:highlights, l:key, []) + l:value
|
||||
endfor
|
||||
call s:apply_highlights(a:server, a:buf, l:highlights)
|
||||
|
||||
call setbufvar(a:buf, 'lsp_semantic_local_data', a:result['data'])
|
||||
endfunction
|
||||
|
||||
function! s:startpos_compare(edit1, edit2) abort
|
||||
return a:edit1[0] == a:edit2[0] ? 0 : a:edit1[0] > a:edit2[0] ? -1 : 1
|
||||
endfunction
|
||||
|
||||
function! s:handle_semantic_tokens_delta_response(server, buf, result) abort
|
||||
" The locations given in the edit are all referenced to the state before
|
||||
" any are applied and sorting is not required from the server,
|
||||
" therefore the edits must be sorted before applying.
|
||||
let l:edits = a:result['edits']
|
||||
call sort(l:edits, function('s:startpos_compare'))
|
||||
|
||||
let l:localdata = getbufvar(a:buf, 'lsp_semantic_local_data')
|
||||
for l:edit in l:edits
|
||||
let l:insertdata = get(l:edit, 'data', [])
|
||||
let l:localdata = l:localdata[:l:edit['start'] - 1]
|
||||
\ + l:insertdata
|
||||
\ + l:localdata[l:edit['start'] + l:edit['deleteCount']:]
|
||||
endfor
|
||||
call setbufvar(a:buf, 'lsp_semantic_local_data', l:localdata)
|
||||
|
||||
let l:highlights = {}
|
||||
let l:legend = lsp#internal#semantic#get_legend(a:server)
|
||||
for l:token in s:decode_tokens(l:localdata)
|
||||
let [l:key, l:value] = s:add_highlight(a:server, l:legend, a:buf, l:token)
|
||||
let l:highlights[l:key] = get(l:highlights, l:key, []) + l:value
|
||||
endfor
|
||||
call s:apply_highlights(a:server, a:buf, l:highlights)
|
||||
endfunction
|
||||
|
||||
function! s:decode_tokens(data) abort
|
||||
let l:tokens = []
|
||||
|
||||
let l:i = 0
|
||||
let l:line = 0
|
||||
let l:char = 0
|
||||
while l:i < len(a:data)
|
||||
let l:line = l:line + a:data[l:i]
|
||||
if a:data[l:i] > 0
|
||||
let l:char = 0
|
||||
endif
|
||||
let l:char = l:char + a:data[l:i + 1]
|
||||
|
||||
call add(l:tokens, {
|
||||
\ 'pos': {'line': l:line, 'character': l:char},
|
||||
\ 'length': a:data[l:i + 2],
|
||||
\ 'token_idx': a:data[l:i + 3],
|
||||
\ 'token_modifiers': a:data[l:i + 4]
|
||||
\ })
|
||||
|
||||
let l:i = l:i + 5
|
||||
endwhile
|
||||
|
||||
return l:tokens
|
||||
endfunction
|
||||
|
||||
function! s:clear_highlights(server, buf) abort
|
||||
if s:use_vim_textprops
|
||||
let l:BeginsWith = {str, prefix -> str[0:len(prefix) - 1] ==# prefix}
|
||||
let l:IsSemanticTextprop = {_, textprop -> l:BeginsWith(textprop, s:textprop_type_prefix)}
|
||||
let l:textprop_types = prop_type_list()
|
||||
call filter(l:textprop_types, l:IsSemanticTextprop)
|
||||
for l:textprop_type in l:textprop_types
|
||||
silent! call prop_remove({'type': l:textprop_type, 'bufnr': a:buf, 'all': v:true})
|
||||
endfor
|
||||
elseif s:use_nvim_highlight
|
||||
call nvim_buf_clear_namespace(a:buf, s:namespace_id, 0, line('$'))
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:add_highlight(server, legend, buf, token) abort
|
||||
let l:startpos = lsp#utils#position#lsp_to_vim(a:buf, a:token['pos'])
|
||||
let l:endpos = a:token['pos']
|
||||
let l:endpos['character'] = l:endpos['character'] + a:token['length']
|
||||
let l:endpos = lsp#utils#position#lsp_to_vim(a:buf, l:endpos)
|
||||
|
||||
if s:use_vim_textprops
|
||||
let l:textprop_name = s:get_textprop_type(a:server, a:legend, a:token['token_idx'], a:token['token_modifiers'])
|
||||
return [l:textprop_name, [[l:startpos[0], l:startpos[1], l:endpos[0], l:endpos[1]]]]
|
||||
elseif s:use_nvim_highlight
|
||||
let l:char = a:token['pos']['character']
|
||||
let l:hl_name = s:get_hl_group(a:server, a:legend, a:token['token_idx'], a:token['token_modifiers'])
|
||||
return [l:hl_name, [[l:startpos[0] - 1, l:startpos[1] - 1, l:endpos[1] - 1]]]
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:apply_highlights(server, buf, highlights) abort
|
||||
call s:clear_highlights(a:server, a:buf)
|
||||
|
||||
if s:use_vim_textprops
|
||||
for [l:type, l:prop_list] in items(a:highlights)
|
||||
call prop_add_list({'type': l:type, 'bufnr': a:buf}, l:prop_list)
|
||||
endfor
|
||||
elseif s:use_nvim_highlight
|
||||
call lsp#log(a:highlights)
|
||||
for [l:hl_name, l:instances] in items(a:highlights)
|
||||
for l:instance in l:instances
|
||||
let [l:line, l:startcol, l:endcol] = l:instance
|
||||
try
|
||||
call nvim_buf_add_highlight(a:buf, s:namespace_id, l:hl_name, l:line, l:startcol, l:endcol)
|
||||
catch
|
||||
call lsp#log('SemanticHighlight: error while adding ' . l:hl_name . ' highlight on line ' . l:line)
|
||||
endtry
|
||||
endfor
|
||||
endfor
|
||||
end
|
||||
endfunction
|
||||
|
||||
let s:hl_group_prefix = 'LspSemantic'
|
||||
|
||||
let s:default_highlight_groups = {
|
||||
\ s:hl_group_prefix . 'Type': 'Type',
|
||||
\ s:hl_group_prefix . 'Class': 'Type',
|
||||
\ s:hl_group_prefix . 'Enum': 'Type',
|
||||
\ s:hl_group_prefix . 'Interface': 'TypeDef',
|
||||
\ s:hl_group_prefix . 'Struct': 'Type',
|
||||
\ s:hl_group_prefix . 'TypeParameter': 'Type',
|
||||
\ s:hl_group_prefix . 'Parameter': 'Identifier',
|
||||
\ s:hl_group_prefix . 'Variable': 'Identifier',
|
||||
\ s:hl_group_prefix . 'Property': 'Identifier',
|
||||
\ s:hl_group_prefix . 'EnumMember': 'Constant',
|
||||
\ s:hl_group_prefix . 'Event': 'Identifier',
|
||||
\ s:hl_group_prefix . 'Function': 'Function',
|
||||
\ s:hl_group_prefix . 'Method': 'Function',
|
||||
\ s:hl_group_prefix . 'Macro': 'Macro',
|
||||
\ s:hl_group_prefix . 'Keyword': 'Keyword',
|
||||
\ s:hl_group_prefix . 'Modifier': 'Type',
|
||||
\ s:hl_group_prefix . 'Comment': 'Comment',
|
||||
\ s:hl_group_prefix . 'String': 'String',
|
||||
\ s:hl_group_prefix . 'Number': 'Number',
|
||||
\ s:hl_group_prefix . 'Regexp': 'String',
|
||||
\ s:hl_group_prefix . 'Operator': 'Operator',
|
||||
\ s:hl_group_prefix . 'Decorator': 'Macro'
|
||||
\ }
|
||||
|
||||
function! s:get_hl_group(server, legend, token_idx, token_modifiers) abort
|
||||
" get highlight group name
|
||||
let l:Capitalise = {str -> toupper(str[0]) . str[1:]}
|
||||
let l:token_name = l:Capitalise(a:legend['tokenTypes'][a:token_idx])
|
||||
let l:token_modifiers = []
|
||||
for l:modifier_idx in range(len(a:legend['tokenModifiers']))
|
||||
" float2nr(pow(2, a)) is 1 << a
|
||||
if and(a:token_modifiers, float2nr(pow(2, l:modifier_idx)))
|
||||
let l:modifier_name = a:legend['tokenModifiers'][l:modifier_idx]
|
||||
call add(l:token_modifiers, l:Capitalise(l:modifier_name))
|
||||
endif
|
||||
endfor
|
||||
call sort(l:token_modifiers)
|
||||
let l:hl_group = s:hl_group_prefix
|
||||
\ . reduce(l:token_modifiers, {acc, val -> acc . val}, '')
|
||||
\ . l:token_name
|
||||
|
||||
" create the highlight group if it does not already exist
|
||||
if !hlexists(l:hl_group)
|
||||
if has_key(s:default_highlight_groups, l:hl_group)
|
||||
exec 'highlight link' l:hl_group s:default_highlight_groups[l:hl_group]
|
||||
else
|
||||
if a:token_modifiers != 0
|
||||
let l:base_hl_group = s:get_hl_group(a:server, a:legend, a:token_idx, 0)
|
||||
exec 'highlight link' l:hl_group l:base_hl_group
|
||||
else
|
||||
exec 'highlight link' l:hl_group 'Normal'
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
return l:hl_group
|
||||
endfunction
|
||||
|
||||
let s:textprop_type_prefix = 'vim-lsp-semantic-'
|
||||
|
||||
function! s:get_textprop_type(server, legend, token_idx, token_modifiers) abort
|
||||
" get textprop type name
|
||||
let l:textprop_type = s:textprop_type_prefix . a:server . '-' . a:token_idx . '-' . a:token_modifiers
|
||||
|
||||
" create the textprop type if it does not already exist
|
||||
if prop_type_get(l:textprop_type) ==# {}
|
||||
let l:hl_group = s:get_hl_group(a:server, a:legend, a:token_idx, a:token_modifiers)
|
||||
silent! call prop_type_add(l:textprop_type, {
|
||||
\ 'highlight': l:hl_group,
|
||||
\ 'combine': v:true,
|
||||
\ 'priority': lsp#internal#textprop#priority('semantic')})
|
||||
endif
|
||||
|
||||
return l:textprop_type
|
||||
endfunction
|
||||
|
||||
" vim: fdm=marker
|
||||
Reference in New Issue
Block a user