2022-12-14 02:46:54 -07:00
---@class InspectorFilter
---@field syntax boolean include syntax based highlight groups (defaults to true)
---@field treesitter boolean include treesitter based highlight groups (defaults to true)
---@field extmarks boolean|"all" include extmarks. When `all`, then extmarks without a `hl_group` will also be included (defaults to true)
2023-03-06 11:03:13 -07:00
---@field semantic_tokens boolean include semantic token highlights (defaults to true)
2022-12-14 02:46:54 -07:00
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!*
---
2023-03-04 06:05:46 -07:00
---@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
2022-12-14 02:46:54 -07:00
---@param filter? InspectorFilter (table|nil) a table with key-value pairs to filter the items
--- - syntax (boolean): include syntax based highlight groups (defaults to true)
--- - treesitter (boolean): include treesitter based highlight groups (defaults to true)
--- - extmarks (boolean|"all"): include extmarks. When `all`, then extmarks without a `hl_group` will also be included (defaults to true)
--- - semantic_tokens (boolean): include semantic tokens (defaults to true)
2023-03-04 06:05:46 -07:00
---@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":
2022-12-14 02:46:54 -07:00
--- - 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 = bufnr == 0 and vim.api . nvim_get_current_buf ( ) or bufnr
local results = {
treesitter = { } ,
syntax = { } ,
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
2023-02-28 04:12:08 -07:00
capture.hl_group = ' @ ' .. capture.capture .. ' . ' .. capture.lang
2023-04-16 02:50:32 -07:00
results.treesitter [ # results.treesitter + 1 ] = resolve_hl ( capture )
2022-12-14 02:46:54 -07:00
end
end
-- syntax
2023-04-16 02:50:32 -07:00
if filter.syntax and vim.api . nvim_buf_is_valid ( bufnr ) then
vim.api . nvim_buf_call ( 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 )
2022-12-14 02:46:54 -07:00
end
2023-03-25 07:19:22 -07:00
-- namespace id -> name map
local nsmap = { }
for name , id in pairs ( vim.api . nvim_get_namespaces ( ) ) do
nsmap [ id ] = name
end
2023-05-13 12:33:22 -07:00
--- Convert an extmark tuple into a table
2023-03-06 11:03:13 -07:00
local function to_map ( extmark )
extmark = {
id = extmark [ 1 ] ,
row = extmark [ 2 ] ,
col = extmark [ 3 ] ,
opts = resolve_hl ( extmark [ 4 ] ) ,
}
2023-03-25 07:19:22 -07:00
extmark.ns_id = extmark.opts . ns_id
extmark.ns = nsmap [ extmark.ns_id ] or ' '
2023-03-06 11:03:13 -07:00
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
2023-03-25 07:19:22 -07:00
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 )
2023-03-06 11:03:13 -07:00
if filter.semantic_tokens then
results.semantic_tokens = vim.tbl_filter ( function ( extmark )
return extmark.ns : find ( ' vim_lsp_semantic_tokens ' ) == 1
end , extmarks )
2022-12-14 02:46:54 -07:00
end
if filter.extmarks then
2023-03-06 11:03:13 -07:00
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 )
2022-12-14 02:46:54 -07:00
end
2023-03-06 11:03:13 -07:00
2022-12-14 02:46:54 -07:00
return results
end
---Show all the items at a given buffer position.
---
---Can also be shown with `:Inspect`. *:Inspect*
---
2023-03-04 06:05:46 -07:00
---@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
2022-12-14 02:46:54 -07:00
---@param filter? InspectorFilter (table|nil) see |vim.inspect_pos()|
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 , capture.lang )
end
nl ( )
end
2023-03-06 11:03:13 -07:00
-- semantic tokens
2022-12-14 02:46:54 -07:00
if # items.semantic_tokens > 0 then
append ( ' Semantic Tokens ' , ' Title ' )
nl ( )
2023-03-06 11:03:13 -07:00
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 )
2022-12-14 02:46:54 -07:00
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
2023-03-06 11:03:13 -07:00
2022-12-14 02:46:54 -07:00
-- 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