aboutsummaryrefslogtreecommitdiff
path: root/test/functional/treesitter
Commit message (Collapse)AuthorAge
* feat(treesitter): add get_captures() for positions and rangesLewis Russell2026-09-28
| | | | | | | | | | | | | | | | | Problem: get_captures_at_pos() only returns captures at one position. It cannot query a whole line or range. It can also miss captures from injected languages outside the visible area. Solution: Add vim.treesitter.get_captures() with a required start and an optional exclusive stop. Accept row numbers or {row, col} positions. Without a stop, query one position or a whole line. Parse the requested range and return capture coordinates. Deprecate get_captures_at_pos() with a compatibility wrapper and migrate its internal callers. Keep get_captures_at_cursor() for capture names at a window's cursor. AI-assisted
* fix(editor)!: operator target/cgn modifies Visual marks #42119Justin M. Keyes2026-09-27
| | | | | | | | | | | | | | | | | | Problem: An operator on a target that creates a Visual selection (`cgn`, `d<C-v>j`, an omap text object like `:<C-U>normal! viw`) sets `'< '>`, `gv` and `visualmode()` to the operator target. This causes at least the following problems: - clobbers the user's previous `'< '>` (annoying and usually unwanted) - E.g. after a custom textobj, `:help text-object-define`. - "cgn" with a `\%V` pattern can't be dot-repeated (".") Solution: Don't save the Visual area if the operator target created it (operator was pending). Also fix `an`/`in` with `'selection'=exclusive` starting from an empty range.
* build: enable more EmmyLua checksLewis Russell2026-09-09
| | | | | | | | | | Enable checks for unused code, deprecated calls, return types, and annotations. Fix the warnings and replace the deprecated highlight function. Keep exceptions where old APIs are still needed or the checker gets it wrong. AI-assisted
* fix(treesitter): conceal_lines is not applied to injected treesVolodymyr Chernetskyi2026-09-06
| | | | | | | | | | | | | | | | | | | | | Problem: TSHighlighter._on_conceal_line() parses with the range { row, row }. A Range2 has an exclusive end, so that is the empty range, and no injected region ever intercepts it. The root tree is parsed regardless, because its region is empty, so only injections are affected: conceal_lines metadata coming from an injected language's highlights query is dropped. on_range_impl() then records the row in _conceal_checked, so the miss persists until the buffer changes. For a markdown code block nested in a markdown code block, the inner fence delimiters stay visible and nvim_win_text_height() reports 5 rows where 3 are displayed. Solution: Pass the one-row range, as the on_range_impl() call below already does. AI-assisted
* fix(treesitter): #has-parent? errors on a node with no parentVolodymyr Chernetskyi2026-09-06
| | | | | | | | | | | | | | | | | Problem: The #has-parent? predicate indexes the result of node:parent() without checking it, so a capture that matches a tree's root node raises query.lua:600: attempt to index a nil value instead of simply not matching. In a highlights query that breaks highlighting for the whole buffer. The sibling #has-ancestor? predicate handles the same situation. Solution: Treat a missing parent as "does not match". AI-assisted
* fix(treesitter): make TSHighlighter.new() idempotent for an active buffer #41090Freddie Haddad2026-08-02
| | | | | | | | | | | | | | | | | | | Problem: Calling vim.treesitter.start() a second time on a buffer that already has an active TSHighlighter creates a brand new instance instead of reusing it, whether the parser tree is unchanged (e.g. calling start() twice) or different (e.g. switching languages). Either way the old instance is silently discarded without calling :destroy() on it, so its on_bytes/on_changedtree/on_detach callbacks stay registered and its buffer-local state (spelloptions, decoration namespace) is never restored, both leaking indefinitely for an orphaned instance that nothing references anymore. Solution: Return the existing instance when TSHighlighter.active[source] is already set for the same parser tree, instead of unconditionally constructing a new one. When the tree differs instead (e.g. a language switch), destroy() the old instance first, matching stop() semantics, before constructing the new one.
* fix(treesitter): clamp unbounded fold rangesMilad Rashidikhah2026-07-28
| | | | | | | | | | Problem: Tree-sitter uses UINT32_MAX for full-document ranges, which becomes -1 on 32-bit platforms and reaches _foldupdate as an invalid end row. Solution: Treat negative changed-range end rows as unbounded and clamp them to the buffer line count. Add a regression test that simulates the 32-bit sentinel. AI-assisted: Codex
* test(harness): migrate away from magic globalsJustin M. Keyes2026-07-21
| | | | | | | | | | | | | | | | | | | | | Problem: The magic globals `it`, `describe`, etc., are more trouble than they are worth. - Hooking into `after_each` requires `getfenv()` hacks. - They confuse luals/emmylua, because the top-level `.luarc.json` isn't merged with `test/.luarc.json` (apparently a luals limitation?) - They totally defeat discoverability because the user just has to "know" about the various magic symbols. So they harm DX, which means they serve no purpose at all. Solution: - Expose the test API from `testutil`, so tests can call `t.it()`, `t.describe()`, etc., in the conventional way. - Drop `getfenv()` hacks. - Drop the `setfenv()` injection in `load_chunk`. - Drop `test/_meta.lua`.
* refactor(test): unify XDG path handling (cmake vs zig)bfredl2026-07-06
| | | | fixes #40381
* feat(treesitter): conceal backslash in markdown inline highlights #40495Francisco Requena2026-07-01
| | | | | | | Problem: Markdown inline backslash escapes display visually. Solution: Use `@conceal` queries to hide the backslash.
* test: wasm tree-sitter #40304Ryan Patterson2026-06-19
| | | | | | | | Problem: No CI coverage for wasm builds. Solution: Add a basic workflow that builds with ENABLE_WASMTIME and runs wasm-specific tests.
* build(zig): run oldtests in zig builds with new lua runnerbfredl2026-06-02
| | | | | | | | | | | | | | | | | The new lua based runner replaces Makefile, runnvim.sh and runnvim.vim As it happens, we run a `--headless` nvim inside a `:terminal` layer, this is pointless. Also there is still a lot remnants for oldesttests, but we don't have any except for test1.in which just checks the environment for following, nonexistant oldesttests. so just skip that. For now, the actual vimscript code which runs in vim-under-test is completely unchanged. On macos, luajit is finally working with the latest ziglua master. Also fix some minor bugs regarding locales, such as incorrect HAVE_WORKING_LIBINTL checks
* refactor(test): use public enum in select_spec #39665Nathan Zeng2026-05-08
| | | | | Problem: select_spec tests a public function, but uses private enums. Solution: Replace private enums with public enums.
* fix(treesitter): crash in ts_parser_delete after gc #39497Szymon Wilczek2026-05-07
| | | | | | | | | | | | | | Problem: parser_gc() calls ts_parser_delete() but leaves the userdata pointer pointing to freed memory. If the GC finalizer runs at an unexpected time (e.g. inside nvim_buf_get_lines #39411), a stale pointer could cause a crash. Solution: - NULL out `*ud` after ts_parser_delete() in parser_gc() - Update parser_check() to handle NULL with a clear error message, guarding all parser methods against UAF Co-authored-by: Lewis Russell <lewis6991@gmail.com> Signed-off-by: Szymon Wilczek <swilczek.lx@gmail.com>
* fix(treesitter): drop select() kwargs #39646Justin M. Keyes2026-05-07
| | | | | | | Problem: Unnecessary kwargs. Solution: Use param instead of kwargs.
* feat(treesitter): provide select() #39069Nathan Zeng2026-05-06
| | | | | Problem: No public method for treesitter incremental selection. Solution: Add `vim.treesitter.select()`.
* fix(treesitter): get_node_text() inconsistent trailing newline #39409David Balatero2026-05-03
| | | | | | | | | | | | | | | | | | Problem: `get_node_text()` returned inconsistent results between buffer and string sources when a node's range ends at `end_col == 0` (i.e. the node ends with a newline). The buffer path dropped the trailing newline; the string path included it correctly. Solution: Append `'\n'` in `buf_range_get_text()` when `end_col == 0` and `start_row ~= end_row`. The `start_row ~= end_row` guard excludes zero-width nodes at column 0, which should return `""`. Remove the workaround in the `#trim!` directive that manually compensated for the missing newline. Strip whitespace in `resolve_lang()` so injection language nodes ending at `end_col == 0` (e.g. `">lua\n"`) still resolve correctly.
* perf(treesitter): reuse edited tree ranges for callbacksLewis Russell2026-04-28
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | After an edit, LanguageTree:_edit() updates the current trees. When the LanguageTree manages explicit regions, _edit() also refreshes _regions from tree:included_ranges(true), so those regions have the edited byte offsets. A later injection pass may call set_included_regions() with a different number of child regions. That path discards the old trees and emits changedtree callbacks for them. invalidate(true) does the same when a buffer is reloaded. Before this change, both discard paths called tree:included_ranges(true) for every old tree, even if _edit() had just collected those exact ranges. That duplicate range extraction is expensive with many injection trees. Realistic shapes include generated C files with many macro bodies parsed by the C preproc_arg injection, Markdown documents with many fenced blocks of the same language, and template files with many embedded-language islands. The stock highlighter registers recursive changedtree callbacks, so this is on the normal highlighting edit path. Track whether _regions currently came from tree:included_ranges(true) with _regions_from_tree_ranges. _do_changedtree_callbacks() reuses _regions only in that state; otherwise it falls back to calling tree:included_ranges(true). Clear the marker when regions are replaced by injection ranges, when a tree is reparsed, or when trees are discarded. This avoids keeping a second copy of the ranges while preserving callback precision: changedtree still receives tree:included_ranges(true) for the old tree, not the broader managed region. Benchmark on 100k C macro injections, one-line edit, recursive changedtree callback: - HEAD median: edited parse 84.2 ms, child region replacement 58.7 ms - This change: edited parse 34.6 ms, child region replacement 8.5 ms That is about 2.4x faster for the edit parse and 6.9x faster for child region replacement in this workload. Add a regression test that replacing injection regions still fires changedtree and still reports the old tree's exact included ranges. AI-assisted: Codex
* feat(treesitter/extmark): support removing a conceal highlight #35087Peter Cardenas2026-04-25
| | | | | | | | | Problem: Cannot remove a `@conceal` highlight when defined in highlights.scm. Solution: Support a `@noconceal` highlight that works similarly to `@nospell` where it overrides the conceal set on the range to remove it. Additionally, can set the conceal metadata field to false for the same behavior.
* feat(treesitter): expand selection to sibling node #38938altermo2026-04-22
| | | | | | | | | Problem: Can't expand treesitter-incremental-selection to the next and previous sibling nodes. Solution: Pressing `]N` in visual mode will expand the selection to the next sibling node, and `[N` will do the same with the previous node.
* test(lsp): extract buf/util parts from lsp_spec.lua #39149Yi Ming2026-04-17
| | | | | | | | | Problem: `test/functional/plugin/lsp_spec.lua` had grown into a large catch-all file that mixed core LSP client lifecycle coverage, `vim.lsp.buf.*` behavior, and `vim.lsp.util.*` behavior in one place. Solution: Split the large tests into more focused test files without changing test coverage or intended behavior. After this change, `lsp_spec.lua` is more focused on core LSP client/config/dynamic-registration behavior.
* fix(treesitter): TSNode:id() with NUL byte causes unreliable select() #39134altermo2026-04-16
| | | | | | | | | | | Problem: `TSNode:id()` returns the underlying c pointer as a string, which may include NUL bytes. In PUC Lua, `('%s'):format('\0a\0')` returns `''` and not `'\0a\0'` (i.e. treats the string as a c-string (which terminates at the NUL byte)). This resulted in two different nodes being able to have the same id. Solution: Use concatenation `..` instead of `string.format()`.
* fix(treesitter): restore highlighting on 32 bit systems #39091Barrett Ruth2026-04-16
| | | | | Problem: Treesitter highlighting regressed on 32-bit builds because ranges that should cover the whole buffer were corrupted when passed into Lua. Solution: Round-trip those range values through Lua and validate them so treesitter sees the same ranges on 32 and 64-bit builds.
* fix(treesitter): select with node ending with unicode char (#38557)altermo2026-03-31
| | | Co-authored-by: zeertzjq <zeertzjq@outlook.com>
* docs: api, plugins, ui2Justin M. Keyes2026-03-24
|
* test: issue #35575vanaigr2026-03-23
|
* refactor(treesitter)!: remove "all" option of Query:iter_matches #33070Gregory Anders2026-03-23
| | | | | This option was introduced to help with transitioning to the new behavior during the 0.11 release cycle with the intention of removing in 0.12.
* test(treesitter): skip unreliable select test #38391altermo2026-03-20
| | | The test sometimes(around 1/30) fails on PUC-lua.
* refactor(treesitter)!: get_parser return nil on error #37276nikolightsaber2026-03-13
|
* docs: api, messages, lsp, trustJustin M. Keyes2026-03-11
| | | | | gen_vimdoc.lua: In prepare for the upcoming release, comment-out the "Experimental" warning for prerelease features.
* feat(treesitter): incremental selectionaltermo2026-03-08
| | | | Co-authored-by: György Andorka <gyorgy.andorka@protonmail.com>
* fix(treesitter): normalize language aliasesStefan VanBuren2026-03-04
| | | | | | | | | | | Hyphenated language names are silently dropped when used as injections (see #38132). This combines the normalization of language aliases into `resolve_lang`, and also adds the normalization of hyphens to underscores, which allows for handling of injected language tags with hyphens in their names. Fixes #38132.
* test(treesitter/fold_spec): fix flaky test (#38075)zeertzjq2026-02-26
|
* fix(treesitter): InspectTree only show the largest injection #37906phanium2026-02-24
| | | | | | | | | | | Problem: :InspectTree don't show luadoc injection lua file. Since luadoc share the same "root" with comment in their common primary (lua) tree. Current logic simply show the largest (comment injection) and ignore all smaller one (luadoc injection). Solution: Handle different lang injections separately. Then sort them by byte_length to ensure the draw tree consistent.
* test: start test runners in Xtest_xdg dir (#37964)zeertzjq2026-02-20
| | | | | | | | | | | | | | This is a better way to prevent parallel tests from interfering with each other, as there are many ways files can be created and deleted in tests, so enforcing different file names is hard. Using $TMPDIR can also work in most cases, but 'backipskip' etc. have special defaults for $TMPDIR. Symlink runtime/, src/, test/ and README.md to Xtest_xdg dir to make tests more convenient (and symlinking test/ is required for busted). Also, use README.md instead of test/README.md in the Ex mode inccommand test, as test/README.md no longer contains 'N' char.
* fix(treesitter): :InspectTree wrong title for non-relative path #37965zeertzjq2026-02-19
| | | | Problem: :InspectTree sets wrong title for file with non-relative path. Solution: Use full path if relpath() returns nil.
* fix(treesitter): fix spell navigation on first line (#37361)ashab-k2026-01-15
| | | | | | | | Problem: Spell navigation skips words on the first line because _on_spell_nav passes an empty range (0,0) to the highlighter. Solution: Use math.max(erow, srow + 1) to ensure a valid search window. Signed-off-by: ashab-k <ashabkhan2000@gmail.com>
* Revert "refactor(treesitter): use scratch buffer for string parser" #36964Riley Bruins2025-12-15
| | | This reverts commit 2a7cb32959b4c616bd2c76ae1933f8e068e391ad.
* refactor(treesitter): use scratch buffer for string parser #35988Riley Bruins2025-10-02
| | | | | | | | | | This commit changes `languagetree.lua` so that it creates a scratch buffer under the hood when dealing with string parsers. This will make it much easier to just use extmarks whenever we need to track injection trees in `languagetree.lua`. This also allows us to remove the `treesitter.c` code for parsing a string directly. Note that the string parser's scratch buffer has `set noeol nofixeol` so that the parsed source exactly matches the passed in string.
* fix(treesitter): don't add final newline if not present #35970Riley Bruins2025-10-01
| | | | | | | | | | | | | | | | | | | | | | | | **Problem(?):** Buffers that (for whatever reason) aren't meant to have a final newline are still parsed with a final newline in `treesitter.c`. **Solution:** Don't add the newline to the last buffer line if it shouldn't be there. (This more closely matches the approach of `read_buffer_into()`.) This allows us to, say, use a scratch buffer with `noeol` and `nofixeol` behind the scenes in `get_string_parser()`. ...which would allow us to track injection trees with extmarks in that case. ...which would allow us to not drop previous trees after reparsing a different range with `get_parser():parse()`. ...which would prevent flickering when editing a buffer that has 2+ windows to it in view at a time. ...which would allow us to keep our sanity!!! (one step at a time...)
* test: remove a few more redundant clear() calls (#35903)zeertzjq2025-09-24
|
* perf: add on_range in treesitter highlightingvanaigr2025-08-28
|
* fix(folds): error when deleting lines at end of buffer #35396tao2025-08-21
| | | | | | | | | | | Problem: with `foldmethod=expr foldexpr=v:lua.vim.treesitter.foldexpr() foldminlines=0`, deleting lines at the end of the buffer always reports an invalid top error, because the top value (i.e. the start line number of the deletion) is always 1 greater than the total line number of the modified buffer. Solution: remove the ml_line_count validation
* fix(treesitter): run FileType autocmds in the context of `<abuf>`Sean Dewar2025-08-19
| | | | | | | | | Problem: many FileType autocommands assume curbuf is the same as the target buffer; this can cause &syntax to be restored for the wrong buffer in some cases when TSHighlighter:destroy is called. Solution: run nvim_exec_autocmds in the context of the target buffer via nvim_buf_call.
* test(fold): flaky "doesn't open folds that are not touched" #34911Justin M. Keyes2025-07-13
|
* fix(treesitter): inconsistent highlight of multiline combined injection #32619Artem2025-07-06
| | | | | | | Problem: Combined injections not entirely highlighted. Solution: Reapply layer highlights on each line.
* test(treesitter): test tree:root() is idempotentRodrigodd2025-07-02
| | | | Test for regression #34605
* docs: vim.fs., diagnostics, lsp #34402Justin M. Keyes2025-06-13
|
* feat(treesitter)!: apply `offset!` directive to all captures #34383Riley Bruins2025-06-13
| | | | | | | | | | | | | | | | | This commit changes the `offset!` directive so that instead of setting a `metadata.range` value for the entire pattern, it will set a `metadata.offset` value. This offset will be applied to the range only in `vim.treesitter.get_range()`, rather than at directive application time. This allows the offset to be applied to any and all nodes captured by the given pattern, and removes the requirement that `#offset!` be applied to only a single node. The downside of this change is that plugins which read from `metadata.range` may be thrown off course, but such plugins should prefer `vim.treesitter.get_range()` when retrieving ranges anyway. Note that `#trim!` still sets `metadata.range`, and `vim.treesitter.get_range()` still reads from `metadata.range`, if it exists.
* ci: skip flaky fold test on freebsd/cirrusJustin M. Keyes2025-06-09
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | FAILED test/functional/treesitter/fold_spec.lua @ 720 : treesitter foldexpr doesn't open folds that are not touched test/functional/treesitter/fold_spec.lua:767: Row 1 did not match. Expected: |*{1:-}^t1 | |*{1:-}# h2 | |*{1:│}t2 | |{3:~ }| |{3:~ }| |{3:~ }| |{3:~ }| |1 line less; before #2 {MATCH:.*}| Actual: |*{1: }^t1 | |*{1:+}{2:+-- 2 lines: # h2·····················}| |*{3:~ }| |{3:~ }| |{3:~ }| |{3:~ }| |{3:~ }| |1 line less; before #2 0 seconds ago | To print the expect() call that would assert the current screen state, use screen:snapshot_util(). In case of non-deterministic failures, use screen:redraw_debug() to show all intermediate screen states. Snapshot: screen:expect([[ {1: }^t1 | {1:+}{2:+-- 2 lines: # h2·····················}| {3:~ }|*5 1 line less; before #2 0 seconds ago | ]])