Skip to content

Commit 526dd49

Browse files
authored
Merge branch 'stevearc:master' into snacks-auto-place-cursor
2 parents 80676d7 + 44684bf commit 526dd49

File tree

8 files changed

+132
-43
lines changed

8 files changed

+132
-43
lines changed

.github/workflows/tests.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ on:
1111
jobs:
1212
luacheck:
1313
name: Luacheck
14-
runs-on: ubuntu-22.04
14+
runs-on: ubuntu-24.04
1515
steps:
1616
- uses: actions/checkout@v4
1717

@@ -27,7 +27,7 @@ jobs:
2727

2828
typecheck:
2929
name: typecheck
30-
runs-on: ubuntu-22.04
30+
runs-on: ubuntu-24.04
3131
steps:
3232
- uses: actions/checkout@v4
3333
- uses: stevearc/nvim-typecheck-action@v2
@@ -37,7 +37,7 @@ jobs:
3737

3838
stylua:
3939
name: StyLua
40-
runs-on: ubuntu-22.04
40+
runs-on: ubuntu-24.04
4141
steps:
4242
- uses: actions/checkout@v4
4343
- name: Stylua
@@ -52,11 +52,11 @@ jobs:
5252
matrix:
5353
include:
5454
- nvim_tag: v0.9.4
55-
- nvim_tag: v0.10.0
5655
- nvim_tag: v0.10.4
56+
- nvim_tag: v0.11.0
5757

5858
name: Run tests
59-
runs-on: ubuntu-22.04
59+
runs-on: ubuntu-24.04
6060
env:
6161
NVIM_TAG: ${{ matrix.nvim_tag }}
6262
steps:
@@ -83,7 +83,7 @@ jobs:
8383
8484
update_docs:
8585
name: Update docs
86-
runs-on: ubuntu-22.04
86+
runs-on: ubuntu-24.04
8787
steps:
8888
- uses: actions/checkout@v4
8989

@@ -120,7 +120,7 @@ jobs:
120120
- run_tests
121121
- typecheck
122122
- update_docs
123-
runs-on: ubuntu-22.04
123+
runs-on: ubuntu-24.04
124124
steps:
125125
- uses: googleapis/release-please-action@v4
126126
id: release

