All notable changes to this project are documented here. The format follows Keep a Changelog, and versioning follows Semantic Versioning.
[0.2.0] — April 19th, 2026
Breaking change
The hook is now loaded as an ES module that imports web-tree-sitter directly. This replaces the two-script model used in 0.1.0 (a global TreeSitter loader plus the hook script).
[0.1.0] — April 19th, 2026
Highlights
First release. Ships a browser-side MF2 syntax highlighter and IDE-style editor as a drop-in Phoenix LiveView hook. Keystrokes parse, highlight, and produce diagnostics in the browser via web-tree-sitter running the mf2_treesitter grammar compiled to WASM — no per-keystroke server round trip. The server stays in the loop only for authoritative operations (formatting, validation, persistence).
The package covers three scopes:
Highlighter and editor runtime. Parse-and-paint on every input event, with inline wavy-underline diagnostics and hover tooltips for every ERROR and MISSING node. Auto-closing brackets ({ → {}, | → ||) with line-balance awareness, skip-over on typed-over closers, pair-delete on backspace, and selection-wrap. Bracket matching highlights the paired brace adjacent to the caret. Server-push canonicalisation on blur (mf2:canonical event) and server-push text replacement (mf2:set_message event) for host-driven content changes through the phx-update="ignore" barrier.
IDE-style editing features, all driven off the tree-sitter queries shipped by mf2_treesitter:
- Goto-definition —
F12(orCmd/Ctrl+click) on a$varjumps to its.input/.localdeclaration. - Rename-in-scope —
F2renames the variable under the caret across every definition and reference. - Outline picker —
Cmd/Ctrl+Shift+Oopens a floating list of every.input/.localbinding; arrow keys + Enter to navigate. - Structural selection —
Cmd/Ctrl+Shift+→grows the selection to the enclosing syntactic node;Cmd/Ctrl+Shift+←shrinks back through the stack. - Smart newline indent —
Enterinside a{{…}}quoted pattern,.matchmatcher, or variant indents the new line. - Completion menu — typing
$offers in-scope variables;:offers the built-in MF2 function registry;@offers common attributes. - Pluralisation skeleton —
Tabafter.match $var(optionally:number) on an otherwise empty line expands to the locale-appropriate CLDR plural categories with empty{{…}}placeholders. Locale comes from adata-mf2-localeattribute on the hook element.
Rich diagnostics beyond tree-sitter's defaults. The diagnostic layer combines tree-sitter's ERROR / MISSING nodes with source-level pre-scans and semantic post-checks so users see specific, actionable messages instead of generic "unexpected input" fallbacks:
- Line-scoped brace balancer catches a forgotten closing
}on a placeholder and points directly at the unmatched{. - Missing-selector pre-scan catches
.matchwith no following selector and pins the diagnostic on the.matchkeyword. - Missing variant-key pre-scan catches a bare
{{…}}appearing where a variant key should be (a beginner-common mistake) and points at the{{. - Matcher key-count semantic check flags variants whose key count doesn't match the selector count (e.g.
.match $a $bfollowed by1 {{…}}). - Undeclared-variable check — in complex messages with at least one
.input/.local, flags$varreferences that aren't declared. - Context-aware MISSING phrasing — "Expected name here" becomes "Expected a selector here", "Expected a variant key here", "Expected a function