vim-lsp icon indicating copy to clipboard operation
vim-lsp copied to clipboard

use error quicklist for LSP diagnostics?

Open jlevon opened this issue 2 years ago • 3 comments

Right now, I can't use :cp, :cn etc. for LSP diagnostics, instead there's a totally separate mechanism. Is there any way to make this possible?

Using latest vim.

Since things like lsp-call-hierarchy-incoming loads into the quicklist, it seems inconsistent that the diagnostics list is completely separate.

I think this is #761 but I can't reopen that.

I made this change:

function! lsp#internal#diagnostics#document_diagnostics_command#do(options) abort
...
    if empty(l:result)
        call lsp#utils#error('No diagnostics results')
        return
    else
        "call setloclist(0, l:result)
        call setqflist(l:result)
        echo 'Retrieved diagnostics results'
        "botright lopen
    endif

which works but I have to call :LspDocumentDiagnostics. I'd like the automatic adding/removing of these to go to quick fix instead of loc list. I don't know anything about vimscripting so I can't find where I might do that.

jlevon avatar Mar 25 '22 12:03 jlevon

+1, I used to have this functionality in ALE and it is very useful. I could simply type :lw and all the diagnostics would be populated in the location list

brianjamesquinn1 avatar Apr 04 '22 18:04 brianjamesquinn1

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jun 11 '22 04:06 stale[bot]

still valid request.

jlevon avatar Jun 11 '22 13:06 jlevon

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Aug 12 '22 06:08 stale[bot]

still a valid request

jlevon avatar Aug 12 '22 10:08 jlevon

Here is my solution:

" NOTE: I rely on $PWD being the project root. Works automatically in my case since I open vim with Unity
" NOTE: This is just done to filter out some stuff. I use Unity and omnisharp shows a bunch of hints about unnecessary using directives
let s:project_file_uris = map(systemlist("find $PWD/Assets/ -name '*.cs'"), {_, val -> lsp#utils#path_to_uri(val)})
let s:qflist = []

function! Updateqf(timer)
    let l:result = []
    for l:uri in s:project_file_uris
        let l:diag = lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri)
        " TODO: custom solution just for Unity & Omnisharp. Make it work for everything
        " NOTE: While the server is starting up there are no diagnostics responses yet. Hence l:diag['omnisharp_mono'] returns an error 
        if !has_key(l:diag, 'omnisharp_mono')
            return
        endif
        let l:result += lsp#ui#vim#utils#diagnostics_to_loc_list({ 'response': l:diag['omnisharp_mono'] })
    endfor
    " Don't change the qf list if there is no change (otherwise qf list jumps back to the first entry everytime this function is called)
    if l:result == s:qflist
        return
    endif
   " Don't change the quickfix list while I'm editing. I don't like stuff changing on my screen while I type
    if mode() == 'i'
        return
    endif
    " Update current qf list
    let s:qflist = l:result

    if empty(l:result)
        cclose
    else
        " Open quickfix list without focusing
        let l:bufname=bufname('%')
        call setqflist(l:result, 'r')
        botright copen 8
        exec bufwinnr(l:bufname) . 'wincmd w'
    endif
endfunction

" Call the function every second
let g:lspqftimer = timer_start(1000, 'Updateqf', {'repeat': -1})

It's customized for Unity/C# editing but hopefully you can adapt it for your purposes.

I added some comments because I wrote shitty code to quickly get to work.

I use a timer to run the function every second. There is an autocommand from vim-lsp that is triggered every time there is a diagnostic change but that happened too often for my taste (and also isn't asynchronous). Same for the vim autocommand when the text changed.

The diagnostics list being loaded in the location list is not inconsistent. The location list is local to a buffer, and LspDocumentDiagnostics only loads the diagnostics for the current buffer. I just happen to like having a global quickfix list that shows me all the problems in my code base.

Final word of caution: I also know next to nothing about vimscript, this is probably not the best way to do this.

pkrack avatar Oct 01 '22 16:10 pkrack

Thanks @pkrack ! Here's what I added to the bottom of lsp.vim for my use case:

let s:qflist = []

function! Updateqf(timer)
    let l:options = get(a:000, 0, {})
    let l:server = get(l:options, 'server', '')
    let l:bufnr = bufnr('%')
    let l:result = []

    if !lsp#internal#diagnostics#state#_is_enabled_for_buffer(l:bufnr)
        return {}
    endif

    let l:uri = lsp#utils#get_buffer_uri(l:bufnr)

    let l:diag = lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_server_for_uri(l:uri)
    
    if !has_key(l:diag, 'clangd')
        return
    endif

    let l:result += lsp#ui#vim#utils#diagnostics_to_loc_list({ 'response': l:diag['clangd'] })

    if l:result == s:qflist
        return
    endif

    " Don't change the quickfix list while I'm editing. I don't like stuff changing on my screen while I type
    if mode() == 'i'
        return
    endif
    " Update current qf list
    let s:qflist = l:result

    if empty(l:result)
        cclose
    else
        call setqflist(l:result, 'r')
    endif
endfunction

" Call the function every second
let g:lspqftimer = timer_start(1000, 'Updateqf', {'repeat': -1})

jlevon avatar Oct 07 '22 15:10 jlevon