--- /dev/null
+" 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('LspErrorVirtualText')
+ if !hlexists('LspErrorText')
+ highlight link LspErrorVirtualText Error
+ else
+ highlight link LspErrorVirtualText LspErrorText
+ endif
+endif
+
+if !hlexists('LspWarningVirtualText')
+ if !hlexists('LspWarningText')
+ highlight link LspWarningVirtualText Todo
+ else
+ highlight link LspWarningVirtualText LspWarningText
+ endif
+endif
+
+if !hlexists('LspInformationVirtualText')
+ if !hlexists('LspInformationText')
+ highlight link LspInformationVirtualText Normal
+ else
+ highlight link LspInformationVirtualText LspInformationText
+ endif
+endif
+
+if !hlexists('LspHintVirtualText')
+ if !hlexists('LspHintText')
+ highlight link LspHintVirtualText Normal
+ else
+ highlight link LspHintVirtualText LspHintText
+ endif
+endif
+
+" imports
+let s:Buffer = vital#lsp#import('VS.Vim.Buffer')
+
+function! lsp#internal#diagnostics#virtual_text#_enable() abort
+ " don't even bother registering if the feature is disabled
+ if !lsp#utils#_has_nvim_virtual_text() && !lsp#utils#_has_vim_virtual_text() | return | endif
+ if !g:lsp_diagnostics_virtual_text_enabled | return | endif
+
+ if s:enabled | return | endif
+ let s:enabled = 1
+
+ if has('nvim')
+ if empty(s:namespace_id)
+ let s:namespace_id = nvim_create_namespace('vim_lsp_diagnostic_virtual_text')
+ endif
+ else
+ if index(prop_type_list(), 'vim_lsp_LspError_virtual_text') ==# -1
+ call prop_type_add('vim_lsp_LspError_virtual_text', { 'highlight': 'LspErrorVirtualText' })
+ call prop_type_add('vim_lsp_LspWarning_virtual_text', { 'highlight': 'LspWarningVirtualText' })
+ call prop_type_add('vim_lsp_LspInformation_virtual_text', { 'highlight': 'LspInformationVirtualText' })
+ call prop_type_add('vim_lsp_LspHint_virtual_text', { 'highlight': 'LspHintVirtualText' })
+ 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_virtual_text_insert_mode_enabled}),
+ \ lsp#callbag#map({_->{ 'uri': lsp#utils#get_buffer_uri() }}),
+ \ ),
+ \ ),
+ \ lsp#callbag#filter({_->g:lsp_diagnostics_virtual_text_enabled}),
+ \ lsp#callbag#debounceTime(g:lsp_diagnostics_virtual_text_delay),
+ \ lsp#callbag#tap({x->s:clear_virtual_text(x)}),
+ \ lsp#callbag#tap({x->s:set_virtual_text(x)}),
+ \ lsp#callbag#subscribe(),
+ \ )
+endfunction
+
+function! lsp#internal#diagnostics#virtual_text#_disable() abort
+ if !s:enabled | return | endif
+ if exists('s:Dispose')
+ call s:Dispose()
+ unlet s:Dispose
+ endif
+ call s:clear_all_virtual_text()
+ let s:enabled = 0
+endfunction
+
+function! s:clear_all_virtual_text() abort
+ if has('nvim')
+ for l:bufnr in nvim_list_bufs()
+ if bufexists(l:bufnr) && bufloaded(l:bufnr)
+ call nvim_buf_clear_namespace(l:bufnr, s:namespace_id, 0, -1)
+ endif
+ endfor
+ else
+ let l:types = ['vim_lsp_LspError_virtual_text', 'vim_lsp_LspWarning_virtual_text', 'vim_lsp_LspInformation_virtual_text', 'vim_lsp_LspHint_virtual_text']
+ for l:bufnr in map(copy(getbufinfo()), 'v:val.bufnr')
+ if lsp#utils#_has_prop_remove_types()
+ call prop_remove({'types': l:types, 'bufnr': l:bufnr, 'all': v:true})
+ else
+ for l:type in l:types
+ call prop_remove({'type': l:type, 'bufnr': l:bufnr, 'all': v:true})
+ endfor
+ endif
+ endfor
+ endif
+endfunction
+
+" params => {
+" server: '' " optional
+" uri: '' " optional
+" }
+function! s:clear_virtual_text(params) abort
+ " TODO: optimize by looking at params
+ call s:clear_all_virtual_text()
+endfunction
+
+" params => {
+" server: '' " optional
+" uri: '' " optional
+" }
+function! s:set_virtual_text(params) abort
+ " TODO: optimize by looking at params
+ if !g:lsp_diagnostics_virtual_text_insert_mode_enabled
+ if mode()[0] ==# 'i' | return | endif
+ endif
+
+ if has('nvim')
+ for l:bufnr in nvim_list_bufs()
+ 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_virtual_text(l:server, l:diagnostics_response, l:bufnr)
+ endfor
+ endif
+ endfor
+ else
+ for l:bufnr in map(copy(getbufinfo()), 'v:val.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_virtual_text(l:server, l:diagnostics_response, l:bufnr)
+ endfor
+ endif
+ endfor
+ endif
+endfunction
+
+function! s:place_virtual_text(server, diagnostics_response, bufnr) abort
+ let l:linecount = s:Buffer.get_line_count(a:bufnr)
+ for l:item in lsp#utils#iteratable(a:diagnostics_response['params']['diagnostics'])
+ let l:line = lsp#utils#position#lsp_line_to_vim(a:bufnr, l:item['range']['start'])
+ let l:name = get(s:severity_sign_names_mapping, get(l:item, 'severity', 3), 'LspError')
+ let l:text = g:lsp_diagnostics_virtual_text_prefix . l:item['message']
+
+ " Some language servers report an unexpected EOF one line past the end
+ if l:line == l:linecount + 1
+ let l:line = l:line - 1
+ endif
+
+ if has('nvim')
+ let l:hl_name = l:name . 'VirtualText'
+ " need to do -1 for virtual text
+ call nvim_buf_set_virtual_text(a:bufnr, s:namespace_id, l:line - 1,
+ \ [[l:text, l:hl_name]], {})
+ else
+ " it's an error to add virtual text on lines that don't exist
+ " anymore due to async processing, just skip such diagnostics
+ if l:line <= l:linecount
+ let l:type = 'vim_lsp_' . l:name . '_virtual_text'
+ call prop_remove({'all': v:true, 'type': l:type, 'bufnr': a:bufnr}, l:line)
+ call prop_add(
+ \ l:line, 0,
+ \ {
+ \ 'type': l:type, 'text': l:text, 'bufnr': a:bufnr,
+ \ 'text_align': g:lsp_diagnostics_virtual_text_align,
+ \ 'text_padding_left': g:lsp_diagnostics_virtual_text_padding_left,
+ \ 'text_wrap': g:lsp_diagnostics_virtual_text_wrap,
+ \ })
+ endif
+ endif
+ endfor
+endfunction