Skip to content

Update tree-sitter-kotlin and adapt the queries - #111

Merged
MrSubidubi merged 2 commits into
zed-extensions:mainfrom
interkelstar:feat/bump-tree-sitter-kotlin
Aug 7, 2026
Merged

Update tree-sitter-kotlin and adapt the queries#111
MrSubidubi merged 2 commits into
zed-extensions:mainfrom
interkelstar:feat/bump-tree-sitter-kotlin

Conversation

@interkelstar

Copy link
Copy Markdown
Contributor

Why

The pinned grammar revision (4e909d6) mis-parses a class carrying two or more annotations when another declaration follows it. class and the class name come out as simple_identifiers and the body as a lambda_literal, so the tree contains no class_declaration at all — and no ERROR node, so the file looks healthy.

@Component
@ConfigurationProperties(prefix = "app")
class Config {
    var url: String? = null
}

class Other

Visible effects: the class name is highlighted as a variable rather than a type, and the class is missing from the outline and from breadcrumbs while its own properties still show. Spring-shaped code hits this constantly.

This is already fixed upstream, 185 commits past the pinned revision.

Query changes

Four node changes since then need the queries to follow. The first one is not cosmetic — it stops outline.scm compiling at all against the newer grammar, so the bump cannot land without it:

Change Query
property_declaration holds binding_pattern_kind instead of bare "val"/"var" tokens outline.scm — the ["val" "var"] alternation became an impossible pattern
"null" is now the named null_literal highlights.scm — was invalid node type
"!is" / "!in" are no longer single tokens (optional "!" + "is"/"in") highlights.scm — dropped; "!", "is" and "in" are all already in the operator list
interpolation exposes interpolation_identifier_start, interpolation_expression_start, interpolation_expression_end (upstream #270) highlights.scm — replaces the "$", "${", "}" tokens

Verification

Every query was run against both grammar revisions over a real 21-file Kotlin project (Spring, Gradle) and the capture counts compared, to catch patterns that still compile but silently stop matching:

query before after
outline 948 952 +4 — the annotated classes that were missing
highlights 11480 10855 −625, see below
brackets 2236 2236 unchanged
indents 2136 2136 unchanged
injections 72 72 unchanged
overrides 334 334 unchanged
runnables 200 200 unchanged

Breaking the highlight delta down by capture kind, it is punctuation.delimiter (−619) and punctuation.bracket (−13); everything else moves by at most ±4, in the positive direction.

The punctuation.delimiter drop is the dots inside import statements: the newer grammar builds the import path as one token, so its dots are no longer separately addressable and no query can colour them independently. Dots in package headers and in navigation expressions are unaffected. This is a deliberate upstream change, not something the queries can restore.

Known remaining case

The same family of mis-parse still affects declarations led by two or more plain modifiers (internal open class First {} followed by another declaration) — upstream issue fwcd/tree-sitter-kotlin#277, with a fix proposed in fwcd/tree-sitter-kotlin#280. That one is independent of this bump; the annotation case above is fixed by the revision this PR moves to.

The pinned revision mis-parses a class carrying two or more annotations when
another declaration follows it: `class` and the class name come out as
`simple_identifier`s and the body as a `lambda_literal`, so the tree holds no
`class_declaration` at all. No `ERROR` node is produced, so the file looks
healthy while the class name is highlighted as a variable and the class is
missing from the outline and breadcrumbs. Spring-style code triggers it
readily:

    @component
    @ConfigurationProperties(prefix = "app")
    class Config {
        var url: String? = null
    }

    class Other

That is fixed upstream, 185 commits after the pinned revision.

Four node changes since then need the queries to follow:

- `property_declaration` holds `binding_pattern_kind` instead of the bare
  "val"/"var" tokens, which made the `["val" "var"]` alternation an
  impossible pattern and stopped `outline.scm` compiling entirely;
- "null" is the named `null_literal`;
- "!is" and "!in" are no longer single tokens — the grammar builds them as an
  optional "!" followed by "is"/"in", both of which the query already covers;
- string interpolation exposes `interpolation_identifier_start`,
  `interpolation_expression_start` and `interpolation_expression_end` in place
  of the "$", "${" and "}" tokens.
@cla-bot cla-bot Bot added the cla-signed label Aug 1, 2026
`c8ac3d2` predates two fixes for the case this PR is about. fwcd/tree-sitter-kotlin#278
stopped a modifier-led `class` before an adjacent declaration from parsing as an infix
expression, and #280 did the same for `object`, which #278 did not cover. Both landed
2026-08-01 and `1852ea1` is the tip carrying them.

Verified against the new grammar: a file with an annotated class, an `internal open class`
followed by an `object`, and a `sealed interface` parses with three `class_declaration`s and
one `object_declaration`, no `ERROR` and no `infix_expression`. All seven query files still
compile against it, and the annotated class appears in the outline again.
@interkelstar

Copy link
Copy Markdown
Contributor Author

Moved the pin from c8ac3d2 to 1852ea1, the current tip of upstream main.

c8ac3d2 predates two fixes for exactly the case this PR is about. fwcd/tree-sitter-kotlin#278 stopped a modifier-led class before an adjacent declaration from parsing as an infix expression, and #280 did the same for object, which #278 did not cover. Both landed on 2026-08-01.

Re-verified against the new grammar with the CLI upstream uses, cache cleared:

@Configuration
@EnableConfigurationProperties
class DataSourceConfig(private val url: String) { ... }

internal open class Other

object Registry { ... }

sealed interface Shape

parses as three class_declarations and one object_declaration, with no ERROR and no infix_expression. All seven query files still compile against it, and the annotated class is back in the outline, which is what was missing from breadcrumbs.

@MrSubidubi MrSubidubi changed the title Update tree-sitter-kotlin and adapt the queries Update tree-sitter-kotlin and adapt the queries Aug 7, 2026

@MrSubidubi MrSubidubi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you once again!

@MrSubidubi
MrSubidubi merged commit f740f85 into zed-extensions:main Aug 7, 2026
5 checks passed
MrSubidubi pushed a commit that referenced this pull request Aug 7, 2026
Zed themes style `comment.doc` apart from `comment`, and every other
bundled language that has doc comments captures it. Kotlin captured both
`line_comment` and `multiline_comment` as plain `comment`, so a KDoc
block was indistinguishable from any other comment.

```kotlin
/**
 * Real KDoc, now `comment.doc`.
 */
class A

/* Plain block comment, still `comment`. */
class B
```

tree-sitter-kotlin has no doc-comment node, unlike tree-sitter-rust
which captures `(block_comment (doc_comment))`, so the opening delimiter
is what distinguishes them. `#match?` predicates are already used for
this kind of thing in the bash, c, cpp and css queries Zed bundles.

Three lines, kept separate from #111 so the grammar bump is not held up
by it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants