Skip to content

Commit eadb20a

Browse files
Cache semantic tokens (#635)
* Move token calculation * Cache tokens * Avoid overflow in hash function
1 parent 8f48b74 commit eadb20a

File tree

1 file changed

+38
-4
lines changed

1 file changed

+38
-4
lines changed

autoload/lsp/ui/vim/semantic.vim

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
let s:use_vim_textprops = has('textprop') && !has('nvim')
22
let s:use_nvim_highlight = exists('*nvim_buf_add_highlight') && has('nvim')
3+
let s:textprop_cache = 'vim-lsp-semantic-cache'
34

45
if s:use_nvim_highlight
56
let s:namespace_id = nvim_create_namespace('vim-lsp-semantic')
@@ -44,7 +45,7 @@ function! lsp#ui#vim#semantic#handle_semantic(server, data) abort
4445
for l:info in a:data['response']['params']['lines']
4546
let l:linenr = l:info['line']
4647
let l:tokens = has_key(l:info, 'tokens') ? l:info['tokens'] : ''
47-
call s:add_highlight(a:server, l:bufnr, l:linenr, s:tokens_to_hl_info(l:tokens))
48+
call s:add_highlight(a:server, l:bufnr, l:linenr, l:tokens)
4849
endfor
4950
endfunction
5051

@@ -62,28 +63,61 @@ function! s:init_highlight(server, buf) abort
6263

6364
silent! call prop_type_add(s:get_textprop_name(a:server, l:scope_idx), {'bufnr': a:buf, 'highlight': l:hl, 'combine': v:true})
6465
endfor
66+
67+
silent! call prop_type_add(s:textprop_cache, {'bufnr': a:buf})
6568
endif
6669

6770
call setbufvar(a:buf, 'lsp_did_semantic_setup', 1)
6871
endfunction
6972

70-
function! s:add_highlight(server, buf, line, highlights) abort
73+
function! s:hash(str) abort
74+
let l:hash = 1
75+
76+
for l:char in split(a:str, '\zs')
77+
let l:hash = (l:hash * 31 + char2nr(l:char)) % 2147483647
78+
endfor
79+
80+
return l:hash
81+
endfunction
82+
83+
function! s:add_highlight(server, buf, line, tokens) abort
84+
" Return quickly if the tokens for this line are already set correctly,
85+
" according to the cached tokens.
86+
" This only works for Vim at the moment, for Neovim, we need extended
87+
" marks.
88+
if s:use_vim_textprops
89+
let l:props = filter(prop_list(a:line + 1, {'bufnr': a:buf}), {idx, prop -> prop['type'] ==# s:textprop_cache})
90+
let l:hash = s:hash(a:tokens)
91+
92+
if !empty(l:props) && l:props[0]['id'] == l:hash
93+
" No changes for this line, so just return.
94+
return
95+
endif
96+
endif
97+
7198
let l:scopes = lsp#ui#vim#semantic#get_scopes(a:server)
99+
let l:highlights = s:tokens_to_hl_info(a:tokens)
72100

73101
if s:use_vim_textprops
74102
" Clear text properties from the previous run
75103
for l:scope_idx in range(len(l:scopes))
76104
call prop_remove({'bufnr': a:buf, 'type': s:get_textprop_name(a:server, l:scope_idx), 'all': v:true}, a:line + 1)
77105
endfor
78106

79-
for l:highlight in a:highlights
107+
" Clear cache from previous run
108+
call prop_remove({'bufnr': a:buf, 'type': s:textprop_cache, 'all': v:true}, a:line + 1)
109+
110+
" Add textprop for cache
111+
call prop_add(a:line + 1, 1, {'bufnr': a:buf, 'type': s:textprop_cache, 'id': l:hash})
112+
113+
for l:highlight in l:highlights
80114
call prop_add(a:line + 1, l:highlight['char'] + 1, { 'length': l:highlight['length'], 'bufnr': a:buf, 'type': s:get_textprop_name(a:server, l:highlight['scope'])})
81115
endfor
82116
elseif s:use_nvim_highlight
83117
" Clear text properties from the previous run
84118
call nvim_buf_clear_namespace(a:buf, s:namespace_id, a:line, a:line + 1)
85119

86-
for l:highlight in a:highlights
120+
for l:highlight in l:highlights
87121
call nvim_buf_add_highlight(a:buf, s:namespace_id, s:get_hl_name(a:server, l:scopes[l:highlight['scope']]), a:line, l:highlight['char'], l:highlight['char'] + l:highlight['length'])
88122
endfor
89123
endif

0 commit comments

Comments
 (0)