`synstack()` equivalent for extmarks-based highlighting?

The excellent Scriptease plugin by Tim Pope has a key binding zS that prints the names of all the syntax highlighting groups under the cursor. The implementation is here; it uses synstack() to get the list of highlight groups active at the current cursor position, and then map() + synIDattr() to get the name of each, something like this:

let l:syn_ids = synstack(row('.'), col('.'))
let l:syn_names = map(syn_ids, {i,s -> hlIDattr(s, 'name')})

However, it doesn’t appear to work with highlight groups set by nvim_buf_add_highlight, making it useless for debugging things like LSP semantic tokens and Tree Sitter highlighting. You can test this easily on a plain text file: if you nvim_buf_add_highlight(), you should see the color of the highlighted text change according to whatever you set, but the zS command over the highlighted text will not emit any output.

Specifically, the issue seems to be that synstack() is entirely unaware of these extra extmarks-based (?) syntax highlighting groups. Is there some way to make it aware of them? If not, what is the equivalent functionality?

Thanks to @bfredl, the equivalent functionality involves:

  • Use nvim_get_namespaces() to get a list of all (non-anonymous) extmark namespaces
  • Use nvim_buf_get_extmarks() to get a list of extmarks at a particular location in a particular buffer and namespace; pass the {'details': 1} option to get highlight information along with each extmark.

Example implementation:

function! GetExtmarkDetailsAt(bufnr, ns_id, pos) abort
  let l:row = line(a:pos)
  let l:col = col(a:pos)
  return nvim_buf_get_extmarks(a:bufnr, a:ns_id, [l:row-1, l:col-1], [l:row, l:col], {'details': 1})
endfunction

function! ListExtHighlightsAtCursor()
  let l:namespaces = nvim_get_namespaces()
  let l:hl_groups = []
  for l:ns_id in values(l:namespaces)
    let l:extmarks = GetExtmarkDetailsAt(0, l:ns_id, '.')
    for l:extmark in l:extmarks
      let l:hl_groups += [l:extmark[3]['hl_group']]
    endfor
  endfor
  return l:hl_groups
endfunction

You can then concatenate this list with the one returned by Scriptease.

1 Like