The diagnostic popup for some of the COC LSP extensions include a URL to get more information about a particular error. For example, coc-sh shows this (invoked through a mapping that calls <Plug>(coc-diagnostic-info)): diagnostic popup for coc-sh

Is there a way to navigate to the shellcheck.net URL that appears? To be clear, the error message appears in a popup and isn't text that's directly in the buffer (so I can't use gx). Running :call CocAction('diagnosticPreview') and CocDiagnostics both show the message in a separate window, but they oddly don't include the URL.

Ideally, I'd be able to map a key to automatically navigate to the link while the popup is open, but I'd also be ok with a solution that lets me programmatically get access to the entire message via a vimscript so I can trigger things myself. I don't have a super great understanding of all of COC configuration options or terminology for things, so it's been a struggle trying to search for solutions.

  • Maybe this answer could be of some help. Commented Dec 18, 2023 at 22:01
  • That would be on the CoC people, no? Why display a cryptic URL if it can't be interacted with?
    – romainl
    Commented Dec 19, 2023 at 15:22

1 Answer 1


This solution somewhat a hack. From what I can find; better to open a ticket on Github.

Best would likely be some mapping on the popup. Have had a look at the codebase but can not find any option for it. It might be that coc-sh should return some sort of URL property that then could be mapped. (The two-space indentation on the code also makes my eyes cry ;) )

Anyhow, one way is to create a function for it. This example is very simple in regards to error checks etc. Basically:

  1. Get popup list
  2. Grab first and get buffer for that one
  3. Get last line of that buffer (URL)
  4. Open the URL by what ever means one want.
  5. Create a mapping

Here mapping is to K with open as popup. Both popup and term require w3m. Lynx and the like should work fine as well. Else one can use coc#ui#open_url which defaults to system defaults like open, xdg-open and cmd /c start. Can be set by g:coc_open_url_command, or you could of course make your own.

"" Open URL by desired method.
function! s:open_url(url, method)
    if a:method == 'popup'
        let doc = system('w3m -dump ' .. a:url)->split('\n')
        call popup_create(doc, #{
            \ close: 'button', padding: [], title: a:url
        \ })
    elseif a:method == 'term'
        exec 'term ++close w3m ' .. a:url
        call coc#ui#open_url(a:url)

"" Extract URL from last line of popup.
function! s:open_shellcheckurl(method)
    "" get a list of popup window IDs
    let list = popup_list()
    if list->len()
        "" Get buffer number of first popup,
        "" Get last line of that buffer
        let url = list[0]->winbufnr()->getbufoneline('$')
        "" Sanitize
        let url = url->matchstr('https\?:\/\/[[:alnum:]%\/_#.-]*')
        "" Open URL if not empty
        if url != ''
            call s:open_url(url, a:method)

"" Mapping
nnoremap K :call <SID>open_shellcheckurl('popup')<CR>

This require of course that the link is at last line of the popup. If there are more links, or not at last line etc. this could be fixed; but the basics of it should be the same.

Have not found any public variable or function to get the popup window-ID or buffer-number in the source code.

Addition: To make the identification of the popup window more safe one could do something like this. Instead of grabbing first popup, look for a popup at cursor position:

function! s:open_shellcheckurl(method)
    "" Get position of current window
    let win_pos = win_screenpos(0)
    "" Add line / column to position
    let pos = [winline() + win_pos[0], wincol() + win_pos[1]]
    "" Get popup at that position
    let win_id = popup_locate(pos[0], pos[1])
    "" If any found, proceed
    if win_id > 0
        let url = win_id->winbufnr()->getbufoneline('$')
        let url = url->matchstr('https\?:\/\/[[:alnum:]%\/_#.-]*')
        if url != ''
            call s:open_url(url, a:method)

Note that I tried with let pos = [screenrow(), screencol()] but the numbers are off.

  • This is exactly the sort of "hack" I was trying to implement. The key thing I was missing was popup_list(). Thank you for taking the time to look into this!
    – Pak
    Commented Dec 22, 2023 at 22:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.