Skip to content

Commit 5743ae2

Browse files
Diagnostics signs revamp (#994)
1 parent bc98be8 commit 5743ae2

File tree

10 files changed

+241
-203
lines changed

10 files changed

+241
-203
lines changed

README.md

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -144,21 +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-
#### Signs
148-
149-
```viml
150-
let g:lsp_signs_enabled = 1 " enable signs
151-
let g:lsp_diagnostics_echo_cursor = 1 " enable echo under cursor when in normal mode
152-
```
153-
154-
Four groups of signs are defined and used: `LspError`, `LspWarning`, `LspInformation`, `LspHint`. It is possible to set custom text or icon that will be used for each sign (note that icons are only available in GUI). To do this, set some of the following globals: `g:lsp_signs_error`, `g:lsp_signs_warning`, `g:lsp_signs_information`, `g:lsp_signs_hint`. They should be set to a dict, that contains either text that will be used as sign in terminal, or icon that will be used for GUI, or both. For example:
155-
156-
```viml
157-
let g:lsp_signs_error = {'text': '✗'}
158-
let g:lsp_signs_warning = {'text': '‼', 'icon': '/path/to/some/icon'} " icons require GUI
159-
let g:lsp_signs_hint = {'icon': '/path/to/some/other/icon'} " icons require GUI
160-
```
161-
162147
#### Highlights
163148

164149
Highlighting diagnostics requires either NeoVim 0.3+ or Vim with patch 8.1.0579.

autoload/lsp.vim

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ function! lsp#enable() abort
5555
endif
5656
let s:enabled = 1
5757
if g:lsp_diagnostics_enabled
58-
if g:lsp_signs_enabled | call lsp#ui#vim#signs#enable() | endif
5958
if g:lsp_highlights_enabled | call lsp#ui#vim#highlights#enable() | endif
6059
if g:lsp_textprop_enabled | call lsp#ui#vim#diagnostics#textprop#enable() | endif
6160
endif
@@ -73,7 +72,6 @@ function! lsp#disable() abort
7372
if !s:enabled
7473
return
7574
endif
76-
call lsp#ui#vim#signs#disable()
7775
call lsp#ui#vim#highlights#disable()
7876
call lsp#ui#vim#diagnostics#textprop#disable()
7977
call lsp#ui#vim#signature_help#_disable()

autoload/lsp/internal/diagnostics.vim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ function! lsp#internal#diagnostics#_enable() abort
55
call lsp#internal#diagnostics#state#_enable() " Needs to be the first one to register
66
call lsp#internal#diagnostics#echo#_enable()
77
call lsp#internal#diagnostics#float#_enable()
8+
call lsp#internal#diagnostics#signs#_enable()
89
call lsp#internal#diagnostics#virtual_text#_enable()
910
endfunction
1011

1112
function! lsp#internal#diagnostics#_disable() abort
1213
call lsp#internal#diagnostics#echo#_disable()
1314
call lsp#internal#diagnostics#float#_disable()
1415
call lsp#internal#diagnostics#virtual_text#_disable()
16+
call lsp#internal#diagnostics#signs#_disable()
1517
call lsp#internal#diagnostics#state#_disable() " Needs to be the last one to unregister
1618
endfunction
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
" internal state for whether it is enabled or not to avoid multiple subscriptions
2+
let s:enabled = 0
3+
let s:sign_group = 'vim_lsp'
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('LspErrorText')
13+
highlight link LspErrorText Error
14+
endif
15+
16+
if !hlexists('LspWarningText')
17+
highlight link LspWarningText Todo
18+
endif
19+
20+
if !hlexists('LspInformationText')
21+
highlight link LspInformationText Normal
22+
endif
23+
24+
if !hlexists('LspHintText')
25+
highlight link LspHintText Normal
26+
endif
27+
28+
function! lsp#internal#diagnostics#signs#_enable() abort
29+
" don't even bother registering if the feature is disabled
30+
if !lsp#utils#_has_signs() | return | endif
31+
if !g:lsp_diagnostics_signs_enabled | return | endif
32+
33+
if s:enabled | return | endif
34+
let s:enabled = 1
35+
36+
call s:define_sign('LspError', 'E>', g:lsp_diagnostics_signs_error)
37+
call s:define_sign('LspWarning', 'W>', g:lsp_diagnostics_signs_warning)
38+
call s:define_sign('LspInformation', 'I>', g:lsp_diagnostics_signs_information)
39+
call s:define_sign('LspHint', 'H>', g:lsp_diagnostics_signs_hint)
40+
41+
let s:Dispose = lsp#callbag#pipe(
42+
\ lsp#callbag#merge(
43+
\ lsp#callbag#pipe(
44+
\ lsp#stream(),
45+
\ lsp#callbag#filter({x->has_key(x, 'server') && has_key(x, 'response')
46+
\ && has_key(x['response'], 'method') && x['response']['method'] ==# '$/vimlsp/lsp_diagnostics_updated'
47+
\ && !lsp#client#is_error(x['response'])}),
48+
\ lsp#callbag#map({x->x['response']['params']}),
49+
\ ),
50+
\ lsp#callbag#pipe(
51+
\ lsp#callbag#fromEvent(['InsertEnter', 'InsertLeave']),
52+
\ lsp#callbag#filter({_->!g:lsp_diagnostics_signs_insert_mode_enabled}),
53+
\ lsp#callbag#map({_->{ 'uri': lsp#utils#get_buffer_uri() }}),
54+
\ ),
55+
\ ),
56+
\ lsp#callbag#filter({_->g:lsp_diagnostics_signs_enabled}),
57+
\ lsp#callbag#debounceTime(g:lsp_diagnostics_signs_delay),
58+
\ lsp#callbag#tap({x->s:clear_signs(x)}),
59+
\ lsp#callbag#tap({x->s:set_signs(x)}),
60+
\ lsp#callbag#subscribe(),
61+
\ )
62+
endfunction
63+
64+
function! lsp#internal#diagnostics#signs#_disable() abort
65+
if !s:enabled | return | endif
66+
if exists('s:Dispose')
67+
call s:Dispose()
68+
unlet s:Dispose
69+
endif
70+
call s:clear_all_signs()
71+
call s:undefine_signs()
72+
let s:enabled = 0
73+
endfunction
74+
75+
" Set default sign text to handle case when user provides empty dict
76+
function! s:define_sign(sign_name, sign_default_text, sign_options) abort
77+
let l:options = {
78+
\ 'text': get(a:sign_options, 'text', a:sign_default_text),
79+
\ 'texthl': a:sign_name . 'Text',
80+
\ 'linehl': a:sign_name . 'Line',
81+
\ }
82+
let l:sign_icon = get(a:sign_options, 'icon', '')
83+
if !empty(l:sign_icon)
84+
let l:options['icon'] = l:sign_icon
85+
endif
86+
call sign_define(a:sign_name, l:options)
87+
endfunction
88+
89+
function! s:undefine_signs() abort
90+
call sign_undefine('LspError')
91+
call sign_undefine('LspWarning')
92+
call sign_undefine('LspInformation')
93+
call sign_undefine('LspHint')
94+
endfunction
95+
96+
function! s:clear_all_signs() abort
97+
call sign_unplace(s:sign_group)
98+
endfunction
99+
100+
" params => {
101+
" server: '' " optional
102+
" uri: '' " optional
103+
" }
104+
function! s:clear_signs(params) abort
105+
" TODO: optimize by looking at params
106+
call s:clear_all_signs()
107+
endfunction
108+
109+
" params => {
110+
" server: '' " optional
111+
" uri: '' " optional
112+
" }
113+
function! s:set_signs(params) abort
114+
" TODO: optimize by looking at params
115+
if !g:lsp_diagnostics_signs_insert_mode_enabled
116+
if mode()[0] ==# 'i' | return | endif
117+
endif
118+
119+
for l:bufnr in range(1, bufnr('$'))
120+
if lsp#internal#diagnostics#state#_is_enabled_for_buffer(l:bufnr) && bufexists(l:bufnr) && bufloaded(l:bufnr)
121+
let l:uri = lsp#utils#get_buffer_uri(l:bufnr)
122+
for [l:server, l:diagnostics_response] in items(lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri))
123+
call s:place_signs(l:server, l:diagnostics_response, l:bufnr)
124+
endfor
125+
endif
126+
endfor
127+
endfunction
128+
129+
function! s:place_signs(server, diagnostics_response, bufnr) abort
130+
for l:item in a:diagnostics_response['params']['diagnostics']
131+
let l:line = lsp#utils#position#lsp_line_to_vim(a:bufnr, l:item['range']['start'])
132+
if has_key(l:item, 'severity') && !empty(l:item['severity'])
133+
let l:sign_name = get(s:severity_sign_names_mapping, l:item['severity'], 'LspError')
134+
let l:sign_priority = get(g:lsp_diagnostics_signs_priority_map, l:sign_name, g:lsp_diagnostics_signs_priority)
135+
let l:sign_priority = get(g:lsp_diagnostics_signs_priority_map,
136+
\ a:server . '_' . l:sign_name, l:sign_priority)
137+
" pass 0 and let vim generate sign id
138+
let l:sign_id = sign_place(0, s:sign_group, l:sign_name, a:bufnr,
139+
\{ 'lnum': l:line, 'priority': l:sign_priority })
140+
endif
141+
endfor
142+
endfunction

