mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 03:18:16 +00:00

This updates the extmark namespace to search for when filtering out semantic tokens to match the new namespace style recently introduced.
270 lines
7.8 KiB
Lua
270 lines
7.8 KiB
Lua
--- @class vim._inspector.Filter
|
|
--- @inlinedoc
|
|
---
|
|
--- Include syntax based highlight groups.
|
|
--- (default: `true`)
|
|
--- @field syntax boolean
|
|
---
|
|
--- Include treesitter based highlight groups.
|
|
--- (default: `true`)
|
|
--- @field treesitter boolean
|
|
---
|
|
--- Include extmarks. When `all`, then extmarks without a `hl_group` will also be included.
|
|
--- (default: true)
|
|
--- @field extmarks boolean|"all"
|
|
---
|
|
--- Include semantic token highlights.
|
|
--- (default: true)
|
|
--- @field semantic_tokens boolean
|
|
local defaults = {
|
|
syntax = true,
|
|
treesitter = true,
|
|
extmarks = true,
|
|
semantic_tokens = true,
|
|
}
|
|
|
|
---Get all the items at a given buffer position.
|
|
---
|
|
---Can also be pretty-printed with `:Inspect!`. [:Inspect!]()
|
|
---
|
|
---@since 11
|
|
---@param bufnr? integer defaults to the current buffer
|
|
---@param row? integer row to inspect, 0-based. Defaults to the row of the current cursor
|
|
---@param col? integer col to inspect, 0-based. Defaults to the col of the current cursor
|
|
---@param filter? vim._inspector.Filter Table with key-value pairs to filter the items
|
|
---@return {treesitter:table,syntax:table,extmarks:table,semantic_tokens:table,buffer:integer,col:integer,row:integer} (table) a table with the following key-value pairs. Items are in "traversal order":
|
|
--- - treesitter: a list of treesitter captures
|
|
--- - syntax: a list of syntax groups
|
|
--- - semantic_tokens: a list of semantic tokens
|
|
--- - extmarks: a list of extmarks
|
|
--- - buffer: the buffer used to get the items
|
|
--- - row: the row used to get the items
|
|
--- - col: the col used to get the items
|
|
function vim.inspect_pos(bufnr, row, col, filter)
|
|
filter = vim.tbl_deep_extend('force', defaults, filter or {})
|
|
|
|
bufnr = bufnr or 0
|
|
if row == nil or col == nil then
|
|
-- get the row/col from the first window displaying the buffer
|
|
local win = bufnr == 0 and vim.api.nvim_get_current_win() or vim.fn.bufwinid(bufnr)
|
|
if win == -1 then
|
|
error('row/col is required for buffers not visible in a window')
|
|
end
|
|
local cursor = vim.api.nvim_win_get_cursor(win)
|
|
row, col = cursor[1] - 1, cursor[2]
|
|
end
|
|
bufnr = vim._resolve_bufnr(bufnr)
|
|
|
|
local results = {
|
|
treesitter = {}, --- @type table[]
|
|
syntax = {}, --- @type table[]
|
|
extmarks = {},
|
|
semantic_tokens = {},
|
|
buffer = bufnr,
|
|
row = row,
|
|
col = col,
|
|
}
|
|
|
|
-- resolve hl links
|
|
local function resolve_hl(data)
|
|
if data.hl_group then
|
|
local hlid = vim.api.nvim_get_hl_id_by_name(data.hl_group)
|
|
local name = vim.fn.synIDattr(vim.fn.synIDtrans(hlid), 'name')
|
|
data.hl_group_link = name
|
|
end
|
|
return data
|
|
end
|
|
|
|
-- treesitter
|
|
if filter.treesitter then
|
|
for _, capture in pairs(vim.treesitter.get_captures_at_pos(bufnr, row, col)) do
|
|
capture.hl_group = '@' .. capture.capture .. '.' .. capture.lang
|
|
results.treesitter[#results.treesitter + 1] = resolve_hl(capture)
|
|
end
|
|
end
|
|
|
|
-- syntax
|
|
if filter.syntax and vim.api.nvim_buf_is_valid(bufnr) then
|
|
vim._with({ buf = bufnr }, function()
|
|
for _, i1 in ipairs(vim.fn.synstack(row + 1, col + 1)) do
|
|
results.syntax[#results.syntax + 1] =
|
|
resolve_hl({ hl_group = vim.fn.synIDattr(i1, 'name') })
|
|
end
|
|
end)
|
|
end
|
|
|
|
-- namespace id -> name map
|
|
local nsmap = {} --- @type table<integer,string>
|
|
for name, id in pairs(vim.api.nvim_get_namespaces()) do
|
|
nsmap[id] = name
|
|
end
|
|
|
|
--- Convert an extmark tuple into a table
|
|
local function to_map(extmark)
|
|
extmark = {
|
|
id = extmark[1],
|
|
row = extmark[2],
|
|
col = extmark[3],
|
|
opts = resolve_hl(extmark[4]),
|
|
}
|
|
extmark.ns_id = extmark.opts.ns_id
|
|
extmark.ns = nsmap[extmark.ns_id] or ''
|
|
extmark.end_row = extmark.opts.end_row or extmark.row -- inclusive
|
|
extmark.end_col = extmark.opts.end_col or (extmark.col + 1) -- exclusive
|
|
return extmark
|
|
end
|
|
|
|
--- Check if an extmark overlaps this position
|
|
local function is_here(extmark)
|
|
return (row >= extmark.row and row <= extmark.end_row) -- within the rows of the extmark
|
|
and (row > extmark.row or col >= extmark.col) -- either not the first row, or in range of the col
|
|
and (row < extmark.end_row or col < extmark.end_col) -- either not in the last row or in range of the col
|
|
end
|
|
|
|
-- all extmarks at this position
|
|
local extmarks = vim.api.nvim_buf_get_extmarks(bufnr, -1, 0, -1, { details = true })
|
|
extmarks = vim.tbl_map(to_map, extmarks)
|
|
extmarks = vim.tbl_filter(is_here, extmarks)
|
|
|
|
if filter.semantic_tokens then
|
|
results.semantic_tokens = vim.tbl_filter(function(extmark)
|
|
return extmark.ns:find('nvim.lsp.semantic_tokens') == 1
|
|
end, extmarks)
|
|
end
|
|
|
|
if filter.extmarks then
|
|
results.extmarks = vim.tbl_filter(function(extmark)
|
|
return extmark.ns:find('nvim.lsp.semantic_tokens') ~= 1
|
|
and (filter.extmarks == 'all' or extmark.opts.hl_group)
|
|
end, extmarks)
|
|
end
|
|
|
|
return results
|
|
end
|
|
|
|
---Show all the items at a given buffer position.
|
|
---
|
|
---Can also be shown with `:Inspect`. [:Inspect]()
|
|
---
|
|
---Example: To bind this function to the vim-scriptease
|
|
---inspired `zS` in Normal mode:
|
|
---
|
|
---```lua
|
|
---vim.keymap.set('n', 'zS', vim.show_pos)
|
|
---```
|
|
---
|
|
---@since 11
|
|
---@param bufnr? integer defaults to the current buffer
|
|
---@param row? integer row to inspect, 0-based. Defaults to the row of the current cursor
|
|
---@param col? integer col to inspect, 0-based. Defaults to the col of the current cursor
|
|
---@param filter? vim._inspector.Filter
|
|
function vim.show_pos(bufnr, row, col, filter)
|
|
local items = vim.inspect_pos(bufnr, row, col, filter)
|
|
|
|
local lines = { {} }
|
|
|
|
local function append(str, hl)
|
|
table.insert(lines[#lines], { str, hl })
|
|
end
|
|
|
|
local function nl()
|
|
table.insert(lines, {})
|
|
end
|
|
|
|
local function item(data, comment)
|
|
append(' - ')
|
|
append(data.hl_group, data.hl_group)
|
|
append(' ')
|
|
if data.hl_group ~= data.hl_group_link then
|
|
append('links to ', 'MoreMsg')
|
|
append(data.hl_group_link, data.hl_group_link)
|
|
append(' ')
|
|
end
|
|
if comment then
|
|
append(comment, 'Comment')
|
|
end
|
|
nl()
|
|
end
|
|
|
|
-- treesitter
|
|
if #items.treesitter > 0 then
|
|
append('Treesitter', 'Title')
|
|
nl()
|
|
for _, capture in ipairs(items.treesitter) do
|
|
item(
|
|
capture,
|
|
string.format(
|
|
'priority: %d language: %s',
|
|
capture.metadata.priority or vim.hl.priorities.treesitter,
|
|
capture.lang
|
|
)
|
|
)
|
|
end
|
|
nl()
|
|
end
|
|
|
|
-- semantic tokens
|
|
if #items.semantic_tokens > 0 then
|
|
append('Semantic Tokens', 'Title')
|
|
nl()
|
|
local sorted_marks = vim.fn.sort(items.semantic_tokens, function(left, right)
|
|
local left_first = left.opts.priority < right.opts.priority
|
|
or left.opts.priority == right.opts.priority and left.opts.hl_group < right.opts.hl_group
|
|
return left_first and -1 or 1
|
|
end)
|
|
for _, extmark in ipairs(sorted_marks) do
|
|
item(extmark.opts, 'priority: ' .. extmark.opts.priority)
|
|
end
|
|
nl()
|
|
end
|
|
|
|
-- syntax
|
|
if #items.syntax > 0 then
|
|
append('Syntax', 'Title')
|
|
nl()
|
|
for _, syn in ipairs(items.syntax) do
|
|
item(syn)
|
|
end
|
|
nl()
|
|
end
|
|
|
|
-- extmarks
|
|
if #items.extmarks > 0 then
|
|
append('Extmarks', 'Title')
|
|
nl()
|
|
for _, extmark in ipairs(items.extmarks) do
|
|
if extmark.opts.hl_group then
|
|
item(extmark.opts, extmark.ns)
|
|
else
|
|
append(' - ')
|
|
append(extmark.ns, 'Comment')
|
|
nl()
|
|
end
|
|
end
|
|
nl()
|
|
end
|
|
|
|
if #lines[#lines] == 0 then
|
|
table.remove(lines)
|
|
end
|
|
|
|
local chunks = {}
|
|
for _, line in ipairs(lines) do
|
|
vim.list_extend(chunks, line)
|
|
table.insert(chunks, { '\n' })
|
|
end
|
|
if #chunks == 0 then
|
|
chunks = {
|
|
{
|
|
'No items found at position '
|
|
.. items.row
|
|
.. ','
|
|
.. items.col
|
|
.. ' in buffer '
|
|
.. items.buffer,
|
|
},
|
|
}
|
|
end
|
|
vim.api.nvim_echo(chunks, false, {})
|
|
end
|