lua/aerial/backends/treesitter/extensions.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ local default_methods = {
1818
get_parent = function(stack, match, node)
1919
for i = #stack, 1, -1 do
2020
local last_node = stack[i].node
21-
if vim.treesitter.is_ancestor(last_node, node) then
21+
if last_node == node or vim.treesitter.is_ancestor(last_node, node) then
2222
return stack[i].item, last_node, i
2323
else
2424
table.remove(stack, i)

lua/aerial/backends/treesitter/init.lua

Lines changed: 87 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ local M = {}
1111
-- start (optional): The location of the start of this symbol (default @symbol)
1212
-- end (optional): The location of the end of this symbol (default @start)
1313

14+
---@param bufnr? integer
15+
---@return boolean
16+
---@return string? msg
1417
M.is_supported = function(bufnr)
1518
local lang = helpers.get_buf_lang(bufnr)
1619
if not helpers.has_parser(lang) then
@@ -22,28 +25,23 @@ M.is_supported = function(bufnr)
2225
return true, nil
2326
end
2427

25-
M.fetch_symbols_sync = function(bufnr)
26-
bufnr = bufnr or 0
27-
local extensions = require("aerial.backends.treesitter.extensions")
28-
local get_node_text = vim.treesitter.get_node_text
29-
local include_kind = config.get_filter_kind_map(bufnr)
30-
local parser = helpers.get_parser(bufnr)
31-
local items = {}
32-
if not parser then
33-
backends.set_symbols(bufnr, items, { backend_name = "treesitter", lang = "unknown" })
34-
return
35-
end
36-
local lang = parser:lang()
37-
local syntax_tree = parser:parse()[1]
38-
local query = helpers.get_query(lang)
39-
if not query or not syntax_tree then
28+
---@param bufnr integer
29+
---@param lang string
30+
---@param query vim.treesitter.Query
31+
---@param syntax_tree? TSTree
32+
local function set_symbols_from_treesitter(bufnr, lang, query, syntax_tree)
33+
if not syntax_tree then
4034
backends.set_symbols(
4135
bufnr,
42-
items,
36+
{},
4337
{ backend_name = "treesitter", lang = lang, syntax_tree = syntax_tree }
4438
)
4539
return
4640
end
41+
local extensions = require("aerial.backends.treesitter.extensions")
42+
local get_node_text = vim.treesitter.get_node_text
43+
local include_kind = config.get_filter_kind_map(bufnr)
44+
local items = {}
4745
-- This will track a loose hierarchy of recent node+items.
4846
-- It is used to determine node parents for the tree structure.
4947
local stack = {}
@@ -79,26 +77,33 @@ M.fetch_symbols_sync = function(bufnr)
7977
local name_match = match.name or {}
8078
local selection_match = match.selection or {}
8179
local symbol_node = (match.symbol or match.type or {}).node
80+
if not symbol_node then
81+
goto continue
82+
end
8283
-- The location capture groups are optional. We default to the
8384
-- location of the @symbol capture
8485
local start_node = (match.start or {}).node or symbol_node
8586
local end_node = (match["end"] or {}).node or start_node
8687
local parent_item, parent_node, level = ext.get_parent(stack, match, symbol_node)
8788
-- Sometimes our queries will match the same node twice.
8889
-- Detect that (symbol_node == parent_node), and skip dupes.
89-
if not symbol_node or symbol_node == parent_node then
90+
if symbol_node == parent_node then
9091
goto continue
9192
end
9293
local kind = match.kind
9394
if not kind then
94-
vim.api.nvim_err_writeln(
95-
string.format("Missing 'kind' metadata in query file for language %s", lang)
95+
vim.api.nvim_echo(
96+
{ { string.format("Missing 'kind' metadata in query file for language %s", lang) } },
97+
true,
98+
{ err = true }
9699
)
97100
break
98101
elseif not vim.lsp.protocol.SymbolKind[kind] then
99-
vim.api.nvim_err_writeln(
100-
string.format("Invalid 'kind' metadata '%s' in query file for language %s", kind, lang)
101-
)
102+
vim.api.nvim_echo({
103+
{
104+
string.format("Invalid 'kind' metadata '%s' in query file for language %s", kind, lang),
105+
},
106+
}, true, { err = true })
102107
break
103108
end
104109
local range = helpers.range_from_nodes(start_node, end_node)
@@ -166,12 +171,71 @@ M.fetch_symbols_sync = function(bufnr)
166171
)
167172
end
168173

169-
M.fetch_symbols = M.fetch_symbols_sync
174+
---@param bufnr integer
175+
---@return nil|vim.treesitter.LanguageTree parser
176+
---@return nil|vim.treesitter.Query query
177+
local function get_lang_and_query(bufnr)
178+
local parser = helpers.get_parser(bufnr)
179+
if not parser then
180+
backends.set_symbols(bufnr, {}, { backend_name = "treesitter", lang = "unknown" })
181+
return
182+
end
183+
local lang = parser:lang()
184+
local query = helpers.get_query(lang)
185+
if not query then
186+
backends.set_symbols(bufnr, {}, { backend_name = "treesitter", lang = lang })
187+
return
188+
end
189+
return parser, query
190+
end
191+
192+
---@param bufnr? integer
193+
M.fetch_symbols_sync = function(bufnr)
194+
bufnr = bufnr or 0
195+
local parser, query = get_lang_and_query(bufnr)
196+
if not parser or not query then
197+
return
198+
end
199+
local lang = parser:lang()
200+
local syntax_tree = parser:parse()[1]
201+
set_symbols_from_treesitter(bufnr, lang, query, syntax_tree)
202+
end
203+
204+
---@param bufnr? integer
205+
M.fetch_symbols = function(bufnr)
206+
if not bufnr or bufnr == 0 then
207+
bufnr = vim.api.nvim_get_current_buf()
208+
end
209+
local parser, query = get_lang_and_query(bufnr)
210+
if not parser or not query then
211+
return
212+
end
213+
local lang = parser:lang()
214+
local syntax_trees = parser:parse(nil, function(err, syntax_trees)
215+
if err then
216+
vim.api.nvim_echo(
217+
{ { string.format("Error parsing buffer: %s", err) } },
218+
true,
219+
{ err = true }
220+
)
221+
backends.set_symbols(bufnr, {}, { backend_name = "treesitter", lang = lang })
222+
return
223+
else
224+
assert(syntax_trees)
225+
set_symbols_from_treesitter(bufnr, lang, query, syntax_trees[1])
226+
end
227+
end)
228+
if syntax_trees then
229+
set_symbols_from_treesitter(bufnr, lang, query, syntax_trees[1])
230+
end
231+
end
170232

233+
---@param bufnr integer
171234
M.attach = function(bufnr)
172235
util.add_change_watcher(bufnr, "treesitter")
173236
end
174237

238+
---@param bufnr integer
175239
M.detach = function(bufnr)
176240
util.remove_change_watcher(bufnr, "treesitter")
177241
end

lua/aerial/highlight.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ M.get_highlight = function(symbol, is_icon, is_collapsed)
6262
end
6363

6464
---@param name string
65-
---@return vim.api.keyset.hl_info
65+
---@return vim.api.keyset.get_hl_info
6666
local function get_hl_by_name(name)
6767
return vim.api.nvim_get_hl(0, { name = name, link = false })
6868
end

lua/aerial/nav_view.lua

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,14 @@ local function render_symbols(panel)
199199
table.insert(lines, text)
200200
local text_cols = vim.api.nvim_strwidth(text)
201201
table.insert(highlights, { "Aerial" .. item.kind .. "Icon", i - 1, 0, kind:len() })
202-
table.insert(highlights, { "Aerial" .. item.kind, i - 1, kind:len(), -1 })
202+
table.insert(highlights, { "Aerial" .. item.kind, i - 1, kind:len(), text:len() })
203203
max_len = math.max(max_len, text_cols)
204204
end
205205

206206
-- If there are no symbols in this section, add some indicator of that
207207
if #lines == 0 then
208208
table.insert(lines, "<none>")
209-
table.insert(highlights, { "Comment", 0, 0, -1 })
209+
table.insert(highlights, { "Comment", 0, 0, 6 })
210210
end
211211

212212
vim.bo[bufnr].modifiable = true
@@ -217,7 +217,11 @@ local function render_symbols(panel)
217217
local ns = vim.api.nvim_create_namespace("aerial")
218218
vim.api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
219219
for _, hl in ipairs(highlights) do
220-
vim.api.nvim_buf_add_highlight(bufnr, ns, unpack(hl))
220+
local hl_group, line, col, end_col = unpack(hl)
221+
vim.api.nvim_buf_set_extmark(bufnr, ns, line, col, {
222+
end_col = end_col,
223+
hl_group = hl_group,
224+
})
221225
end
222226
panel.width = max_len
223227
panel.height = #lines

lua/aerial/render.lua

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ M.update_aerial_buffer = function(buf)
166166
group = name_hl,
167167
row = row,
168168
col_start = string_len[spacing] + string_len[kind],
169-
col_end = -1,
169+
col_end = text:len(),
170170
})
171171
end
172172
max_len = math.max(max_len, text_cols)
@@ -184,7 +184,10 @@ M.update_aerial_buffer = function(buf)
184184
local ns = vim.api.nvim_create_namespace("aerial")
185185
vim.api.nvim_buf_clear_namespace(aer_bufnr, ns, 0, -1)
186186
for _, hl in ipairs(highlights) do
187-
vim.api.nvim_buf_add_highlight(aer_bufnr, ns, hl.group, hl.row - 1, hl.col_start, hl.col_end)
187+
vim.api.nvim_buf_set_extmark(aer_bufnr, ns, hl.row - 1, hl.col_start, {
188+
end_col = hl.col_end,
189+
hl_group = hl.group,
190+
})
188191
end
189192
M.update_highlights(bufnr)
190193
vim.b[aer_bufnr].rendered = true

