]> git.madduck.net Git - etc/vim.git/blob - autoload/lsp/internal/diagnostics/virtual_text.vim

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Squashed '.vim/bundle/vim-lsp/' content from commit 04428c92
[etc/vim.git] / autoload / lsp / internal / diagnostics / virtual_text.vim
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 let s:severity_sign_names_mapping = {
5     \ 1: 'LspError',
6     \ 2: 'LspWarning',
7     \ 3: 'LspInformation',
8     \ 4: 'LspHint',
9     \ }
10
11 if !hlexists('LspErrorVirtualText')
12   if !hlexists('LspErrorText')
13     highlight link LspErrorVirtualText Error
14   else
15     highlight link LspErrorVirtualText LspErrorText
16   endif
17 endif
18
19 if !hlexists('LspWarningVirtualText')
20   if !hlexists('LspWarningText')
21     highlight link LspWarningVirtualText Todo
22   else
23     highlight link LspWarningVirtualText LspWarningText
24   endif
25 endif
26
27 if !hlexists('LspInformationVirtualText')
28   if !hlexists('LspInformationText')
29     highlight link LspInformationVirtualText Normal
30   else
31     highlight link LspInformationVirtualText LspInformationText
32   endif
33 endif
34
35 if !hlexists('LspHintVirtualText')
36   if !hlexists('LspHintText')
37     highlight link LspHintVirtualText Normal
38   else
39     highlight link LspHintVirtualText LspHintText
40   endif
41 endif
42
43 " imports
44 let s:Buffer = vital#lsp#import('VS.Vim.Buffer')
45
46 function! lsp#internal#diagnostics#virtual_text#_enable() abort
47     " don't even bother registering if the feature is disabled
48     if !lsp#utils#_has_nvim_virtual_text() && !lsp#utils#_has_vim_virtual_text() | return | endif
49     if !g:lsp_diagnostics_virtual_text_enabled | return | endif 
50
51     if s:enabled | return | endif
52     let s:enabled = 1
53
54     if has('nvim')
55         if empty(s:namespace_id)
56             let s:namespace_id = nvim_create_namespace('vim_lsp_diagnostic_virtual_text')
57         endif
58     else
59         if index(prop_type_list(), 'vim_lsp_LspError_virtual_text') ==# -1
60             call prop_type_add('vim_lsp_LspError_virtual_text', { 'highlight': 'LspErrorVirtualText' })
61             call prop_type_add('vim_lsp_LspWarning_virtual_text', { 'highlight': 'LspWarningVirtualText' })
62             call prop_type_add('vim_lsp_LspInformation_virtual_text', { 'highlight': 'LspInformationVirtualText' })
63             call prop_type_add('vim_lsp_LspHint_virtual_text', { 'highlight': 'LspHintVirtualText' })
64         endif
65     endif
66
67     let s:Dispose = lsp#callbag#pipe(
68         \ lsp#callbag#merge(
69         \   lsp#callbag#pipe(
70         \       lsp#stream(),
71         \       lsp#callbag#filter({x->has_key(x, 'server') && has_key(x, 'response')
72         \       && has_key(x['response'], 'method') && x['response']['method'] ==# '$/vimlsp/lsp_diagnostics_updated'
73         \       && !lsp#client#is_error(x['response'])}),
74         \       lsp#callbag#map({x->x['response']['params']}),
75         \   ),
76         \   lsp#callbag#pipe(
77         \       lsp#callbag#fromEvent(['InsertEnter', 'InsertLeave']),
78         \       lsp#callbag#filter({_->!g:lsp_diagnostics_virtual_text_insert_mode_enabled}),
79         \       lsp#callbag#map({_->{ 'uri': lsp#utils#get_buffer_uri() }}),
80         \   ),
81         \ ),
82         \ lsp#callbag#filter({_->g:lsp_diagnostics_virtual_text_enabled}),
83         \ lsp#callbag#debounceTime(g:lsp_diagnostics_virtual_text_delay),
84         \ lsp#callbag#tap({x->s:clear_virtual_text(x)}),
85         \ lsp#callbag#tap({x->s:set_virtual_text(x)}),
86         \ lsp#callbag#subscribe(),
87         \ )
88 endfunction
89
90 function! lsp#internal#diagnostics#virtual_text#_disable() abort
91     if !s:enabled | return | endif
92     if exists('s:Dispose')
93         call s:Dispose()
94         unlet s:Dispose
95     endif
96     call s:clear_all_virtual_text()
97     let s:enabled = 0
98 endfunction
99
100 function! s:clear_all_virtual_text() abort
101     if has('nvim')
102         for l:bufnr in nvim_list_bufs()
103             if bufexists(l:bufnr) && bufloaded(l:bufnr)
104                 call nvim_buf_clear_namespace(l:bufnr, s:namespace_id, 0, -1)
105             endif
106         endfor
107     else
108         let l:types = ['vim_lsp_LspError_virtual_text', 'vim_lsp_LspWarning_virtual_text', 'vim_lsp_LspInformation_virtual_text', 'vim_lsp_LspHint_virtual_text']
109         for l:bufnr in map(copy(getbufinfo()), 'v:val.bufnr')
110             if lsp#utils#_has_prop_remove_types()
111                 call prop_remove({'types': l:types, 'bufnr': l:bufnr, 'all': v:true})
112             else
113                 for l:type in l:types
114                     call prop_remove({'type': l:type, 'bufnr': l:bufnr, 'all': v:true})
115                 endfor
116             endif
117         endfor
118     endif
119 endfunction
120
121 " params => {
122 "   server: ''  " optional
123 "   uri: ''     " optional
124 " }
125 function! s:clear_virtual_text(params) abort
126     " TODO: optimize by looking at params
127     call s:clear_all_virtual_text()
128 endfunction
129
130 " params => {
131 "   server: ''  " optional
132 "   uri: ''     " optional
133 " }
134 function! s:set_virtual_text(params) abort
135     " TODO: optimize by looking at params
136     if !g:lsp_diagnostics_virtual_text_insert_mode_enabled
137         if mode()[0] ==# 'i' | return | endif
138     endif
139
140     if has('nvim')
141         for l:bufnr in nvim_list_bufs()
142             if lsp#internal#diagnostics#state#_is_enabled_for_buffer(l:bufnr) && bufexists(l:bufnr) && bufloaded(l:bufnr)
143                 let l:uri = lsp#utils#get_buffer_uri(l:bufnr)
144                 for [l:server, l:diagnostics_response] in items(lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri))
145                     call s:place_virtual_text(l:server, l:diagnostics_response, l:bufnr)
146                 endfor
147             endif
148         endfor
149     else
150         for l:bufnr in map(copy(getbufinfo()), 'v:val.bufnr')
151             if lsp#internal#diagnostics#state#_is_enabled_for_buffer(l:bufnr) && bufexists(l:bufnr) && bufloaded(l:bufnr)
152                 let l:uri = lsp#utils#get_buffer_uri(l:bufnr)
153                 for [l:server, l:diagnostics_response] in items(lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri))
154                     call s:place_virtual_text(l:server, l:diagnostics_response, l:bufnr)
155                 endfor
156             endif
157         endfor
158     endif
159 endfunction
160
161 function! s:place_virtual_text(server, diagnostics_response, bufnr) abort
162     let l:linecount = s:Buffer.get_line_count(a:bufnr)
163     for l:item in lsp#utils#iteratable(a:diagnostics_response['params']['diagnostics'])
164         let l:line = lsp#utils#position#lsp_line_to_vim(a:bufnr, l:item['range']['start'])
165         let l:name = get(s:severity_sign_names_mapping, get(l:item, 'severity', 3), 'LspError')
166         let l:text = g:lsp_diagnostics_virtual_text_prefix . l:item['message']
167
168         " Some language servers report an unexpected EOF one line past the end
169         if l:line == l:linecount + 1
170             let l:line = l:line - 1
171         endif
172
173         if has('nvim')
174             let l:hl_name = l:name . 'VirtualText'
175             " need to do -1 for virtual text
176             call nvim_buf_set_virtual_text(a:bufnr, s:namespace_id, l:line - 1,
177                 \ [[l:text, l:hl_name]], {})
178         else
179             " it's an error to add virtual text on lines that don't exist
180             " anymore due to async processing, just skip such diagnostics
181             if l:line <= l:linecount
182                 let l:type = 'vim_lsp_' . l:name . '_virtual_text'
183                 call prop_remove({'all': v:true, 'type': l:type, 'bufnr': a:bufnr}, l:line)
184                 call prop_add(
185                 \ l:line, 0,
186                 \ {
187                 \   'type': l:type, 'text': l:text, 'bufnr': a:bufnr,
188                 \   'text_align': g:lsp_diagnostics_virtual_text_align,
189                 \   'text_padding_left': g:lsp_diagnostics_virtual_text_padding_left,
190                 \   'text_wrap': g:lsp_diagnostics_virtual_text_wrap,
191                 \ })
192             endif
193         endif
194     endfor
195 endfunction