2022-12-14 10:46:54 +01: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)
---@field semantic_tokens boolean include semantic tokens (defaults to true)
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 22:05:46 +09: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 10:46:54 +01: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 22:05:46 +09: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 10:46:54 +01: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
---@private
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 12:12:08 +01:00
capture.hl_group = ' @ ' .. capture.capture .. ' . ' .. capture.lang
2022-12-14 10:46:54 +01:00
table.insert ( results.treesitter , resolve_hl ( capture ) )
end
end
-- syntax
if filter.syntax then
for _ , i1 in ipairs ( vim.fn . synstack ( row + 1 , col + 1 ) ) do
table.insert ( results.syntax , resolve_hl ( { hl_group = vim.fn . synIDattr ( i1 , ' name ' ) } ) )
end
end
-- semantic tokens
if filter.semantic_tokens then
for _ , token in ipairs ( vim.lsp . semantic_tokens.get_at_pos ( bufnr , row , col ) or { } ) do
token.hl_groups = {
type = resolve_hl ( { hl_group = ' @ ' .. token.type } ) ,
modifiers = vim.tbl_map ( function ( modifier )
return resolve_hl ( { hl_group = ' @ ' .. modifier } )
end , token.modifiers or { } ) ,
}
table.insert ( results.semantic_tokens , token )
end
end
-- extmarks
if filter.extmarks then
for ns , nsid in pairs ( vim.api . nvim_get_namespaces ( ) ) do
if ns : find ( ' vim_lsp_semantic_tokens ' ) ~= 1 then
local extmarks = vim.api . nvim_buf_get_extmarks ( bufnr , nsid , 0 , - 1 , { details = true } )
for _ , extmark in ipairs ( extmarks ) do
extmark = {
ns_id = nsid ,
ns = ns ,
id = extmark [ 1 ] ,
row = extmark [ 2 ] ,
col = extmark [ 3 ] ,
opts = resolve_hl ( extmark [ 4 ] ) ,
}
local end_row = extmark.opts . end_row or extmark.row -- inclusive
local end_col = extmark.opts . end_col or ( extmark.col + 1 ) -- exclusive
if
( filter.extmarks == ' all ' or extmark.opts . hl_group ) -- filter hl_group
and ( row >= extmark.row and row <= 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 < end_row or col < end_col ) -- either not in the last row or in range of the col
then
table.insert ( results.extmarks , extmark )
end
end
end
end
end
return results
end
---Show all the items at a given buffer position.
---
---Can also be shown with `:Inspect`. *:Inspect*
---
2023-03-04 22:05:46 +09: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 10:46:54 +01: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 = { { } }
---@private
local function append ( str , hl )
table.insert ( lines [ # lines ] , { str , hl } )
end
---@private
local function nl ( )
table.insert ( lines , { } )
end
---@private
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
if # items.semantic_tokens > 0 then
append ( ' Semantic Tokens ' , ' Title ' )
nl ( )
for _ , token in ipairs ( items.semantic_tokens ) do
local client = vim.lsp . get_client_by_id ( token.client_id )
client = client and ( ' ( ' .. client.name .. ' ) ' ) or ' '
item ( token.hl_groups . type , ' type ' .. client )
for _ , modifier in ipairs ( token.hl_groups . modifiers ) do
item ( modifier , ' modifier ' .. client )
end
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