From 40da8f8fca838aa48f52b24aa585560fee8d144a Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Sun, 26 May 2024 09:50:38 +0300 Subject: [PATCH] fix: skip out of unrecognized filetypes Closes #43 Co-Authored-By: Argjun Sahlot --- lua/regexplainer.lua | 51 +++++++++++++++------------ lua/regexplainer/utils/treesitter.lua | 28 +++++++++------ 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/lua/regexplainer.lua b/lua/regexplainer.lua index 3202a13..6ead6e5 100644 --- a/lua/regexplainer.lua +++ b/lua/regexplainer.lua @@ -3,7 +3,23 @@ local tree = require 'regexplainer.utils.treesitter' local utils = require 'regexplainer.utils' local buffers = require 'regexplainer.buffers' local defer = require 'regexplainer.utils.defer' -local get_node_text = vim.treesitter.get_node_text or vim.treesitter.query.get_node_text + +---@class RegexplainerOptions +---@field mode? 'narrative' # TODO: 'ascii', 'graphical' +---@field auto? boolean # Automatically display when cursor enters a regexp +---@field filetypes? string[] # Filetypes (extensions) to automatically show regexplainer. +---@field debug? boolean # Notify debug logs +---@field display? 'split'|'popup' +---@field mappings? RegexplainerMappings # keymappings to automatically bind. Supports `which-key` +---@field narrative? RegexplainerNarrativeRendererOptions # Options for the narrative renderer +---@field popup? NuiPopupBufferOptions # options for the popup buffer +---@field split? NuiSplitBufferOptions # options for the split buffer + +---@class RegexplainerRenderOptions : RegexplainerOptions +---@field register "*"|"+"|'"'|":"|"."|"%"|"/"|"#"|"0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9" + +---@class RegexplainerYankOptions : RegexplainerOptions +---@field register "*"|"+"|'"'|":"|"."|"%"|"/"|"#"|"0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9" ---@class RegexplainerMappings ---@field show? string # shows regexplainer @@ -13,6 +29,8 @@ local get_node_text = vim.treesitter.get_node_text or vim.treesitter.query.get_n ---@field show_split? string # shows regexplainer in a split window ---@field show_popup? string # shows regexplainer in a popup window +local get_node_text = vim.treesitter.get_node_text or vim.treesitter.query.get_node_text + ---Maps config.mappings keys to vim command names and descriptions -- local config_command_map = { @@ -27,17 +45,7 @@ local config_command_map = { ---Augroup for auto = true local augroup_name = 'Regexplainer' ----@class RegexplainerOptions ----@field mode? 'narrative' # TODO: 'ascii', 'graphical' ----@field auto? boolean # Automatically display when cursor enters a regexp ----@field filetypes? string[] # Filetypes (extensions) to automatically show regexplainer. ----@field debug? boolean # Notify debug logs ----@field display? 'split'|'popup' ----@field mappings? RegexplainerMappings # keymappings to automatically bind. Supports `which-key` ----@field narrative? RegexplainerNarrativeRendererOptions # Options for the narrative renderer ----@field popup? NuiPopupBufferOptions # options for the popup buffer ----@field split? NuiSplitBufferOptions # options for the split buffer --- +---@type RegexplainerOptions local default_config = { mode = 'narrative', auto = false, @@ -62,9 +70,6 @@ local default_config = { }, } ----@class RegexplainerRenderOptions : RegexplainerOptions ----@field register "*"|"+"|'"'|":"|"."|"%"|"/"|"#"|"0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9" - --- A deep copy of the default config. --- During setup(), any user-provided config will be folded in ---@type RegexplainerOptions @@ -77,7 +82,7 @@ local local_config = vim.tbl_deep_extend('keep', default_config, {}) -- local function show(options) options = vim.tbl_deep_extend('force', local_config, options or {}) - local node, error = tree.get_regexp_pattern_at_cursor() + local node, error = tree.get_regexp_pattern_at_cursor(options) if error and options.debug then utils.notify('Rexexplainer: ' .. error, 'debug') @@ -89,7 +94,7 @@ local function show(options) ---@type RegexplainerRenderer local renderer - ---@type boolean, RexeplainerRenderer + ---@type boolean, RegexplainerRenderer local can_render, _renderer = pcall(require, 'regexplainer.renderers.' .. options.mode) if can_render then @@ -105,7 +110,8 @@ local function show(options) local buffer = buffers.get_buffer(options) if not buffer and options.debug then - return require 'regexplainer.renderers.debug'.render(options, components) + local Debug = require 'regexplainer.renderers.debug' + return Debug.render(options, components) end buffers.render(buffer, renderer, components, options, { @@ -132,9 +138,6 @@ function M.show(options) disable_auto = false end ----@class RegexplainerYankOptions ----@field register "*"|"+"|'"'|":"|"."|"%"|"/"|"#"|"0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9" - --- Yank the explainer for the regexp under the cursor into a given register ---@param options? string|RegexplainerYankOptions function M.yank(options) @@ -142,7 +145,11 @@ function M.yank(options) if type(options) == 'string' then options = { register = options } end - show(vim.tbl_deep_extend('force', options, { display = 'register' })) + show(vim.tbl_deep_extend( + 'force', + options, + { display = 'register' } + )) disable_auto = false end diff --git a/lua/regexplainer/utils/treesitter.lua b/lua/regexplainer/utils/treesitter.lua index c97b8e3..b59fbb4 100644 --- a/lua/regexplainer/utils/treesitter.lua +++ b/lua/regexplainer/utils/treesitter.lua @@ -39,13 +39,13 @@ for _, type in ipairs(node_types) do end -- Get previous node with same parent ----@param node TSNode +---@param node? TSNode ---@param allow_switch_parents? boolean allow switching parents if first node ---@param allow_previous_parent? boolean allow previous parent if first node and previous parent without children ---@return TSNode? local function get_previous_node(node, allow_switch_parents, allow_previous_parent) local destination_node ---@type TSNode? - local parent = node:parent() + local parent = node and node:parent() if not parent then return end @@ -60,7 +60,7 @@ local function get_previous_node(node, allow_switch_parents, allow_previous_pare if 0 < found_pos then destination_node = parent:named_child(found_pos - 1) elseif allow_switch_parents then - local previous_node = get_previous_node(node:parent()) + local previous_node = get_previous_node(parent) if previous_node and previous_node:named_child_count() > 0 then destination_node = previous_node:named_child(previous_node:named_child_count() - 1) elseif previous_node and allow_previous_parent then @@ -70,7 +70,7 @@ local function get_previous_node(node, allow_switch_parents, allow_previous_pare return destination_node end ----@param node TSNode +---@param node? TSNode ---@return TSNode? local function get_root_for_node(node) ---@type TSNode? @@ -87,7 +87,7 @@ end ---@param row number ---@param col number ----@param root_lang_tree LanguageTree +---@param root_lang_tree vim.treesitter.LanguageTree ---@return TSNode? local function get_root_for_position(row, col, root_lang_tree) local lang_tree = root_lang_tree:language_for_range { row, col, row, col } @@ -103,7 +103,7 @@ local function get_root_for_position(row, col, root_lang_tree) return nil end ----@param root_lang_tree LanguageTree +---@param root_lang_tree vim.treesitter.LanguageTree ---@return TSNode? local function get_node_at_cursor(root_lang_tree) local cursor = vim.api.nvim_win_get_cursor(0) @@ -121,7 +121,7 @@ end ---Enter a parent-language's regexp node which contains the embedded ---regexp grammar ----@param root_lang_tree LanguageTree +---@param root_lang_tree vim.treesitter.LanguageTree ---@param node TSNode ---@return TSNode? local function enter_js_re_node(root_lang_tree, node) @@ -183,7 +183,7 @@ function M.is_punctuation(type) end -- Is this the document root (or close enough for our purposes)? ----@param node TSNode +---@param node? TSNode ---@return boolean function M.is_document(node) if node == nil then return true else @@ -231,19 +231,25 @@ end --- Using treesitter, find the current node at cursor, and traverse up to the --- document root to determine if we're on a regexp +---@param options RegexplainerOptions ---@return any, string|nil -- -function M.get_regexp_pattern_at_cursor() - local root_lang_tree = vim.treesitter.get_parser(0, vim.treesitter.language.get_lang(vim.bo[0].ft)) +function M.get_regexp_pattern_at_cursor(options) + local filetype = vim.bo[0].ft + if not vim.tbl_contains(options.filetypes, filetype) then + return nil, 'unrecognized filetype' + end + local root_lang_tree = vim.treesitter.get_parser(0, vim.treesitter.language.get_lang(filetype)) local cursor_node = get_node_at_cursor(root_lang_tree) local cursor_node_type = cursor_node and cursor_node:type() if not cursor_node or cursor_node_type == 'program' then return end + ---@type TSNode? local node = cursor_node - if node:type() == 'regex' then + if node and node:type() == 'regex' then local iterator = node:iter_children() -- break if we enter an infinite loop (probably)