Skip to content

Commit 187f866

Browse files
authored
refactor!: remove nvim-treesitter dependency (#41)
1 parent 4250c8f commit 187f866

File tree

2 files changed

+109
-22
lines changed

2 files changed

+109
-22
lines changed

lua/regexplainer/component/init.lua

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
local ts_utils = require 'nvim-treesitter.ts_utils'
21
local node_pred = require 'regexplainer.utils.treesitter'
32

43
---@diagnostic disable-next-line: unused-local
@@ -202,9 +201,9 @@ end
202201
---@alias TreesitterNode any
203202

204203
--- Transform a treesitter node to a table of components which are easily rendered
205-
---@param node TreesitterNode
206-
---@param parent? TreesitterNode
207-
---@param root_regex_node TreesitterNode
204+
---@param node TSNode
205+
---@param parent? TSNode
206+
---@param root_regex_node TSNode
208207
---@return RegexplainerComponent[]
209208
--
210209
function M.make_components(node, parent, root_regex_node)
@@ -282,7 +281,7 @@ function M.make_components(node, parent, root_regex_node)
282281
elseif (type == 'identity_escape' or type == 'decimal_escape')
283282
and M.is_simple_pattern_character(previous) then
284283
if node_type ~= 'character_class'
285-
and not node_pred.is_modifier(ts_utils.get_next_node(child)) then
284+
and not node_pred.is_modifier(child:next_sibling()) then
286285
previous.text = previous.text .. child_text:gsub([[^\+]], '')
287286
else
288287
table.insert(components, {

lua/regexplainer/utils/treesitter.lua

+105-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
local ts_utils = require 'nvim-treesitter.ts_utils'
2-
31
local M = {}
42

53
local GUARD_MAX = 1000
@@ -31,28 +29,112 @@ local node_types = {
3129
}
3230

3331
for _, type in ipairs(node_types) do
32+
---@type fun(node: TSNode): boolean
3433
M['is_' .. type] = function(node)
35-
if not node then return false end
34+
if not node then
35+
return false
36+
end
3637
return node and node:type() == type
3738
end
3839
end
3940

41+
-- Get previous node with same parent
42+
---@param node TSNode
43+
---@param allow_switch_parents? boolean allow switching parents if first node
44+
---@param allow_previous_parent? boolean allow previous parent if first node and previous parent without children
45+
---@return TSNode?
46+
local function get_previous_node(node, allow_switch_parents, allow_previous_parent)
47+
local destination_node ---@type TSNode?
48+
local parent = node:parent()
49+
if not parent then
50+
return
51+
end
52+
53+
local found_pos = 0
54+
for i = 0, parent:named_child_count() - 1, 1 do
55+
if parent:named_child(i) == node then
56+
found_pos = i
57+
break
58+
end
59+
end
60+
if 0 < found_pos then
61+
destination_node = parent:named_child(found_pos - 1)
62+
elseif allow_switch_parents then
63+
local previous_node = get_previous_node(node:parent())
64+
if previous_node and previous_node:named_child_count() > 0 then
65+
destination_node = previous_node:named_child(previous_node:named_child_count() - 1)
66+
elseif previous_node and allow_previous_parent then
67+
destination_node = previous_node
68+
end
69+
end
70+
return destination_node
71+
end
72+
73+
---@param node TSNode
74+
---@return TSNode?
75+
local function get_root_for_node(node)
76+
---@type TSNode?
77+
local parent = node
78+
local result = node
79+
80+
while parent ~= nil do
81+
result = parent
82+
parent = result:parent()
83+
end
84+
85+
return result
86+
end
87+
88+
---@param row number
89+
---@param col number
90+
---@param root_lang_tree LanguageTree
91+
---@return TSNode?
92+
local function get_root_for_position(row, col, root_lang_tree)
93+
local lang_tree = root_lang_tree:language_for_range { row, col, row, col }
94+
95+
for _, tree in pairs(lang_tree:trees()) do
96+
local root = tree:root()
97+
98+
if root and vim.treesitter.is_in_node_range(root, row, col) then
99+
return root
100+
end
101+
end
102+
103+
return nil
104+
end
105+
106+
---@param root_lang_tree LanguageTree
107+
---@return TSNode?
108+
local function get_node_at_cursor(root_lang_tree)
109+
local cursor = vim.api.nvim_win_get_cursor(0)
110+
local cursor_range = { cursor[1] - 1, cursor[2] }
111+
112+
---@type TSNode?
113+
local root = get_root_for_position(cursor_range[1], cursor_range[2], root_lang_tree)
114+
115+
if not root then
116+
return
117+
end
118+
119+
return root:named_descendant_for_range(cursor_range[1], cursor_range[2], cursor_range[1], cursor_range[2])
120+
end
121+
40122
---Enter a parent-language's regexp node which contains the embedded
41123
---regexp grammar
42-
---@param node TreesitterNode
43-
local function enter_js_re_node(node)
124+
---@param root_lang_tree LanguageTree
125+
---@param node TSNode
126+
---@return TSNode?
127+
local function enter_js_re_node(root_lang_tree, node)
44128
-- cribbed from get_node_at_cursor impl
45-
local parsers = require 'nvim-treesitter.parsers'
46-
local root_lang_tree = parsers.get_parser(0)
47129
local row, col = vim.treesitter.get_node_range(node)
48130

49-
local root = ts_utils.get_root_for_position(row, col + 1--[[hack that works for js]] , root_lang_tree)
131+
local root = get_root_for_position(row, col + 1--[[hack that works for js]], root_lang_tree)
50132

51133
if not root then
52-
root = ts_utils.get_root_for_node(node)
134+
root = get_root_for_node(node)
53135

54136
if not root then
55-
return nil, 'no node immediately to the right of the regexp node'
137+
return nil
56138
end
57139
end
58140

@@ -62,9 +144,8 @@ end
62144
---Containers are regexp treesitter nodes which may contain leaf nodes like pattern_character.
63145
---An example container is anonymous_capturing_group.
64146
--
65-
---@param node TreesitterNode regexp treesitter node
147+
---@param node TSNode regexp treesitter node
66148
---@return boolean
67-
--
68149
function M.is_container(node)
69150
if node:child_count() == 0 then
70151
return false
@@ -102,7 +183,8 @@ function M.is_punctuation(type)
102183
end
103184

104185
-- Is this the document root (or close enough for our purposes)?
105-
--
186+
---@param node TSNode
187+
---@return boolean
106188
function M.is_document(node)
107189
if node == nil then return true else
108190
local type = node:type()
@@ -120,6 +202,8 @@ function M.is_document(node)
120202
end
121203
end
122204

205+
---@param node TSNode
206+
---@return unknown
123207
function M.is_control_escape(node)
124208
return require 'regexplainer.component'.is_control_escape {
125209
type = node:type(),
@@ -150,7 +234,8 @@ end
150234
---@return any, string|nil
151235
--
152236
function M.get_regexp_pattern_at_cursor()
153-
local cursor_node = ts_utils.get_node_at_cursor()
237+
local root_lang_tree = vim.treesitter.get_parser(0, vim.treesitter.language.get_lang(vim.bo[0].ft))
238+
local cursor_node = get_node_at_cursor(root_lang_tree)
154239
local cursor_node_type = cursor_node and cursor_node:type()
155240
if not cursor_node or cursor_node_type == 'program' then
156241
return
@@ -180,7 +265,10 @@ function M.get_regexp_pattern_at_cursor()
180265
if type == 'pattern' then
181266
node = next
182267
elseif type == 'regex_pattern' or type == 'regex' then
183-
node = enter_js_re_node(next)
268+
node = enter_js_re_node(root_lang_tree, next)
269+
if not node then
270+
return nil, 'no node immediately to the right of the regexp node'
271+
end
184272
end
185273
end
186274
end
@@ -196,9 +284,9 @@ function M.get_regexp_pattern_at_cursor()
196284
end
197285

198286
local _node = node
199-
node = ts_utils.get_previous_node(node, true, true)
287+
node = get_previous_node(node, true, true)
200288
if not node then
201-
node = ts_utils.get_root_for_node(_node)
289+
node = get_root_for_node(_node)
202290
if not node then
203291
return nil, 'no upwards node'
204292
end

0 commit comments

Comments
 (0)