autoload/lsp/internal/diagnostics/virtual_text.vim

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ function! s:set_virtual_text(params) abort
113113
endif
114114

115115
for l:bufnr in nvim_list_bufs()
116-
let l:uri = lsp#utils#get_buffer_uri(l:bufnr)
117-
if lsp#internal#diagnostics#state#_is_enabled_for_buffer(l:uri) && bufexists(l:bufnr) && bufloaded(l:bufnr)
116+
if lsp#internal#diagnostics#state#_is_enabled_for_buffer(l:bufnr) && bufexists(l:bufnr) && bufloaded(l:bufnr)
117+
let l:uri = lsp#utils#get_buffer_uri(l:bufnr)
118118
for [l:server, l:diagnostics_response] in items(lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri))
119119
call s:place_virtual_text(l:server, l:diagnostics_response, l:bufnr)
120120
endfor
@@ -124,6 +124,7 @@ endfunction
124124

125125
function! s:place_virtual_text(server, diagnostics_response, bufnr) abort
126126
for l:item in a:diagnostics_response['params']['diagnostics']
127+
" need to do -1 for virtual text
127128
let l:line = lsp#utils#position#lsp_line_to_vim(a:bufnr, l:item['range']['start']) - 1
128129
let l:name = get(s:severity_sign_names_mapping, get(l:item, 'severity', 3), 'LspError')
129130
let l:hl_name = l:name . 'VirtualText'

