From 9a5678463c96baf3b39cb3083ddf0da87d39aa23 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sat, 4 Feb 2023 14:58:38 +0000 Subject: fix(treesitter): fix most diagnostics --- runtime/lua/vim/treesitter/playground.lua | 36 ++++++++++++++++--------------- 1 file changed, 19 insertions(+), 17 deletions(-) (limited to 'runtime/lua/vim/treesitter/playground.lua') diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua index bb073290c6..be7764e6f0 100644 --- a/runtime/lua/vim/treesitter/playground.lua +++ b/runtime/lua/vim/treesitter/playground.lua @@ -1,12 +1,13 @@ local api = vim.api -local M = {} - ----@class Playground +---@class TSPlayground ---@field ns number API namespace ---@field opts table Options table with the following keys: --- - anon (boolean): If true, display anonymous nodes --- - lang (boolean): If true, display the language alongside each node +---@field nodes Node[] +---@field named Node[] +local TSPlayground = {} --- ---@class Node ---@field id number Node id @@ -18,6 +19,7 @@ local M = {} ---@field end_lnum number Final line number of this node in the source buffer ---@field end_col number Final column number of this node in the source buffer ---@field lang string Source language of this node +---@field root TSNode --- Traverse all child nodes starting at {node}. --- @@ -31,10 +33,10 @@ local M = {} --- node of each of these trees is contained within a node in the primary tree. The {injections} --- table maps nodes in the primary tree to root nodes of injected trees. --- ----@param node userdata Starting node to begin traversal |tsnode| +---@param node TSNode Starting node to begin traversal |tsnode| ---@param depth number Current recursion depth ---@param lang string Language of the tree currently being traversed ----@param injections table Mapping of node ids to root nodes of injected language trees (see +---@param injections table Mapping of node ids to root nodes of injected language trees (see --- explanation above) ---@param tree Node[] Output table containing a list of tables each representing a node in the tree ---@private @@ -48,7 +50,7 @@ local function traverse(node, depth, lang, injections, tree) local type = child:type() local lnum, col, end_lnum, end_col = child:range() local named = child:named() - local text + local text ---@type string if named then if field then text = string.format('%s: (%s)', field, type) @@ -79,14 +81,14 @@ end --- Create a new Playground object. --- ----@param bufnr number Source buffer number +---@param bufnr integer Source buffer number ---@param lang string|nil Language of source buffer --- ----@return Playground|nil +---@return TSPlayground|nil ---@return string|nil Error message, if any --- ---@private -function M.new(self, bufnr, lang) +function TSPlayground:new(bufnr, lang) local ok, parser = pcall(vim.treesitter.get_parser, bufnr or 0, lang) if not ok then return nil, 'No parser available for the given buffer' @@ -96,7 +98,7 @@ function M.new(self, bufnr, lang) -- the primary tree that contains that root. Add a mapping from the node in the primary tree to -- the root in the child tree to the {injections} table. local root = parser:parse()[1]:root() - local injections = {} + local injections = {} ---@type table parser:for_each_child(function(child, lang_) child:for_each_tree(function(tree) local r = tree:root() @@ -112,7 +114,7 @@ function M.new(self, bufnr, lang) local nodes = traverse(root, 0, parser:lang(), injections, {}) - local named = {} + local named = {} ---@type Node[] for _, v in ipairs(nodes) do if v.named then named[#named + 1] = v @@ -138,9 +140,9 @@ end --- ---@param bufnr number Buffer number to write into. ---@private -function M.draw(self, bufnr) +function TSPlayground:draw(bufnr) vim.bo[bufnr].modifiable = true - local lines = {} + local lines = {} ---@type string[] for _, item in self:iter() do lines[#lines + 1] = table.concat({ string.rep(' ', item.depth), @@ -168,19 +170,19 @@ end ---@param i number Node number to get ---@return Node ---@private -function M.get(self, i) +function TSPlayground:get(i) local t = self.opts.anon and self.nodes or self.named return t[i] end --- Iterate over all of the nodes in this Playground object. --- ----@return function Iterator over all nodes in this Playground +---@return (fun(): integer, Node) Iterator over all nodes in this Playground ---@return table ---@return number ---@private -function M.iter(self) +function TSPlayground:iter() return ipairs(self.opts.anon and self.nodes or self.named) end -return M +return TSPlayground -- cgit From f5bad01869df449cb1e4ae7f264bcd5c8150f606 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 9 Feb 2023 15:20:47 +0000 Subject: feat(treesitter): playground improvements - Render node ranges as virtual text - Set filettype=query. The virtual text is to avoid parsing errors. - Make sure highlights text is always in view. --- runtime/lua/vim/treesitter/playground.lua | 36 +++++++++++++++++++------------ 1 file changed, 22 insertions(+), 14 deletions(-) (limited to 'runtime/lua/vim/treesitter/playground.lua') diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua index be7764e6f0..001bc2d5bf 100644 --- a/runtime/lua/vim/treesitter/playground.lua +++ b/runtime/lua/vim/treesitter/playground.lua @@ -136,6 +136,8 @@ function TSPlayground:new(bufnr, lang) return t end +local decor_ns = api.nvim_create_namespace('ts.playground') + --- Write the contents of this Playground into {bufnr}. --- ---@param bufnr number Buffer number to write into. @@ -144,22 +146,28 @@ function TSPlayground:draw(bufnr) vim.bo[bufnr].modifiable = true local lines = {} ---@type string[] for _, item in self:iter() do - lines[#lines + 1] = table.concat({ - string.rep(' ', item.depth), - item.text, - item.lnum == item.end_lnum - and string.format(' [%d:%d-%d]', item.lnum + 1, item.col + 1, item.end_col) - or string.format( - ' [%d:%d-%d:%d]', - item.lnum + 1, - item.col + 1, - item.end_lnum + 1, - item.end_col - ), - self.opts.lang and string.format(' %s', item.lang) or '', - }) + lines[#lines + 1] = string.rep(' ', item.depth) .. item.text end api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) + + api.nvim_buf_clear_namespace(bufnr, decor_ns, 0, -1) + + for i, item in self:iter() do + local range_str + if item.lnum == item.end_lnum then + range_str = string.format('[%d:%d-%d]', item.lnum + 1, item.col + 1, item.end_col) + else + range_str = + string.format('[%d:%d-%d:%d]', item.lnum + 1, item.col + 1, item.end_lnum + 1, item.end_col) + end + + local lang_str = self.opts.lang and string.format(' %s', item.lang) or '' + + api.nvim_buf_set_extmark(bufnr, decor_ns, i - 1, 0, { + virt_text = { { range_str, 'Comment' }, { lang_str, 'Title' } }, + }) + end + vim.bo[bufnr].modifiable = false end -- cgit From da56f06037c26180021c1e3b73a77fc990f0c1e3 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Mon, 27 Feb 2023 10:49:19 +0000 Subject: fix(treesitter): remove virtual text from playground Implement the range and lang annotations as comments instead --- runtime/lua/vim/treesitter/playground.lua | 46 +++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 14 deletions(-) (limited to 'runtime/lua/vim/treesitter/playground.lua') diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua index 001bc2d5bf..fd5b687195 100644 --- a/runtime/lua/vim/treesitter/playground.lua +++ b/runtime/lua/vim/treesitter/playground.lua @@ -138,6 +138,19 @@ end local decor_ns = api.nvim_create_namespace('ts.playground') +---@private +---@param lnum integer +---@param col integer +---@param end_lnum integer +---@param end_col integer +---@return string +local function get_range_str(lnum, col, end_col, end_lnum) + if lnum == end_lnum then + return string.format('[%d:%d-%d]', lnum + 1, col + 1, end_col) + end + return string.format('[%d:%d-%d:%d]', lnum + 1, col + 1, end_lnum + 1, end_col) +end + --- Write the contents of this Playground into {bufnr}. --- ---@param bufnr number Buffer number to write into. @@ -145,26 +158,31 @@ local decor_ns = api.nvim_create_namespace('ts.playground') function TSPlayground:draw(bufnr) vim.bo[bufnr].modifiable = true local lines = {} ---@type string[] + local lang_hl_marks = {} ---@type table[] + for _, item in self:iter() do - lines[#lines + 1] = string.rep(' ', item.depth) .. item.text + local range_str = get_range_str(item.lnum, item.col, item.end_lnum, item.end_col) + local lang_str = self.opts.lang and string.format(' %s', item.lang) or '' + local line = string.rep(' ', item.depth) .. item.text .. '; ' .. range_str .. lang_str + + if self.opts.lang then + lang_hl_marks[#lang_hl_marks + 1] = { + col = #line - #lang_str, + end_col = #line, + } + end + + lines[#lines + 1] = line end + api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) api.nvim_buf_clear_namespace(bufnr, decor_ns, 0, -1) - for i, item in self:iter() do - local range_str - if item.lnum == item.end_lnum then - range_str = string.format('[%d:%d-%d]', item.lnum + 1, item.col + 1, item.end_col) - else - range_str = - string.format('[%d:%d-%d:%d]', item.lnum + 1, item.col + 1, item.end_lnum + 1, item.end_col) - end - - local lang_str = self.opts.lang and string.format(' %s', item.lang) or '' - - api.nvim_buf_set_extmark(bufnr, decor_ns, i - 1, 0, { - virt_text = { { range_str, 'Comment' }, { lang_str, 'Title' } }, + for i, m in ipairs(lang_hl_marks) do + api.nvim_buf_set_extmark(bufnr, decor_ns, i - 1, m.col, { + hl_group = 'Title', + end_col = m.end_col, }) end -- cgit From 86ff239240e955ef6da95bc9c8814cfd4492f5aa Mon Sep 17 00:00:00 2001 From: Gregory Anders Date: Thu, 2 Mar 2023 14:15:18 -0700 Subject: refactor(treesitter): use string.format to create lines --- runtime/lua/vim/treesitter/playground.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'runtime/lua/vim/treesitter/playground.lua') diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua index fd5b687195..992433961f 100644 --- a/runtime/lua/vim/treesitter/playground.lua +++ b/runtime/lua/vim/treesitter/playground.lua @@ -146,9 +146,9 @@ local decor_ns = api.nvim_create_namespace('ts.playground') ---@return string local function get_range_str(lnum, col, end_col, end_lnum) if lnum == end_lnum then - return string.format('[%d:%d-%d]', lnum + 1, col + 1, end_col) + return string.format('[%d:%d - %d]', lnum + 1, col + 1, end_col) end - return string.format('[%d:%d-%d:%d]', lnum + 1, col + 1, end_lnum + 1, end_col) + return string.format('[%d:%d - %d:%d]', lnum + 1, col + 1, end_lnum + 1, end_col) end --- Write the contents of this Playground into {bufnr}. @@ -163,7 +163,8 @@ function TSPlayground:draw(bufnr) for _, item in self:iter() do local range_str = get_range_str(item.lnum, item.col, item.end_lnum, item.end_col) local lang_str = self.opts.lang and string.format(' %s', item.lang) or '' - local line = string.rep(' ', item.depth) .. item.text .. '; ' .. range_str .. lang_str + local line = + string.format('%s%s ; %s%s', string.rep(' ', item.depth), item.text, range_str, lang_str) if self.opts.lang then lang_hl_marks[#lang_hl_marks + 1] = { -- cgit From 128b82103ba477482bdfaf10ec71e0986876cca1 Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Sat, 4 Mar 2023 22:04:05 +0900 Subject: docs(treesitter): number → integer (#22513) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- runtime/lua/vim/treesitter/playground.lua | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'runtime/lua/vim/treesitter/playground.lua') diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua index 992433961f..7f181c23fd 100644 --- a/runtime/lua/vim/treesitter/playground.lua +++ b/runtime/lua/vim/treesitter/playground.lua @@ -1,7 +1,7 @@ local api = vim.api ---@class TSPlayground ----@field ns number API namespace +---@field ns integer API namespace ---@field opts table Options table with the following keys: --- - anon (boolean): If true, display anonymous nodes --- - lang (boolean): If true, display the language alongside each node @@ -10,14 +10,14 @@ local api = vim.api local TSPlayground = {} --- ---@class Node ----@field id number Node id +---@field id integer Node id ---@field text string Node text ---@field named boolean True if this is a named (non-anonymous) node ----@field depth number Depth of the node within the tree ----@field lnum number Beginning line number of this node in the source buffer ----@field col number Beginning column number of this node in the source buffer ----@field end_lnum number Final line number of this node in the source buffer ----@field end_col number Final column number of this node in the source buffer +---@field depth integer Depth of the node within the tree +---@field lnum integer Beginning line number of this node in the source buffer +---@field col integer Beginning column number of this node in the source buffer +---@field end_lnum integer Final line number of this node in the source buffer +---@field end_col integer Final column number of this node in the source buffer ---@field lang string Source language of this node ---@field root TSNode @@ -34,7 +34,7 @@ local TSPlayground = {} --- table maps nodes in the primary tree to root nodes of injected trees. --- ---@param node TSNode Starting node to begin traversal |tsnode| ----@param depth number Current recursion depth +---@param depth integer Current recursion depth ---@param lang string Language of the tree currently being traversed ---@param injections table Mapping of node ids to root nodes of injected language trees (see --- explanation above) @@ -153,7 +153,7 @@ end --- Write the contents of this Playground into {bufnr}. --- ----@param bufnr number Buffer number to write into. +---@param bufnr integer Buffer number to write into. ---@private function TSPlayground:draw(bufnr) vim.bo[bufnr].modifiable = true @@ -194,7 +194,7 @@ end --- --- The node number is dependent on whether or not anonymous nodes are displayed. --- ----@param i number Node number to get +---@param i integer Node number to get ---@return Node ---@private function TSPlayground:get(i) @@ -206,7 +206,7 @@ end --- ---@return (fun(): integer, Node) Iterator over all nodes in this Playground ---@return table ----@return number +---@return integer ---@private function TSPlayground:iter() return ipairs(self.opts.anon and self.nodes or self.named) -- cgit From 4e4203f71b0b9bb2ca4ad9abd2fbf4ea1deaf9a6 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Thu, 23 Mar 2023 11:23:51 +0000 Subject: fix(treesitter): annotations - Begin using `@package` in place of `@private` for functions that are accessed internally but outside their defined class. - Rename Node -> TSP.Node --- runtime/lua/vim/treesitter/playground.lua | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'runtime/lua/vim/treesitter/playground.lua') diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua index 7f181c23fd..35f06f5caf 100644 --- a/runtime/lua/vim/treesitter/playground.lua +++ b/runtime/lua/vim/treesitter/playground.lua @@ -5,11 +5,11 @@ local api = vim.api ---@field opts table Options table with the following keys: --- - anon (boolean): If true, display anonymous nodes --- - lang (boolean): If true, display the language alongside each node ----@field nodes Node[] ----@field named Node[] +---@field nodes TSP.Node[] +---@field named TSP.Node[] local TSPlayground = {} ---- ----@class Node + +---@class TSP.Node ---@field id integer Node id ---@field text string Node text ---@field named boolean True if this is a named (non-anonymous) node @@ -36,9 +36,9 @@ local TSPlayground = {} ---@param node TSNode Starting node to begin traversal |tsnode| ---@param depth integer Current recursion depth ---@param lang string Language of the tree currently being traversed ----@param injections table Mapping of node ids to root nodes of injected language trees (see +---@param injections table Mapping of node ids to root nodes of injected language trees (see --- explanation above) ----@param tree Node[] Output table containing a list of tables each representing a node in the tree +---@param tree TSP.Node[] Output table containing a list of tables each representing a node in the tree ---@private local function traverse(node, depth, lang, injections, tree) local injection = injections[node:id()] @@ -87,7 +87,7 @@ end ---@return TSPlayground|nil ---@return string|nil Error message, if any --- ----@private +---@package function TSPlayground:new(bufnr, lang) local ok, parser = pcall(vim.treesitter.get_parser, bufnr or 0, lang) if not ok then @@ -114,7 +114,7 @@ function TSPlayground:new(bufnr, lang) local nodes = traverse(root, 0, parser:lang(), injections, {}) - local named = {} ---@type Node[] + local named = {} ---@type TSP.Node[] for _, v in ipairs(nodes) do if v.named then named[#named + 1] = v @@ -154,7 +154,7 @@ end --- Write the contents of this Playground into {bufnr}. --- ---@param bufnr integer Buffer number to write into. ----@private +---@package function TSPlayground:draw(bufnr) vim.bo[bufnr].modifiable = true local lines = {} ---@type string[] @@ -195,8 +195,8 @@ end --- The node number is dependent on whether or not anonymous nodes are displayed. --- ---@param i integer Node number to get ----@return Node ----@private +---@return TSP.Node +---@package function TSPlayground:get(i) local t = self.opts.anon and self.nodes or self.named return t[i] @@ -204,10 +204,10 @@ end --- Iterate over all of the nodes in this Playground object. --- ----@return (fun(): integer, Node) Iterator over all nodes in this Playground +---@return (fun(): integer, TSP.Node) Iterator over all nodes in this Playground ---@return table ---@return integer ----@private +---@package function TSPlayground:iter() return ipairs(self.opts.anon and self.nodes or self.named) end -- cgit From b1de4820b7b1a527f4d0cf9a20192d92bea1d9c4 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Sat, 1 Apr 2023 12:55:04 +0100 Subject: refactor(treesitter): move inspect_tree impl --- runtime/lua/vim/treesitter/playground.lua | 225 +++++++++++++++++++++++++++++- 1 file changed, 224 insertions(+), 1 deletion(-) (limited to 'runtime/lua/vim/treesitter/playground.lua') diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua index 35f06f5caf..7eead14579 100644 --- a/runtime/lua/vim/treesitter/playground.lua +++ b/runtime/lua/vim/treesitter/playground.lua @@ -1,5 +1,8 @@ local api = vim.api +---@class TSPlaygroundModule +local M = {} + ---@class TSPlayground ---@field ns integer API namespace ---@field opts table Options table with the following keys: @@ -212,4 +215,224 @@ function TSPlayground:iter() return ipairs(self.opts.anon and self.nodes or self.named) end -return TSPlayground +--- @class InspectTreeOpts +--- @field lang string? The language of the source buffer. If omitted, the +--- filetype of the source buffer is used. +--- @field bufnr integer? Buffer to draw the tree into. If omitted, a new +--- buffer is created. +--- @field winid integer? Window id to display the tree buffer in. If omitted, +--- a new window is created with {command}. +--- @field command string? Vimscript command to create the window. Default +--- value is "60vnew". Only used when {winid} is nil. +--- @field title (string|fun(bufnr:integer):string|nil) Title of the window. If a +--- function, it accepts the buffer number of the source +--- buffer as its only argument and should return a string. + +--- @param opts InspectTreeOpts +function M.inspect_tree(opts) + vim.validate({ + opts = { opts, 't', true }, + }) + + opts = opts or {} + + local buf = api.nvim_get_current_buf() + local win = api.nvim_get_current_win() + local pg = assert(TSPlayground:new(buf, opts.lang)) + + -- Close any existing playground window + if vim.b[buf].playground then + local w = vim.b[buf].playground + if api.nvim_win_is_valid(w) then + api.nvim_win_close(w, true) + end + end + + local w = opts.winid + if not w then + vim.cmd(opts.command or '60vnew') + w = api.nvim_get_current_win() + end + + local b = opts.bufnr + if b then + api.nvim_win_set_buf(w, b) + else + b = api.nvim_win_get_buf(w) + end + + vim.b[buf].playground = w + + vim.wo[w].scrolloff = 5 + vim.wo[w].wrap = false + vim.wo[w].foldmethod = 'manual' -- disable folding + vim.bo[b].buflisted = false + vim.bo[b].buftype = 'nofile' + vim.bo[b].bufhidden = 'wipe' + vim.bo[b].filetype = 'query' + + local title --- @type string? + local opts_title = opts.title + if not opts_title then + local bufname = api.nvim_buf_get_name(buf) + title = string.format('Syntax tree for %s', vim.fn.fnamemodify(bufname, ':.')) + elseif type(opts_title) == 'function' then + title = opts_title(buf) + end + + assert(type(title) == 'string', 'Window title must be a string') + api.nvim_buf_set_name(b, title) + + pg:draw(b) + + api.nvim_buf_clear_namespace(buf, pg.ns, 0, -1) + api.nvim_buf_set_keymap(b, 'n', '', '', { + desc = 'Jump to the node under the cursor in the source buffer', + callback = function() + local row = api.nvim_win_get_cursor(w)[1] + local pos = pg:get(row) + api.nvim_set_current_win(win) + api.nvim_win_set_cursor(win, { pos.lnum + 1, pos.col }) + end, + }) + api.nvim_buf_set_keymap(b, 'n', 'a', '', { + desc = 'Toggle anonymous nodes', + callback = function() + local row, col = unpack(api.nvim_win_get_cursor(w)) + local curnode = pg:get(row) + while curnode and not curnode.named do + row = row - 1 + curnode = pg:get(row) + end + + pg.opts.anon = not pg.opts.anon + pg:draw(b) + + if not curnode then + return + end + + local id = curnode.id + for i, node in pg:iter() do + if node.id == id then + api.nvim_win_set_cursor(w, { i, col }) + break + end + end + end, + }) + api.nvim_buf_set_keymap(b, 'n', 'I', '', { + desc = 'Toggle language display', + callback = function() + pg.opts.lang = not pg.opts.lang + pg:draw(b) + end, + }) + + local group = api.nvim_create_augroup('treesitter/playground', {}) + + api.nvim_create_autocmd('CursorMoved', { + group = group, + buffer = b, + callback = function() + api.nvim_buf_clear_namespace(buf, pg.ns, 0, -1) + local row = api.nvim_win_get_cursor(w)[1] + local pos = pg:get(row) + api.nvim_buf_set_extmark(buf, pg.ns, pos.lnum, pos.col, { + end_row = pos.end_lnum, + end_col = math.max(0, pos.end_col), + hl_group = 'Visual', + }) + + local topline, botline = vim.fn.line('w0', win), vim.fn.line('w$', win) + + -- Move the cursor if highlighted range is completely out of view + if pos.lnum < topline and pos.end_lnum < topline then + api.nvim_win_set_cursor(win, { pos.end_lnum + 1, 0 }) + elseif pos.lnum > botline and pos.end_lnum > botline then + api.nvim_win_set_cursor(win, { pos.lnum + 1, 0 }) + end + end, + }) + + api.nvim_create_autocmd('CursorMoved', { + group = group, + buffer = buf, + callback = function() + if not api.nvim_buf_is_loaded(b) then + return true + end + + api.nvim_buf_clear_namespace(b, pg.ns, 0, -1) + + local cursor_node = vim.treesitter.get_node({ + bufnr = buf, + lang = opts.lang, + ignore_injections = false, + }) + if not cursor_node then + return + end + + local cursor_node_id = cursor_node:id() + for i, v in pg:iter() do + if v.id == cursor_node_id then + local start = v.depth + local end_col = start + #v.text + api.nvim_buf_set_extmark(b, pg.ns, i - 1, start, { + end_col = end_col, + hl_group = 'Visual', + }) + api.nvim_win_set_cursor(w, { i, 0 }) + break + end + end + end, + }) + + api.nvim_create_autocmd({ 'TextChanged', 'InsertLeave' }, { + group = group, + buffer = buf, + callback = function() + if not api.nvim_buf_is_loaded(b) then + return true + end + + pg = assert(TSPlayground:new(buf, opts.lang)) + pg:draw(b) + end, + }) + + api.nvim_create_autocmd('BufLeave', { + group = group, + buffer = b, + callback = function() + api.nvim_buf_clear_namespace(buf, pg.ns, 0, -1) + end, + }) + + api.nvim_create_autocmd('BufLeave', { + group = group, + buffer = buf, + callback = function() + if not api.nvim_buf_is_loaded(b) then + return true + end + + api.nvim_buf_clear_namespace(b, pg.ns, 0, -1) + end, + }) + + api.nvim_create_autocmd('BufHidden', { + group = group, + buffer = buf, + once = true, + callback = function() + if api.nvim_win_is_valid(w) then + api.nvim_win_close(w, true) + end + end, + }) +end + +return M -- cgit From e826d09c18ab8840592b6cdbbcfe3e311a047174 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Tue, 4 Apr 2023 23:37:46 +0200 Subject: fix(windows): consistent normalization in fs.find vim.fs.find(".luacheckrc") ``` c:\\projects\\neovim/.luacheckrc # before c:/projects/neovim/.luacheckrc # after ``` Co-authored-by: kylo252 <59826753+kylo252@users.noreply.github.com> --- runtime/lua/vim/treesitter/playground.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/treesitter/playground.lua') diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua index 7eead14579..2c0a0d1aa6 100644 --- a/runtime/lua/vim/treesitter/playground.lua +++ b/runtime/lua/vim/treesitter/playground.lua @@ -265,7 +265,7 @@ function M.inspect_tree(opts) vim.wo[w].scrolloff = 5 vim.wo[w].wrap = false - vim.wo[w].foldmethod = 'manual' -- disable folding + vim.wo[w].foldmethod = 'manual' -- disable folding vim.bo[b].buflisted = false vim.bo[b].buftype = 'nofile' vim.bo[b].bufhidden = 'wipe' -- cgit From ab2811746eb72e06309a9877dbe6cb70d9cd3b12 Mon Sep 17 00:00:00 2001 From: William <50717946+BIKA-C@users.noreply.github.com> Date: Thu, 20 Apr 2023 06:42:49 -0700 Subject: fix(treesitter playground): fix the wrong range of a node displayed i… (#23209) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix(treesitter playground): wrong range of a node displayed in playground The call parameters order of the function `get_range_str` is flipped for the last two arguments compared to the declaration. --- runtime/lua/vim/treesitter/playground.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/treesitter/playground.lua') diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua index 2c0a0d1aa6..c512710810 100644 --- a/runtime/lua/vim/treesitter/playground.lua +++ b/runtime/lua/vim/treesitter/playground.lua @@ -147,7 +147,7 @@ local decor_ns = api.nvim_create_namespace('ts.playground') ---@param end_lnum integer ---@param end_col integer ---@return string -local function get_range_str(lnum, col, end_col, end_lnum) +local function get_range_str(lnum, col, end_lnum, end_col) if lnum == end_lnum then return string.format('[%d:%d - %d]', lnum + 1, col + 1, end_col) end -- cgit From c194acbfc479d8e5839fa629363f93f6550d035c Mon Sep 17 00:00:00 2001 From: Stephan Seitz Date: Sat, 29 Apr 2023 18:22:26 +0200 Subject: feat(treesitter): add query_linter from nvim-treesitter/playground (#22784) Co-authored-by: clason Co-authored-by: lewis6991 --- runtime/lua/vim/treesitter/playground.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'runtime/lua/vim/treesitter/playground.lua') diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua index c512710810..8293c1bd0a 100644 --- a/runtime/lua/vim/treesitter/playground.lua +++ b/runtime/lua/vim/treesitter/playground.lua @@ -269,6 +269,7 @@ function M.inspect_tree(opts) vim.bo[b].buflisted = false vim.bo[b].buftype = 'nofile' vim.bo[b].bufhidden = 'wipe' + vim.b[b].disable_query_linter = true vim.bo[b].filetype = 'query' local title --- @type string? -- cgit From c48b1421af28d0317c807bca00c7e2fff97d9ad0 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 6 Jun 2023 08:23:20 -0700 Subject: refactor!: rename "playground" => "dev" #23919 Problem: "playground" is new jargon that overlaps with existing concepts: "dev" (`:help dev`) and "view" (also "scratch" `:help scratch-buffer`) . Solution: We should consistently use "dev" as the namespace for where "developer tools" live. For purposes of a "throwaway sandbox object", we can use the name "view". - Rename `TSPlayground` => `TSView` - Rename `playground.lua` => `dev.lua` --- runtime/lua/vim/treesitter/playground.lua | 439 ------------------------------ 1 file changed, 439 deletions(-) delete mode 100644 runtime/lua/vim/treesitter/playground.lua (limited to 'runtime/lua/vim/treesitter/playground.lua') diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/playground.lua deleted file mode 100644 index 8293c1bd0a..0000000000 --- a/runtime/lua/vim/treesitter/playground.lua +++ /dev/null @@ -1,439 +0,0 @@ -local api = vim.api - ----@class TSPlaygroundModule -local M = {} - ----@class TSPlayground ----@field ns integer API namespace ----@field opts table Options table with the following keys: ---- - anon (boolean): If true, display anonymous nodes ---- - lang (boolean): If true, display the language alongside each node ----@field nodes TSP.Node[] ----@field named TSP.Node[] -local TSPlayground = {} - ----@class TSP.Node ----@field id integer Node id ----@field text string Node text ----@field named boolean True if this is a named (non-anonymous) node ----@field depth integer Depth of the node within the tree ----@field lnum integer Beginning line number of this node in the source buffer ----@field col integer Beginning column number of this node in the source buffer ----@field end_lnum integer Final line number of this node in the source buffer ----@field end_col integer Final column number of this node in the source buffer ----@field lang string Source language of this node ----@field root TSNode - ---- Traverse all child nodes starting at {node}. ---- ---- This is a recursive function. The {depth} parameter indicates the current recursion level. ---- {lang} is a string indicating the language of the tree currently being traversed. Each traversed ---- node is added to {tree}. When recursion completes, {tree} is an array of all nodes in the order ---- they were visited. ---- ---- {injections} is a table mapping node ids from the primary tree to language tree injections. Each ---- injected language has a series of trees nested within the primary language's tree, and the root ---- node of each of these trees is contained within a node in the primary tree. The {injections} ---- table maps nodes in the primary tree to root nodes of injected trees. ---- ----@param node TSNode Starting node to begin traversal |tsnode| ----@param depth integer Current recursion depth ----@param lang string Language of the tree currently being traversed ----@param injections table Mapping of node ids to root nodes of injected language trees (see ---- explanation above) ----@param tree TSP.Node[] Output table containing a list of tables each representing a node in the tree ----@private -local function traverse(node, depth, lang, injections, tree) - local injection = injections[node:id()] - if injection then - traverse(injection.root, depth, injection.lang, injections, tree) - end - - for child, field in node:iter_children() do - local type = child:type() - local lnum, col, end_lnum, end_col = child:range() - local named = child:named() - local text ---@type string - if named then - if field then - text = string.format('%s: (%s)', field, type) - else - text = string.format('(%s)', type) - end - else - text = string.format('"%s"', type:gsub('\n', '\\n')) - end - - table.insert(tree, { - id = child:id(), - text = text, - named = named, - depth = depth, - lnum = lnum, - col = col, - end_lnum = end_lnum, - end_col = end_col, - lang = lang, - }) - - traverse(child, depth + 1, lang, injections, tree) - end - - return tree -end - ---- Create a new Playground object. ---- ----@param bufnr integer Source buffer number ----@param lang string|nil Language of source buffer ---- ----@return TSPlayground|nil ----@return string|nil Error message, if any ---- ----@package -function TSPlayground:new(bufnr, lang) - local ok, parser = pcall(vim.treesitter.get_parser, bufnr or 0, lang) - if not ok then - return nil, 'No parser available for the given buffer' - end - - -- For each child tree (injected language), find the root of the tree and locate the node within - -- the primary tree that contains that root. Add a mapping from the node in the primary tree to - -- the root in the child tree to the {injections} table. - local root = parser:parse()[1]:root() - local injections = {} ---@type table - parser:for_each_child(function(child, lang_) - child:for_each_tree(function(tree) - local r = tree:root() - local node = root:named_descendant_for_range(r:range()) - if node then - injections[node:id()] = { - lang = lang_, - root = r, - } - end - end) - end) - - local nodes = traverse(root, 0, parser:lang(), injections, {}) - - local named = {} ---@type TSP.Node[] - for _, v in ipairs(nodes) do - if v.named then - named[#named + 1] = v - end - end - - local t = { - ns = api.nvim_create_namespace(''), - nodes = nodes, - named = named, - opts = { - anon = false, - lang = false, - }, - } - - setmetatable(t, self) - self.__index = self - return t -end - -local decor_ns = api.nvim_create_namespace('ts.playground') - ----@private ----@param lnum integer ----@param col integer ----@param end_lnum integer ----@param end_col integer ----@return string -local function get_range_str(lnum, col, end_lnum, end_col) - if lnum == end_lnum then - return string.format('[%d:%d - %d]', lnum + 1, col + 1, end_col) - end - return string.format('[%d:%d - %d:%d]', lnum + 1, col + 1, end_lnum + 1, end_col) -end - ---- Write the contents of this Playground into {bufnr}. ---- ----@param bufnr integer Buffer number to write into. ----@package -function TSPlayground:draw(bufnr) - vim.bo[bufnr].modifiable = true - local lines = {} ---@type string[] - local lang_hl_marks = {} ---@type table[] - - for _, item in self:iter() do - local range_str = get_range_str(item.lnum, item.col, item.end_lnum, item.end_col) - local lang_str = self.opts.lang and string.format(' %s', item.lang) or '' - local line = - string.format('%s%s ; %s%s', string.rep(' ', item.depth), item.text, range_str, lang_str) - - if self.opts.lang then - lang_hl_marks[#lang_hl_marks + 1] = { - col = #line - #lang_str, - end_col = #line, - } - end - - lines[#lines + 1] = line - end - - api.nvim_buf_set_lines(bufnr, 0, -1, false, lines) - - api.nvim_buf_clear_namespace(bufnr, decor_ns, 0, -1) - - for i, m in ipairs(lang_hl_marks) do - api.nvim_buf_set_extmark(bufnr, decor_ns, i - 1, m.col, { - hl_group = 'Title', - end_col = m.end_col, - }) - end - - vim.bo[bufnr].modifiable = false -end - ---- Get node {i} from this Playground object. ---- ---- The node number is dependent on whether or not anonymous nodes are displayed. ---- ----@param i integer Node number to get ----@return TSP.Node ----@package -function TSPlayground:get(i) - local t = self.opts.anon and self.nodes or self.named - return t[i] -end - ---- Iterate over all of the nodes in this Playground object. ---- ----@return (fun(): integer, TSP.Node) Iterator over all nodes in this Playground ----@return table ----@return integer ----@package -function TSPlayground:iter() - return ipairs(self.opts.anon and self.nodes or self.named) -end - ---- @class InspectTreeOpts ---- @field lang string? The language of the source buffer. If omitted, the ---- filetype of the source buffer is used. ---- @field bufnr integer? Buffer to draw the tree into. If omitted, a new ---- buffer is created. ---- @field winid integer? Window id to display the tree buffer in. If omitted, ---- a new window is created with {command}. ---- @field command string? Vimscript command to create the window. Default ---- value is "60vnew". Only used when {winid} is nil. ---- @field title (string|fun(bufnr:integer):string|nil) Title of the window. If a ---- function, it accepts the buffer number of the source ---- buffer as its only argument and should return a string. - ---- @param opts InspectTreeOpts -function M.inspect_tree(opts) - vim.validate({ - opts = { opts, 't', true }, - }) - - opts = opts or {} - - local buf = api.nvim_get_current_buf() - local win = api.nvim_get_current_win() - local pg = assert(TSPlayground:new(buf, opts.lang)) - - -- Close any existing playground window - if vim.b[buf].playground then - local w = vim.b[buf].playground - if api.nvim_win_is_valid(w) then - api.nvim_win_close(w, true) - end - end - - local w = opts.winid - if not w then - vim.cmd(opts.command or '60vnew') - w = api.nvim_get_current_win() - end - - local b = opts.bufnr - if b then - api.nvim_win_set_buf(w, b) - else - b = api.nvim_win_get_buf(w) - end - - vim.b[buf].playground = w - - vim.wo[w].scrolloff = 5 - vim.wo[w].wrap = false - vim.wo[w].foldmethod = 'manual' -- disable folding - vim.bo[b].buflisted = false - vim.bo[b].buftype = 'nofile' - vim.bo[b].bufhidden = 'wipe' - vim.b[b].disable_query_linter = true - vim.bo[b].filetype = 'query' - - local title --- @type string? - local opts_title = opts.title - if not opts_title then - local bufname = api.nvim_buf_get_name(buf) - title = string.format('Syntax tree for %s', vim.fn.fnamemodify(bufname, ':.')) - elseif type(opts_title) == 'function' then - title = opts_title(buf) - end - - assert(type(title) == 'string', 'Window title must be a string') - api.nvim_buf_set_name(b, title) - - pg:draw(b) - - api.nvim_buf_clear_namespace(buf, pg.ns, 0, -1) - api.nvim_buf_set_keymap(b, 'n', '', '', { - desc = 'Jump to the node under the cursor in the source buffer', - callback = function() - local row = api.nvim_win_get_cursor(w)[1] - local pos = pg:get(row) - api.nvim_set_current_win(win) - api.nvim_win_set_cursor(win, { pos.lnum + 1, pos.col }) - end, - }) - api.nvim_buf_set_keymap(b, 'n', 'a', '', { - desc = 'Toggle anonymous nodes', - callback = function() - local row, col = unpack(api.nvim_win_get_cursor(w)) - local curnode = pg:get(row) - while curnode and not curnode.named do - row = row - 1 - curnode = pg:get(row) - end - - pg.opts.anon = not pg.opts.anon - pg:draw(b) - - if not curnode then - return - end - - local id = curnode.id - for i, node in pg:iter() do - if node.id == id then - api.nvim_win_set_cursor(w, { i, col }) - break - end - end - end, - }) - api.nvim_buf_set_keymap(b, 'n', 'I', '', { - desc = 'Toggle language display', - callback = function() - pg.opts.lang = not pg.opts.lang - pg:draw(b) - end, - }) - - local group = api.nvim_create_augroup('treesitter/playground', {}) - - api.nvim_create_autocmd('CursorMoved', { - group = group, - buffer = b, - callback = function() - api.nvim_buf_clear_namespace(buf, pg.ns, 0, -1) - local row = api.nvim_win_get_cursor(w)[1] - local pos = pg:get(row) - api.nvim_buf_set_extmark(buf, pg.ns, pos.lnum, pos.col, { - end_row = pos.end_lnum, - end_col = math.max(0, pos.end_col), - hl_group = 'Visual', - }) - - local topline, botline = vim.fn.line('w0', win), vim.fn.line('w$', win) - - -- Move the cursor if highlighted range is completely out of view - if pos.lnum < topline and pos.end_lnum < topline then - api.nvim_win_set_cursor(win, { pos.end_lnum + 1, 0 }) - elseif pos.lnum > botline and pos.end_lnum > botline then - api.nvim_win_set_cursor(win, { pos.lnum + 1, 0 }) - end - end, - }) - - api.nvim_create_autocmd('CursorMoved', { - group = group, - buffer = buf, - callback = function() - if not api.nvim_buf_is_loaded(b) then - return true - end - - api.nvim_buf_clear_namespace(b, pg.ns, 0, -1) - - local cursor_node = vim.treesitter.get_node({ - bufnr = buf, - lang = opts.lang, - ignore_injections = false, - }) - if not cursor_node then - return - end - - local cursor_node_id = cursor_node:id() - for i, v in pg:iter() do - if v.id == cursor_node_id then - local start = v.depth - local end_col = start + #v.text - api.nvim_buf_set_extmark(b, pg.ns, i - 1, start, { - end_col = end_col, - hl_group = 'Visual', - }) - api.nvim_win_set_cursor(w, { i, 0 }) - break - end - end - end, - }) - - api.nvim_create_autocmd({ 'TextChanged', 'InsertLeave' }, { - group = group, - buffer = buf, - callback = function() - if not api.nvim_buf_is_loaded(b) then - return true - end - - pg = assert(TSPlayground:new(buf, opts.lang)) - pg:draw(b) - end, - }) - - api.nvim_create_autocmd('BufLeave', { - group = group, - buffer = b, - callback = function() - api.nvim_buf_clear_namespace(buf, pg.ns, 0, -1) - end, - }) - - api.nvim_create_autocmd('BufLeave', { - group = group, - buffer = buf, - callback = function() - if not api.nvim_buf_is_loaded(b) then - return true - end - - api.nvim_buf_clear_namespace(b, pg.ns, 0, -1) - end, - }) - - api.nvim_create_autocmd('BufHidden', { - group = group, - buffer = buf, - once = true, - callback = function() - if api.nvim_win_is_valid(w) then - api.nvim_win_close(w, true) - end - end, - }) -end - -return M -- cgit