123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- --- @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('vim_lsp_semantic_tokens') == 1
- end, extmarks)
- end
- if filter.extmarks then
- results.extmarks = vim.tbl_filter(function(extmark)
- return extmark.ns:find('vim_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
|