" internal state for whether it is enabled or not to avoid multiple subscriptions let s:enabled = 0 let s:namespace_id = '' " will be set when enabled let s:severity_sign_names_mapping = { \ 1: 'LspError', \ 2: 'LspWarning', \ 3: 'LspInformation', \ 4: 'LspHint', \ } if !hlexists('LspErrorHighlight') highlight link LspErrorHighlight Error endif if !hlexists('LspWarningHighlight') highlight link LspWarningHighlight Todo endif if !hlexists('LspInformationHighlight') highlight link LspInformationHighlight Normal endif if !hlexists('LspHintHighlight') highlight link LspHintHighlight Normal endif function! lsp#internal#diagnostics#highlights#_enable() abort " don't even bother registering if the feature is disabled if !lsp#utils#_has_highlights() | return | endif if !g:lsp_diagnostics_highlights_enabled | return | endif if s:enabled | return | endif let s:enabled = 1 if empty(s:namespace_id) if has('nvim') let s:namespace_id = nvim_create_namespace('vim_lsp_diagnostics_highlights') else let s:namespace_id = 'vim_lsp_diagnostics_highlights' for l:severity in keys(s:severity_sign_names_mapping) let l:hl_group = s:severity_sign_names_mapping[l:severity] . 'Highlight' call prop_type_add(s:get_prop_type_name(l:severity), \ {'highlight': l:hl_group, 'combine': v:true, 'priority': lsp#internal#textprop#priority('diagnostics_highlight') }) endfor endif endif let s:Dispose = lsp#callbag#pipe( \ lsp#callbag#merge( \ lsp#callbag#pipe( \ lsp#stream(), \ lsp#callbag#filter({x->has_key(x, 'server') && has_key(x, 'response') \ && has_key(x['response'], 'method') && x['response']['method'] ==# '$/vimlsp/lsp_diagnostics_updated' \ && !lsp#client#is_error(x['response'])}), \ lsp#callbag#map({x->x['response']['params']}), \ ), \ lsp#callbag#pipe( \ lsp#callbag#fromEvent(['InsertEnter', 'InsertLeave']), \ lsp#callbag#filter({_->!g:lsp_diagnostics_highlights_insert_mode_enabled}), \ lsp#callbag#map({_->{ 'uri': lsp#utils#get_buffer_uri() }}), \ ), \ ), \ lsp#callbag#filter({_->g:lsp_diagnostics_highlights_enabled}), \ lsp#callbag#debounceTime(g:lsp_diagnostics_highlights_delay), \ lsp#callbag#tap({x->s:clear_highlights(x)}), \ lsp#callbag#tap({x->s:set_highlights(x)}), \ lsp#callbag#subscribe(), \ ) endfunction function! lsp#internal#diagnostics#highlights#_disable() abort if !s:enabled | return | endif if exists('s:Dispose') call s:Dispose() unlet s:Dispose endif call s:clear_all_highlights() let s:enabled = 0 endfunction function! s:get_prop_type_name(severity) abort return s:namespace_id . '_' . get(s:severity_sign_names_mapping, a:severity, 'LspError') endfunction function! s:clear_all_highlights() abort for l:bufnr in range(1, bufnr('$')) if bufexists(l:bufnr) && bufloaded(l:bufnr) if has('nvim') call nvim_buf_clear_namespace(l:bufnr, s:namespace_id, 0, -1) else for l:severity in keys(s:severity_sign_names_mapping) try " TODO: need to check for valid range before calling prop_add " See https://github.com/prabirshrestha/vim-lsp/pull/721 silent! call prop_remove({ \ 'type': s:get_prop_type_name(l:severity), \ 'bufnr': l:bufnr, \ 'all': v:true }) catch call lsp#log('diagnostics', 'clear_all_highlights', 'prop_remove', v:exception, v:throwpoint) endtry endfor endif endif endfor endfunction function! s:clear_highlights(params) abort " TODO: optimize by looking at params call s:clear_all_highlights() endfunction function! s:set_highlights(params) abort " TODO: optimize by looking at params if !g:lsp_diagnostics_highlights_insert_mode_enabled if mode()[0] ==# 'i' | return | endif endif for l:bufnr in range(1, bufnr('$')) if lsp#internal#diagnostics#state#_is_enabled_for_buffer(l:bufnr) && bufexists(l:bufnr) && bufloaded(l:bufnr) let l:uri = lsp#utils#get_buffer_uri(l:bufnr) for [l:server, l:diagnostics_response] in items(lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri)) call s:place_highlights(l:server, l:diagnostics_response, l:bufnr) endfor endif endfor endfunction function! s:place_highlights(server, diagnostics_response, bufnr) abort " TODO: make diagnostics highlights same across vim and neovim for l:item in lsp#utils#iteratable(a:diagnostics_response['params']['diagnostics']) let [l:start_line, l:start_col] = lsp#utils#position#lsp_to_vim(a:bufnr, l:item['range']['start']) let [l:end_line, l:end_col] = lsp#utils#position#lsp_to_vim(a:bufnr, l:item['range']['end']) let l:severity = get(l:item, 'severity', 3) let l:hl_group = get(s:severity_sign_names_mapping, l:severity, 'LspError') . 'Highlight' if has('nvim') for l:line in range(l:start_line, l:end_line) if l:line == l:start_line let l:highlight_start_col = l:start_col else let l:highlight_start_col = 1 endif if l:line == l:end_line let l:highlight_end_col = l:end_col else " neovim treats -1 as end of line, special handle it later " when calling nvim_buf_add_higlight let l:highlight_end_col = -1 endif if l:start_line == l:end_line && l:highlight_start_col == l:highlight_end_col " higlighting same start col and end col on same line " doesn't work so use -1 for start col let l:highlight_start_col -= 1 if l:highlight_start_col <= 0 let l:highlight_start_col = 1 endif endif call nvim_buf_add_highlight(a:bufnr, s:namespace_id, l:hl_group, \ l:line - 1, l:highlight_start_col - 1, l:highlight_end_col == -1 ? -1 : l:highlight_end_col - 1) endfor else if l:start_line == l:end_line try " TODO: need to check for valid range before calling prop_add " See https://github.com/prabirshrestha/vim-lsp/pull/721 silent! call prop_add(l:start_line, l:start_col, { \ 'end_col': l:end_col, \ 'bufnr': a:bufnr, \ 'type': s:get_prop_type_name(l:severity), \ }) catch call lsp#log('diagnostics', 'place_highlights', 'prop_add', v:exception, v:throwpoint) endtry else for l:line in range(l:start_line, l:end_line) if l:line == l:start_line let l:highlight_start_col = l:start_col else let l:highlight_start_col = 1 endif if l:line == l:end_line let l:highlight_end_col = l:end_col else if has('patch-9.0.0916') let l:highlight_end_col = strlen(getbufoneline(a:bufnr, l:line)) + 1 else let l:highlight_end_col = strlen(getbufline(a:bufnr, l:line)[0]) + 1 endif endif try " TODO: need to check for valid range before calling prop_add " See https://github.com/prabirshrestha/vim-lsp/pull/721 silent! call prop_add(l:line, l:highlight_start_col, { \ 'end_col': l:highlight_end_col, \ 'bufnr': a:bufnr, \ 'type': s:get_prop_type_name(l:severity), \ }) catch call lsp#log('diagnostics', 'place_highlights', 'prop_add', v:exception, v:throwpoint) endtry endfor endif endif endfor endfunction