|
| 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 |
0 commit comments