From 13e2d2d63ce7bc5d875e8bdf89cb070bc8cc7a00 Mon Sep 17 00:00:00 2001 From: Pedro Castro Date: Fri, 8 Apr 2022 13:44:00 -0300 Subject: [PATCH] feat: add TSNodeUnderCursor (#74) add `hl-info.show_ts_node({opts})` to show tree-sitter node under cursor, exposed via `:TSNodeUnderCursor` --- lua/nvim-treesitter-playground.lua | 3 + lua/nvim-treesitter-playground/hl-info.lua | 110 +++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/lua/nvim-treesitter-playground.lua b/lua/nvim-treesitter-playground.lua index 8be6695..9c2f39f 100644 --- a/lua/nvim-treesitter-playground.lua +++ b/lua/nvim-treesitter-playground.lua @@ -33,6 +33,9 @@ function M.init() vim.cmd [[ command! TSHighlightCapturesUnderCursor :lua require'nvim-treesitter-playground.hl-info'.show_hl_captures() ]] + vim.cmd [[ + command! TSNodeUnderCursor :lua require'nvim-treesitter-playground.hl-info'.show_ts_node() + ]] end return M diff --git a/lua/nvim-treesitter-playground/hl-info.lua b/lua/nvim-treesitter-playground/hl-info.lua index 350de0c..96a9f86 100644 --- a/lua/nvim-treesitter-playground/hl-info.lua +++ b/lua/nvim-treesitter-playground/hl-info.lua @@ -1,5 +1,7 @@ local utils = require "nvim-treesitter-playground.utils" local highlighter = require "vim.treesitter.highlighter" +local ts_utils = require "nvim-treesitter.ts_utils" +local parsers = require "nvim-treesitter.parsers" local M = {} @@ -68,4 +70,112 @@ function M.show_hl_captures() vim.lsp.util.open_floating_preview(lines, "markdown", { border = "single", pad_left = 4, pad_right = 4 }) end +-- Show Node at Cursor +---@param opts? table with optional fields +--- - full_path: (boolean, default false) show full path to current node +--- - show_range: (boolean, default true) show range of current node +--- - include_anonymous: (boolean, default false) include anonymous node +--- - highlight_node: (boolean, default true) highlight the current node +--- - hl_group: (string, default "TSPlaygroundFocus") name of group +--- @return number|nil bufnr number +function M.show_ts_node(opts) + opts = vim.tbl_deep_extend("keep", opts or {}, { + full_path = false, + show_range = true, + include_anonymous = false, + highlight_node = true, + hl_group = "TSPlaygroundFocus", + }) + + if not parsers.has_parser() then + return + end + + -- Get Full Path to node + -- @param node + -- @param array? + -- @return string + local function get_full_path(node, array) + local parent = node:parent() + if parent == nil then + if array == nil then + return node:type() + end + local reverse = vim.tbl_map(function(index) + return array[#array + 1 - index]:type() + end, vim.tbl_keys(array)) + return table.concat(reverse, " -> ") + end + return get_full_path(parent, vim.list_extend(array or {}, { node })) + end + + local cursor = vim.api.nvim_win_get_cursor(0) + local line = cursor[1] - 1 + local col = cursor[2] + + local bufnr = 0 + local root_lang_tree = parsers.get_parser(bufnr) + local lang_tree = root_lang_tree:language_for_range { line, col, line, col } + + local lines = { "# Treesitter" } + local node_under_cursor + + for _, tree in ipairs(lang_tree:trees()) do + local root = tree:root() + if root and ts_utils.is_in_node_range(root, line, col) then + local node = root:named_descendant_for_range(line, col, line, col) + local path = opts.full_path and get_full_path(node) or node:type() + + node_under_cursor = node + + vim.list_extend(lines, { + "* Parser: " .. lang_tree:lang(), + string.format("* %s: ", opts.full_path and "Node path" or "Node") .. path, + }) + + if opts.include_anonymous then + local anonymous_node = root:descendant_for_range(line, col, line, col) + vim.list_extend(lines, { + " - Anonymous: " .. anonymous_node:type(), + }) + end + + if opts.show_range then + local srow, scol, erow, ecol = ts_utils.get_vim_range({ node:range() }, bufnr) + + vim.list_extend(lines, { + "* Range: ", + " - Start row: " .. srow, + " - End row: " .. erow, + " - Start col: " .. scol, + " - End col: " .. ecol, + }) + end + end + end + + if not node_under_cursor then + lines[#lines + 1] = "* Node not found" + end + + local ns = vim.api.nvim_create_namespace "nvim-treesitter-current-node" + + if opts.highlight_node and node_under_cursor then + ts_utils.highlight_node(node_under_cursor, bufnr, ns, opts.hl_group) + vim.cmd(string.format( + [[ + augroup TreesitterNodeUnderCursor + au! + autocmd CursorMoved lua require'nvim-treesitter-playground.internal'.clear_highlights(%d, %d) + augroup END + ]], + bufnr, + bufnr, + ns + )) + end + + return vim.lsp.util.open_floating_preview(lines, "markdown", { border = "single", pad_left = 4, pad_right = 4 }) +end + return M