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