-
Notifications
You must be signed in to change notification settings - Fork 24
/
treesitter.lua
181 lines (171 loc) · 4.74 KB
/
treesitter.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
local configs = require('dropbar.configs')
local bar = require('dropbar.bar')
---Convert a snake_case string to camelCase
---@param str string?
---@return string?
local function snake_to_camel(str)
if not str then
return nil
end
return (
str:gsub('^%l', string.upper):gsub('_%l', string.upper):gsub('_', '')
)
end
---Get short name of treesitter symbols in buffer buf
---@param node TSNode
---@param buf integer buffer handler
local function get_node_short_name(node, buf)
return vim
.trim(
vim.fn.matchstr(
vim.treesitter.get_node_text(node, buf):gsub('\n', ' '),
configs.opts.sources.treesitter.name_regex
)
)
:gsub('%s+', ' ')
end
---Get valid treesitter node type name
---@param node TSNode
---@return string type_name
local function get_node_short_type(node)
local ts_type = node:type()
for _, type in ipairs(configs.opts.sources.treesitter.valid_types) do
if vim.startswith(ts_type, type) then
return type
end
end
return ''
end
---Check if treesitter node is valid
---@param node TSNode
---@param buf integer buffer handler
---@return boolean
local function valid_node(node, buf)
return get_node_short_type(node) ~= ''
and get_node_short_name(node, buf) ~= ''
end
---Get treesitter node children
---@param node TSNode
---@param buf integer buffer handler
---@return TSNode[] children
local function get_node_children(node, buf)
local children = {}
for child in node:iter_children() do
if valid_node(child, buf) then
table.insert(children, child)
else
vim.list_extend(children, get_node_children(child, buf))
end
end
return children
end
---Get treesitter node siblings
---@param node TSNode
---@param buf integer buffer handler
---@return TSNode[] siblings
---@return integer idx index of the node in its siblings
local function get_node_siblings(node, buf)
local siblings = {}
local current = node --[[@type TSNode?]]
while current do
if valid_node(current, buf) then
table.insert(siblings, 1, current)
else
for _, sib in ipairs(get_node_children(current, buf)) do
table.insert(siblings, 1, sib)
end
end
current = current:prev_sibling()
end
local idx = #siblings
current = node:next_sibling()
while current do
if valid_node(current, buf) then
table.insert(siblings, current)
else
vim.list_extend(siblings, get_node_children(current, buf))
end
current = current:next_sibling()
end
return siblings, idx
end
---Convert TSNode into winbar symbol structure
---@param ts_node TSNode
---@param buf integer buffer handler
---@param win integer window handler
---@return dropbar_symbol_t?
local function convert(ts_node, buf, win)
if not valid_node(ts_node, buf) then
return nil
end
local kind = snake_to_camel(get_node_short_type(ts_node))
local range = { ts_node:range() }
return bar.dropbar_symbol_t:new(setmetatable({
buf = buf,
win = win,
name = get_node_short_name(ts_node, buf),
icon = configs.opts.icons.kinds.symbols[kind],
name_hl = 'DropBarKind' .. kind,
icon_hl = 'DropBarIconKind' .. kind,
range = {
start = {
line = range[1],
character = range[2],
},
['end'] = {
line = range[3],
character = range[4],
},
},
}, {
---@param self dropbar_symbol_t
---@param k string|number
__index = function(self, k)
if k == 'children' then
self.children = vim.tbl_map(function(child)
return convert(child, buf, win)
end, get_node_children(ts_node, buf))
return self.children
elseif k == 'siblings' or k == 'sibling_idx' then
local siblings, idx = get_node_siblings(ts_node, buf)
self.siblings = vim.tbl_map(function(sibling)
return convert(sibling, buf, win)
end, siblings)
self.sibling_idx = idx
return self[k]
end
end,
}))
end
---Get treesitter symbols from buffer
---@param buf integer buffer handler
---@param win integer window handler
---@param cursor integer[] cursor position
---@return dropbar_symbol_t[] symbols winbar symbols
local function get_symbols(buf, win, cursor)
local ts_ok = pcall(vim.treesitter.get_parser, buf or 0)
if not ts_ok then
return {}
end
local symbols = {} ---@type dropbar_symbol_t[]
local current_node =
vim.treesitter.get_node({
bufnr = buf,
pos = {
cursor[1] - 1,
cursor[2] - (cursor[2] >= 1 and vim.api
.nvim_get_mode().mode
:match('^i') and 1 or 0),
},
})
while current_node do
if valid_node(current_node, buf) then
table.insert(symbols, 1, convert(current_node, buf, win))
end
current_node = current_node:parent()
end
return symbols
end
return {
get_symbols = get_symbols,
}