lua/aerial/util.lua

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,14 @@ M.flash_highlight = function(bufnr, lnum, durationMs, hl_group)
180180
if durationMs == true or durationMs == 1 then
181181
durationMs = 300
182182
end
183-
local ns = vim.api.nvim_buf_add_highlight(bufnr, 0, hl_group, lnum - 1, 0, -1)
183+
local ns = vim.api.nvim_create_namespace("AerialFlashHighlight")
184+
local line = vim.api.nvim_buf_get_lines(bufnr, lnum - 1, lnum, true)[1]
185+
local ext_id = vim.api.nvim_buf_set_extmark(bufnr, ns, lnum - 1, 0, {
186+
end_col = #line,
187+
hl_group = hl_group,
188+
})
184189
local remove_highlight = function()
185-
pcall(vim.api.nvim_buf_clear_namespace, bufnr, ns, 0, -1)
190+
vim.api.nvim_buf_del_extmark(bufnr, ns, ext_id)
186191
end
187192
vim.defer_fn(remove_highlight, durationMs)
188193
end

tests/treesitter_spec.lua

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,32 @@ local function list_files(dir)
2323
end
2424

2525
describe("treesitter", function()
26+
local skip_tests = {}
27+
if vim.fn.has("nvim-0.11") == 0 then
28+
-- ABI version mismatch
29+
table.insert(skip_tests, "enforce_test.c")
30+
end
31+
2632
for _, filename in ipairs(list_files("tests/treesitter")) do
33+
if vim.tbl_contains(skip_tests, filename) then
34+
print("Skipping test", filename)
35+
goto continue
36+
end
2737
local filepath = "./tests/treesitter/" .. filename
2838
local basename = vim.fn.fnamemodify(filename, ":r")
2939
local symbols_file = "./tests/symbols/" .. basename .. ".json"
30-
it(filename, function()
31-
util.test_file_symbols("treesitter", filepath, symbols_file)
32-
end)
40+
if not vim.env.TS_TEST or vim.env.TS_TEST == basename then
41+
it(filename, function()
42+
util.test_file_symbols("treesitter", filepath, symbols_file)
43+
end)
44+
end
3345

34-
if filename == "markdown_test.md" then
46+
if filename == "markdown_test.md" and not vim.env.TS_TEST then
3547
it("<markdown backend> " .. filename, function()
3648
util.test_file_symbols("markdown", filepath, "./tests/symbols/markdown_backend.json")
3749
end)
3850
end
51+
::continue::
3952
end
4053

4154
util.test_file_symbols("man", "./tests/man_test.txt", "./tests/symbols/man.json")

0 commit comments

Comments
 (0)