autoload/lsp/ui/vim/diagnostics.vim

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ function! lsp#ui#vim#diagnostics#handle_text_document_publish_diagnostics(server
1414

1515
call lsp#ui#vim#highlights#set(a:server_name, a:data)
1616
call lsp#ui#vim#diagnostics#textprop#set(a:server_name, a:data)
17-
call lsp#ui#vim#signs#set(a:server_name, a:data)
1817

1918
doautocmd <nomodeline> User lsp_diagnostics_updated
2019
endfunction
@@ -25,7 +24,6 @@ function! lsp#ui#vim#diagnostics#force_refresh(bufnr) abort
2524
for [l:server_name, l:response] in items(l:data)
2625
call lsp#ui#vim#highlights#set(l:server_name, l:response)
2726
call lsp#ui#vim#diagnostics#textprop#set(l:server_name, l:response)
28-
call lsp#ui#vim#signs#set(l:server_name, l:response)
2927
endfor
3028
endif
3129
endfunction

autoload/lsp/ui/vim/signs.vim

Lines changed: 0 additions & 141 deletions
This file was deleted.

autoload/lsp/utils.vim

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ function! lsp#utils#_has_virtual_text() abort
33
return s:has_virtual_text
44
endfunction
55

6+
let s:has_signs = exists('*sign_define') && (has('nvim') || has('patch-8.1.0772'))
7+
function! lsp#utils#_has_signs() abort
8+
return s:has_signs
9+
endfunction
10+
611
function! lsp#utils#is_file_uri(uri) abort
712
return stridx(a:uri, 'file:///') == 0
813
endfunction

0 commit comments

Comments
 (0)