Skip to content

Autocomplete: block keywords leak into group/@annotation name slot when typed token is a reserved keyword #806

@MrCoder

Description

@MrCoder

Summary

When a user types a reserved keyword (e.g. if, opt, while) immediately after group or @Actor , the autocomplete popup offers block keywords including "Conditional (alt) block". This violates the context-aware rule that the declaration name slot must never offer keywords.

Steps to reproduce

  1. Open the ZenUML editor (empty doc).
  2. Type group (group keyword + space).
  3. Type if.
  4. Observe the autocomplete popup.

Expected: No block keywords offered (the popup should be empty or offer only participant names, since if is an invalid name anyway).

Actual: "Conditional (alt) block" (if) appears in the popup.

The same bug fires for @Actor if, <<stereo>> if, and with any other reserved keyword (opt, par, while, try, new, ref, …).

Root cause

isNamingDeclaration (web/src/editor/zenumlAutocomplete.ts:91) detects "cursor is in a declaration name slot" by walking up the syntax tree looking for a Group or Participant ancestor. This works correctly when the typed text is an Identifier — the parser absorbs it into Group > Name / Participant > Name.

However, Name { !name Identifier } (web/src/editor/grammar/zenuml.grammar:160) only accepts Identifier tokens. Reserved keywords tokenize as dedicated terminals (IfKeyword, WhileKeyword, etc.) and cannot satisfy Name. When group if is parsed, the IfKeyword token falls outside the Group subtree (parsed as a sibling or via error recovery). isNamingDeclaration's ancestor walk finds no Group or Participant and returns false. The guard at line 272 never fires. Zone resolves to 'top' (top-level document), so keywordsForZone('top') (= HEAD_KEYWORDS ∪ BLOCK_KEYWORDS) populates the popup.

Commit 2b22053 already generalized the naming guard to include GroupKeyword, but it only helps when the parser successfully places the cursor token inside the Group node — which keyword tokens never are.

Fix sketch

Replace the tree-containment check in isNamingDeclaration (or add a complementary text-based guard) that detects "cursor is on the same line as, and immediately follows, a declaration marker (group, @Annotation, <<stereo>>) with no intervening newline or brace." A regex similar to the existing atMessageEndpoint approach would work:

// In isNamingDeclaration or in the keyword-gate branch:
const before = state.doc.sliceString(Math.max(0, pos - 60), pos)
// After `group `, `@Annotation `, or `<<Stereotype>> ` with no newline:
if (/(?:^|\n)\s*(?:group|@\w+|<<[^>]+>>)\s+\w*$/.test(before)) return true

This must NOT suppress block keywords on the next line: group\nif is a legitimate top-level if statement and should still offer if. The same-line anchor in the regex ensures that.

Affected file

web/src/editor/zenumlAutocomplete.tsisNamingDeclaration function (~line 91) and the keyword-gate guard at line 272.

Found via the 100-case browser-test campaign (catalog-extended.spec.ts, case P9); adversarially verified.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions