Skip to content

Commit efd07d8

Browse files
Revamp diagnostics highlights (#995)
1 parent b4f710f commit efd07d8

File tree

13 files changed

+266
-340
lines changed

13 files changed

+266
-340
lines changed

README.md

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -144,19 +144,6 @@ preferred to turn them off and use other plugins instead (like
144144
let g:lsp_diagnostics_enabled = 0 " disable diagnostics support
145145
```
146146

147-
#### Highlights
148-
149-
Highlighting diagnostics requires either NeoVim 0.3+ or Vim with patch 8.1.0579.
150-
They are enabled by default when supported, but can be turned off respectively by
151-
152-
```viml
153-
let g:lsp_highlights_enabled = 0
154-
let g:lsp_textprop_enabled = 0
155-
```
156-
157-
Can be customized by setting or linking `LspErrorHighlight`, `LspWarningHighlight`,
158-
`LspInformationHighlight` and `LspHintHighlight` highlight groups.
159-
160147
### Highlight references
161148

162149
Highlight references to the symbol under the cursor (enabled by default).

autoload/lsp.vim

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,6 @@ function! lsp#enable() abort
5555
let s:already_setup = 1
5656
endif
5757
let s:enabled = 1
58-
if g:lsp_diagnostics_enabled
59-
if g:lsp_highlights_enabled | call lsp#ui#vim#highlights#enable() | endif
60-
if g:lsp_textprop_enabled | call lsp#ui#vim#diagnostics#textprop#enable() | endif
61-
endif
6258
if g:lsp_signature_help_enabled
6359
call lsp#ui#vim#signature_help#setup()
6460
endif
@@ -74,8 +70,6 @@ function! lsp#disable() abort
7470
if !s:enabled
7571
return
7672
endif
77-
call lsp#ui#vim#highlights#disable()
78-
call lsp#ui#vim#diagnostics#textprop#disable()
7973
call lsp#ui#vim#signature_help#_disable()
8074
call lsp#ui#vim#completion#_disable()
8175
call lsp#internal#document_highlight#_disable()
@@ -237,7 +231,6 @@ function! s:on_text_document_did_open(...) abort
237231
" Some language server notify diagnostics to the buffer that has not been loaded yet.
238232
" This diagnostics was stored `autoload/lsp/ui/vim/diagnostics.vim` but not highlighted.
239233
" So we should refresh highlights when buffer opened.
240-
call lsp#ui#vim#diagnostics#force_refresh(l:buf)
241234
call lsp#internal#diagnostics#state#_force_notify_buffer(l:buf)
242235

243236
for l:server_name in lsp#get_allowed_servers(l:buf)

autoload/lsp/internal/diagnostics.vim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ function! lsp#internal#diagnostics#_enable() abort
44

55
call lsp#internal#diagnostics#state#_enable() " Needs to be the first one to register
66
call lsp#internal#diagnostics#echo#_enable()
7+
call lsp#internal#diagnostics#highlights#_enable()
78
call lsp#internal#diagnostics#float#_enable()
89
call lsp#internal#diagnostics#signs#_enable()
910
call lsp#internal#diagnostics#virtual_text#_enable()
@@ -12,6 +13,7 @@ endfunction
1213
function! lsp#internal#diagnostics#_disable() abort
1314
call lsp#internal#diagnostics#echo#_disable()
1415
call lsp#internal#diagnostics#float#_disable()
16+
call lsp#internal#diagnostics#highlights#_disable()
1517
call lsp#internal#diagnostics#virtual_text#_disable()
1618
call lsp#internal#diagnostics#signs#_disable()
1719
call lsp#internal#diagnostics#state#_disable() " Needs to be the last one to unregister

autoload/lsp/internal/diagnostics/document_diagnostics_command.vim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
" }
44
function! lsp#internal#diagnostics#document_diagnostics_command#do(options) abort
55
if !g:lsp_diagnostics_enabled
6-
call lsp#utils#error(':LspDocumentDiagnostics', 'g:lsp_diagnostics_enabled must be enabled')
6+
call lsp#utils#error(':LspDocumentDiagnostics g:lsp_diagnostics_enabled must be enabled')
77
return
88
endif
99

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
" internal state for whether it is enabled or not to avoid multiple subscriptions
2+
let s:enabled = 0
3+
let s:namespace_id = '' " will be set when enabled
4+
5+
let s:severity_sign_names_mapping = {
6+
\ 1: 'LspError',
7+
\ 2: 'LspWarning',
8+
\ 3: 'LspInformation',
9+
\ 4: 'LspHint',
10+
\ }
11+
12+
if !hlexists('LspErrorHighlight')
13+
highlight link LspErrorHighlight Error
14+
endif
15+
16+
if !hlexists('LspWarningHighlight')
17+
highlight link LspWarningHighlight Todo
18+
endif
19+
20+
if !hlexists('LspInformationHighlight')
21+
highlight link LspInformationHighlight Normal
22+
endif
23+
24+
if !hlexists('LspHintHighlight')
25+
highlight link LspHintHighlight Normal
26+
endif
27+
28+
function! lsp#internal#diagnostics#highlights#_enable() abort
29+
" don't even bother registering if the feature is disabled
30+
if !lsp#utils#_has_highlights() | return | endif
31+
if !g:lsp_diagnostics_highlights_enabled | return | endif
32+
33+
if s:enabled | return | endif
34+
let s:enabled = 1
35+
36+
if empty(s:namespace_id)
37+
if has('nvim')
38+
let s:namespace_id = nvim_create_namespace('vim_lsp_diagnostics_highlights')
39+
else
40+
let s:namespace_id = 'vim_lsp_diagnostics_highlights'
41+
for l:severity in keys(s:severity_sign_names_mapping)
42+
let l:hl_group = s:severity_sign_names_mapping[l:severity] . 'Highlight'
43+
call prop_type_add(s:get_prop_type_name(l:severity),
44+
\ {'highlight': l:hl_group, 'combine': v:true })
45+
endfor
46+
endif
47+
endif
48+
49+
let s:Dispose = lsp#callbag#pipe(
50+
\ lsp#callbag#merge(
51+
\ lsp#callbag#pipe(
52+
\ lsp#stream(),
53+
\ lsp#callbag#filter({x->has_key(x, 'server') && has_key(x, 'response')
54+
\ && has_key(x['response'], 'method') && x['response']['method'] ==# '$/vimlsp/lsp_diagnostics_updated'
55+
\ && !lsp#client#is_error(x['response'])}),
56+
\ lsp#callbag#map({x->x['response']['params']}),
57+
\ ),
58+
\ lsp#callbag#pipe(
59+
\ lsp#callbag#fromEvent(['InsertEnter', 'InsertLeave']),
60+
\ lsp#callbag#filter({_->!g:lsp_diagnostics_highlights_insert_mode_enabled}),
61+
\ lsp#callbag#map({_->{ 'uri': lsp#utils#get_buffer_uri() }}),
62+
\ ),
63+
\ ),
64+
\ lsp#callbag#filter({_->g:lsp_diagnostics_highlights_enabled}),
65+
\ lsp#callbag#debounceTime(g:lsp_diagnostics_highlights_delay),
66+
\ lsp#callbag#tap({x->s:clear_highlights(x)}),
67+
\ lsp#callbag#tap({x->s:set_highlights(x)}),
68+
\ lsp#callbag#subscribe(),
69+
\ )
70+
endfunction
71+
72+
function! lsp#internal#diagnostics#highlights#_disable() abort
73+
if !s:enabled | return | endif
74+
if exists('s:Dispose')
75+
call s:Dispose()
76+
unlet s:Dispose
77+
endif
78+
call s:clear_all_highlights()
79+
let s:enabled = 0
80+
endfunction
81+
82+
function! s:get_prop_type_name(severity) abort
83+
return s:namespace_id . '_' . get(s:severity_sign_names_mapping, a:severity, 'LspError')
84+
endfunction
85+
86+
function! s:clear_all_highlights() abort
87+
for l:bufnr in range(1, bufnr('$'))
88+
if bufexists(l:bufnr) && bufloaded(l:bufnr)
89+
if has('nvim')
90+
call nvim_buf_clear_namespace(l:bufnr, s:namespace_id, 0, -1)
91+
else
92+
for l:severity in keys(s:severity_sign_names_mapping)
93+
try
94+
" TODO: need to check for valid range before calling prop_add
95+
" See https://github.com/prabirshrestha/vim-lsp/pull/721
96+
silent! call prop_remove({
97+
\ 'type': s:get_prop_type_name(l:severity),
98+
\ 'bufnr': l:bufnr,
99+
\ 'all': v:true })
100+
catch
101+
call lsp#log('diagnostics', 'clear_all_highlights', 'prop_remove', v:exception, v:throwpoint)
102+
endtry
103+
endfor
104+
endif
105+
endif
106+
endfor
107+
endfunction
108+
109+
function! s:clear_highlights(params) abort
110+
" TODO: optimize by looking at params
111+
call s:clear_all_highlights()
112+
endfunction
113+
114+
function! s:set_highlights(params) abort
115+
" TODO: optimize by looking at params
116+
if !g:lsp_diagnostics_highlights_insert_mode_enabled
117+
if mode()[0] ==# 'i' | return | endif
118+
endif
119+
120+
for l:bufnr in range(1, bufnr('$'))
121+
if lsp#internal#diagnostics#state#_is_enabled_for_buffer(l:bufnr) && bufexists(l:bufnr) && bufloaded(l:bufnr)
122+
let l:uri = lsp#utils#get_buffer_uri(l:bufnr)
123+
for [l:server, l:diagnostics_response] in items(lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri))
124+
call s:place_highlights(l:server, l:diagnostics_response, l:bufnr)
125+
endfor
126+
endif
127+
endfor
128+
endfunction
129+
130+
function! s:place_highlights(server, diagnostics_response, bufnr) abort
131+
" TODO: make diagnostics highlights same across vim and neovim
132+
for l:item in a:diagnostics_response['params']['diagnostics']
133+
let [l:start_line, l:start_col] = lsp#utils#position#lsp_to_vim(a:bufnr, l:item['range']['start'])
134+
let [l:end_line, l:end_col] = lsp#utils#position#lsp_to_vim(a:bufnr, l:item['range']['end'])
135+
let l:severity = get(l:item, 'severity', 3)
136+
let l:hl_group = get(s:severity_sign_names_mapping, l:severity, 'LspError') . 'Highlight'
137+
if has('nvim')
138+
for l:line in range(l:start_line, l:end_line)
139+
if l:line == l:start_line
140+
let l:highlight_start_col = l:start_col
141+
else
142+
let l:highlight_start_col = 1
143+
endif
144+
145+
if l:line == l:end_line
146+
let l:highlight_end_col = l:end_col
147+
else
148+
" neovim treats -1 as end of line, special handle it later
149+
" when calling nvim_buf_add_higlight
150+
let l:highlight_end_col = -1
151+
endif
152+
153+
if l:start_line == l:end_line && l:highlight_start_col == l:highlight_end_col
154+
" higlighting same start col and end col on same line
155+
" doesn't work so use -1 for start col
156+
let l:highlight_start_col -= 1
157+
if l:highlight_start_col <= 0
158+
let l:highlight_start_col = 1
159+
endif
160+
endif
161+
162+
call nvim_buf_add_highlight(a:bufnr, s:namespace_id, l:hl_group,
163+
\ l:line - 1, l:highlight_start_col - 1, l:highlight_end_col == -1 ? -1 : l:highlight_end_col)
164+
endfor
165+
else
166+
try
167+
" TODO: need to check for valid range before calling prop_add
168+
" See https://github.com/prabirshrestha/vim-lsp/pull/721
169+
silent! call prop_add(l:start_line, l:start_col, {
170+
\ 'end_lnum': l:end_line,
171+
\ 'end_col': l:end_col,
172+
\ 'bufnr': a:bufnr,
173+
\ 'type': s:get_prop_type_name(l:severity),
174+
\ })
175+
catch
176+
call lsp#log('diagnostics', 'place_highlights', 'prop_add', v:exception, v:throwpoint)
177+
endtry
178+
endif
179+
endfor
180+
endfunction

autoload/lsp/internal/document_highlight.vim

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
let s:use_vim_textprops = has('textprop') && !has('nvim')
1+
let s:use_vim_textprops = lsp#utils#_has_textprops() && !has('nvim')
22
let s:prop_id = 11
33

44
function! lsp#internal#document_highlight#_enable() abort
@@ -108,12 +108,18 @@ function! s:set_highlights(data) abort
108108
call s:init_reference_highlight(l:bufnr)
109109
if s:use_vim_textprops
110110
for l:position in l:position_list
111-
call prop_add(l:position[0], l:position[1],
112-
\ {'id': s:prop_id,
113-
\ 'bufnr': l:bufnr,
114-
\ 'length': l:position[2],
115-
\ 'type': 'vim-lsp-reference-highlight'})
116-
call add(b:lsp_reference_matches, l:position[0])
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
117123
endfor
118124
else
119125
for l:position in l:position_list

autoload/lsp/ui/vim/diagnostics.vim

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,9 @@ function! lsp#ui#vim#diagnostics#handle_text_document_publish_diagnostics(server
1212
endif
1313
let s:diagnostics[l:uri][a:server_name] = a:data
1414

15-
call lsp#ui#vim#highlights#set(a:server_name, a:data)
16-
call lsp#ui#vim#diagnostics#textprop#set(a:server_name, a:data)
17-
1815
doautocmd <nomodeline> User lsp_diagnostics_updated
1916
endfunction
2017

21-
function! lsp#ui#vim#diagnostics#force_refresh(bufnr) abort
22-
let l:data = lsp#ui#vim#diagnostics#get_document_diagnostics(a:bufnr)
23-
if !empty(l:data)
24-
for [l:server_name, l:response] in items(l:data)
25-
call lsp#ui#vim#highlights#set(l:server_name, l:response)
26-
call lsp#ui#vim#diagnostics#textprop#set(l:server_name, l:response)
27-
endfor
28-
endif
29-
endfunction
30-
3118
function! lsp#ui#vim#diagnostics#get_document_diagnostics(bufnr) abort
3219
return get(s:diagnostics, lsp#utils#get_buffer_uri(a:bufnr), {})
3320
endfunction

0 commit comments

Comments
 (0)