Skip to content

Annotation completions wrongly offered inside async message label (A->B: @) #805

@MrCoder

Description

@MrCoder

Steps to reproduce

  1. Open the ZenUML editor (rewrite/web-foundation branch).
  2. Type A->B: @.
  3. Observe the autocomplete popup.

Expected: No annotation completions (@Actor, @Database, etc.) — the cursor is inside a message label (Content node), which is not a participant-declaration slot.

Actual: The full annotation list (@Actor, @BoundaryParticipant, @CloudFrontParticipant, etc.) is offered.

Root cause

zenumlAutocomplete.ts line 258:

if ((zone === 'head' || zone === 'top') && (typed.startsWith('@') || context.explicit)) {
  options.push(...annotationCompletions())
}

For A->B: @ the parser produces the ancestor path LineContent → Content → AsyncMessage → Statement → Program. None of those nodes are Head or StatementBraceBlock, so resolveZone returns 'top'. The annotation gate then fires because zone === 'top' and typed.startsWith('@') are both true.

The 'top' zone was introduced to mean "the document top level where both participant declarations and message statements are syntactically valid start positions" (ADR 0002). It does not mean "a declaration slot". A message label (Content) is neither a declaration position nor a statement start — it is the free-text body of an async message — but the zone-only gate cannot distinguish these cases.

Fix sketch

Add a helper isInsideMessageContent(state, pos) that walks the syntax tree up from pos and returns true if any ancestor is Content or AsyncMessage:

function isInsideMessageContent(state: EditorState, pos: number): boolean {
  for (let n: SyntaxNode | null = syntaxTree(state).resolveInner(pos, -1); n; n = n.parent) {
    if (n.name === 'Content' || n.name === 'AsyncMessage') return true
  }
  return false
}

Then gate annotations out of that position:

// line 258 — add the !isInsideMessageContent guard
if ((zone === 'head' || zone === 'top') && !isInsideMessageContent(state, pos) &&
    (typed.startsWith('@') || context.explicit)) {
  options.push(...annotationCompletions())
}

Location: /web/src/editor/zenumlAutocomplete.ts line 258.

Found via the 100-case browser-test campaign (catalog-extended.spec.ts, case K7); 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