Skip to content

fix: quote flowchart labels with nested square brackets (#21) - #23

Merged
drakulavich merged 2 commits into
mainfrom
fix/issue-21-bracket-labels
Jun 15, 2026
Merged

fix: quote flowchart labels with nested square brackets (#21)#23
drakulavich merged 2 commits into
mainfrom
fix/issue-21-bracket-labels

Conversation

@drakulavich

Copy link
Copy Markdown
Owner

Closes #21.

Problem

A flowchart rectangle node label whose text contains a [ — e.g. Vosk[return []] — breaks Mermaid's lexer: the inner [ is read as a new shape token and the whole diagram fails to render (got 'SQS').

flowchart TD
    G2P -- yes --> Vosk["return []"]
Loading

Fix

sanitize() gains a quote-aware, shape-aware scan (quoteBracketedLabels) for flowchart/graph blocks: it finds id[…] rectangle labels, walks to the balanced closing ] (so nesting like arr[0] is handled), and wraps any label containing a square bracket in quotes → Vosk["return []"].

It deliberately leaves untouched:

  • non-rectangle shapes: stadium ([…]), parallelogram [/…/], cylinder [(…)], subroutine [[…]], trapezoid [/…\]
  • already-quoted labels, and [ inside an existing quoted span — so the pass is idempotent on its own output
  • malformed unbalanced brackets (emitted as-is rather than guessed at)

This sits alongside the existing ;, (sequence) and leading-@ (flowchart) repairs; scope is still strictly inside ```mermaid fences.

Tests

  • New unit tests in cli/tests/sanitize.test.ts: nested-bracket quoting, non-empty index (arr[0]), all five shape families left untouched, already-quoted no-op, no re-quote inside quoted labels, idempotence, CRLF.
  • Real-parser regression corpus (cli/tests/mermaid-validation.test.ts): the issue's diagram verbatim as invalid/nested-square-bracket-label.mmd (fails to parse as-is, parses after sanitize()), plus valid/ fixtures for the quoted result and an all-shapes preservation case.

Verification (full CI parity): bun test → 91 pass, bun run typecheck clean, bun run build ok, claude plugin validate . --strict passes.

A rectangle node label whose text contains a '[' — e.g. `Vosk[return []]`
— breaks Mermaid's flowchart lexer: it reads the inner '[' as a new shape
token and aborts the whole diagram (`got 'SQS'`).

sanitize() now scans flowchart blocks for rectangle labels, walks to the
balanced closing ']', and quotes any label containing a square bracket
(`Vosk["return []"]`). The scan is quote-aware and shape-aware, so it
leaves stadium `([...])`, parallelogram `[/.../]`, cylinder `[(...)]`,
subroutine `[[...]]`, trapezoid `[/...\]`, and already-quoted labels
untouched, and is idempotent on its own output.

Regression coverage: invalid fixture fails the real Mermaid parser as-is
and parses after sanitize(); a valid fixture exercises all five shape
families end-to-end.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Jun 15, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a Mermaid render failure (issue #21) where a rectangle node label containing a [ — e.g. Vosk[return []] — caused the lexer to re-enter shape-token scanning and abort the whole diagram. The fix adds quoteBracketedLabels, a balanced-bracket scanner that identifies plain rectangle labels, walks to their true closing ], and wraps any label containing a bracket in double quotes.

  • iago/scripts/sanitize.ts — new quoteBracketedLabels function runs before the existing LEADING_AT regex, handling combined @ + bracket labels correctly; non-rect shapes ([[, [(, ([, [/, [\\) are excluded via a second-character check.
  • cli/tests/sanitize.test.ts — eleven new unit tests covering quoting, no-ops, idempotency, CRLF, combined @+bracket labels, and sequence-diagram exclusion.
  • Three new .mmd fixtures provide real-parser regression coverage: the original broken diagram, its corrected form, and an all-shapes preservation case.

Confidence Score: 5/5

Safe to merge — the transform is strictly additive, scoped to mermaid fences, and the balanced-bracket scanner correctly handles the targeted rectangle-node case.

The change is a narrow, well-tested text transform inside a single function. All previously passing behaviour is guarded by regression tests, the new quoting path has thorough unit and real-parser coverage, and the fix is idempotent. The one identified edge case (non-rect shapes with deeply nested word+bracket patterns in their labels) is very unlikely to appear in real diagrams and does not affect the correctness of the targeted fix.

iago/scripts/sanitize.ts — specifically the inner-shape-label boundary in quoteBracketedLabels, which lacks context tracking for non-rectangle shape spans.

Important Files Changed

Filename Overview
iago/scripts/sanitize.ts Adds quoteBracketedLabels with balanced-bracket scanning and shape-type detection; core logic is correct for rectangle nodes but the scanner does not track outer non-rect shape boundaries, so deeply-nested content inside stadium/cylinder labels could be incorrectly quoted in an edge case.
cli/tests/sanitize.test.ts Comprehensive unit tests added for the new quoting behaviour: no-op for clean labels, quoting for nested brackets, idempotency, CRLF preservation, combined leading-@ + bracket label, and sequence-diagram exclusion; all targeted scenarios are well covered.
cli/tests/fixtures/mermaid/valid/flowchart-shapes-preserved.mmd All five non-rectangle shape families included in a single valid fixture; confirms shapes are left untouched by the transformer.
cli/tests/fixtures/mermaid/invalid/nested-square-bracket-label.mmd New invalid-fixture that reproduces issue #21 verbatim (Vosk[return []]); used as a regression corpus entry.
cli/tests/fixtures/mermaid/valid/nested-square-bracket-label-quoted.mmd Expected-output counterpart to the invalid fixture; label correctly reads Vosk["return []"].

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Input[sanitize src] --> FenceMatch[Match mermaid fences]
    FenceMatch --> RewriteBlock[rewriteBlock body]
    RewriteBlock --> DetectType{isFlow?}
    DetectType -- yes --> QBL[quoteBracketedLabels per line]
    QBL --> ScanChar{scan char}
    ScanChar -- quote char --> ToggleQuote[toggle inQuote]
    ToggleQuote --> ScanChar
    ScanChar -- rect open bracket --> WalkBalanced[walk to balanced closing bracket]
    WalkBalanced -- inner has bracket no quotes --> WrapQuotes[emit quoted label]
    WalkBalanced -- else --> PassThrough[emit bracket as-is]
    WrapQuotes --> ScanChar
    PassThrough --> ScanChar
    ScanChar -- not rect open --> EmitChar[emit char as-is]
    EmitChar --> ScanChar
    ScanChar -- end of line --> LeadingAt[replace LEADING AT regex]
    DetectType -- no --> MsgReplace[replace semicolons in sequence messages]
    LeadingAt --> Output[rewritten line]
    MsgReplace --> Output
Loading

Reviews (2): Last reviewed commit: "test: pin combined leading-@ + nested-br..." | Re-trigger Greptile

Comment thread cli/tests/sanitize.test.ts
Guards the two-pass ordering (quoteBracketedLabels before LEADING_AT):
`N[@utils[0]]` must become `N["@utils[0]"]`. LEADING_AT alone would stop
at the first `]` and leave the still-broken `N["@utils[0"]]`; quoting the
whole label first makes LEADING_AT a no-op. Pins behaviour against a
future reordering of the two transforms (Greptile review on PR #23).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@drakulavich
drakulavich merged commit bf270eb into main Jun 15, 2026
4 checks passed
drakulavich added a commit that referenced this pull request Jun 15, 2026
Quote flowchart node labels containing nested square brackets so the
Mermaid lexer no longer aborts the whole diagram on `id[return []]`-style
labels (#21, #23). Version bumped across cli/package.json, plugin.json,
and marketplace.json (both fields).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@drakulavich

Copy link
Copy Markdown
Owner Author

🗺️ Change diagram — flow

Auto-generated by iago. Edit or remove this block; it will be replaced on the next run.

flowchart TD
    Start([Mermaid block]) --> Type{Flowchart or graph?}
    Type -- no --> Preserve[Keep existing sanitizer behavior]
    Type -- yes --> Scan[Scan each line]
    Scan --> Shape{Plain rectangle label?}
    Shape -- no --> Next[Continue]
    Shape -- yes --> Balanced{Balanced nested brackets?}
    Balanced -- no --> Preserve
    Balanced -- yes --> Quote[Wrap label text in quotes]
    Quote --> LeadingAt[Apply existing leading-at repair]
    Preserve --> Next
    LeadingAt --> Next
    Next --> Output([Valid Mermaid diagram])
Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

render error for empty array []

1 participant