Skip to content

TextMate grammar: callback-setup swallows everything after a bare function call like max(...) #11568

@1Mr-Newton

Description

@1Mr-Newton

What

The TextMate grammar in docs/common/src/utils/slint.tmLanguage.json (also served as editors/vscode/slint.tmLanguage.json) has a callback-setup rule whose 2nd pattern greedily begins on any identifier( and only ends at )\s*=>. As a result, any plain function call written with an unqualified name — e.g. max(...), min(...), mod(...), debug(...) at top-level of a property body — opens the callback-setup scope and never closes it, swallowing every following token to EOF.

Dotted function names (Math.max(...), TimeUtil.foo(...)) are not affected because the rule's identifier class [a-zA-Z_][a-zA-Z0-9_-]* excludes ., so they fall through to function-call.

Repro

component Foo inherits Rectangle {
    property <float> a:
        max(1, 2);
    property <length> next: 5px;       // not highlighted
    VerticalLayout { ... }              // not highlighted
}

After max(, property and VerticalLayout lose their scopes. Confirmed against slint.tmLanguage.json from master (and the published slint-nightly VSCode/Cursor extension 2026.4.2500) using vscode-textmate + vscode-oniguruma: the rule stack pushed by max( is never popped.

Cause

"callback-setup": {
  "patterns": [
    { "match": "(?<!-)([a-zA-Z_][a-zA-Z0-9_-]*)\\s*=>", ... },
    {
      "begin": "(?<!-)([a-zA-Z_][a-zA-Z0-9_-]*)\\s*\\(",
      "end":   "\\)\\s*=>",
      ...
    }
  ]
}

The begin pattern fires on every id( but only ends on )\s*=>. For ordinary function calls the => never arrives.

Suggested fix

Add a positive lookahead that requires => after the closing paren before entering the scope. Slint callback bindings have flat parameter lists (just identifiers + commas), so a single-line, no-nested-paren lookahead is sufficient:

"begin": "(?<!-)([a-zA-Z_][a-zA-Z0-9_-]*)(?=\\s*\\([^()]*\\)\\s*=>)\\s*\\("

Verified locally: max(...), min(...), debug(...) etc. now fall through to function-call, while clicked => { ... } and moved(x, y) => { ... } callback bindings still highlight correctly.

Happy to send this as a PR if useful.


Side note: @rust-attr(...) has no grammar rule either

Tangentially, while looking at this, I noticed the grammar has no rule for @rust-attr(...), so those annotations render as plain text. PR #11569 also adds a rust-attr / rust-attr-body rule pair that scopes the whole annotation under meta.attribute.rust-attr.slint (with sensible sub-scopes for type names, identifiers, strings, ::, =, , and nested parens). Happy to split that into its own PR if you'd prefer to evaluate the two changes separately.


Disclosure: this issue and the accompanying PR #11569 were authored by Claude Opus 4.7 (Anthropic) running inside Cursor, on behalf of @1Mr-Newton. The diagnosis, suggested fix, tokenizer-based verification, and write-up were all produced by the model; @1Mr-Newton reviewed before filing. Flagging up-front so triage can apply whatever extra scrutiny is preferred for AI-assisted reports.

Metadata

Metadata

Assignees

No one assigned

    Labels

    a:editor integrationVSCode extension, treesitter, and other editor integration (mT,bO)

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions