Expand description
Translates a parsed RawGrammar into syntect’s own
SyntaxDefinition.
§Why this goes through .sublime-syntax YAML text
The obvious approach — build syntect::parsing::syntax_definition::{ Context, MatchPattern, ContextReference, ...} values directly — turned
out not to be possible from outside the syntect crate: syntax_definition.rs
says “Everything here is public”, but ContextReference (and each of its
variants) is #[non_exhaustive] with no public constructor function,
so external code cannot construct a ContextReference::Named(..) or
::ByScope { .. } value at all (confirmed against syntect 5.3.0; see
TODO.canvas.md, “Correction: ContextReference isn’t externally
constructible”).
syntect’s own actually-public, stable construction path is
syntect::parsing::SyntaxDefinition::load_from_str — the same entry
point .sublime-syntax files go through. So this crate instead emits
.sublime-syntax-shaped YAML (via yaml-rust, the exact YAML crate
syntect itself parses with) and hands that text to syntect’s own
loader, which builds the real tree for us. This is arguably a better fit
for the project’s Goal item 3 anyway (“works offline: convert once, ship
a generated .sublime-syntax”) — see to_sublime_syntax_yaml, which
exposes that intermediate text directly, valid to save as a
.sublime-syntax file as-is.
§Mapping from TextMate’s model to sublime-syntax’s
- Top-level
patternsbecome themaincontext (the conventional entry pointsyntectlooks up by name). - Each
repositoryentry becomes its own named context, addressable viainclude: "#name". include: "$self"/"$base"becomeinclude: main; any otherincludestring is treated as an external scope name (include: "scope:<name>").- A
begin/endrule becomes apushmatch onbegininto a freshly named body context, whose ownpop: truematch onendis ordered relative to the body’s ownpatternsperapplyEndPatternLast(endfirst when absent/false— TextMate’s default — last whentrue).name/contentNamebecome that body context’smeta_scope/meta_content_scope.
with_prototype is not part of the .tmLanguage.json spec at all
(it’s a sublime-syntax-only concept for prototype injection without
nesting under patterns) — nothing to translate.
begin/while rules are real — VS Code’s own markdown.tmLanguage.json
uses them extensively (blockquotes, most fenced-code-block variants) —
but syntect has no engine-level equivalent whatsoever: no YAML
while: key, no MatchOperation variant for it, checked by grepping
its source. A begin/while rule fails translation with
Error::WhileNotSupported rather than being silently mistranslated;
this means real-world grammars built around while (markdown chief
among them) can’t be fully translated today. See TODO.canvas.md for
the analysis (this was originally — wrongly — listed as “not part of
the spec”; VS Code’s markdown grammar disproved that).
Injections (the grammar’s injections key, VS Code’s
injectionSelector) genuinely aren’t handled either: structural
embedding (a fixed-boundary begin/end region whose body includes
another grammar’s scope) already works through the mapping above and is
covered by a real end-to-end test, but true selector-based injection
isn’t something syntect itself supports at all (checked by grepping
its source for “injection”: zero matches) — see TODO.canvas.md for
the full analysis.
\G anchors are rejected outright (Error::GAnchorNotSupported),
checked on every match/begin/end regex: confirmed empirically
(examples/g_anchor_never_matches.rs) that \G never matches at all
through syntect’s onig backend, in any position, because syntect
runs each pattern as an independent search rather than the iterative
same-string session real \G semantics depend on. This isn’t a rare
corner case — VS Code’s real shell-unix-bash.tmLanguage.json and
html.tmLanguage.json both hit it in commonly-exercised paths (adjacent
quoted command names; <script>/<style> content detection), so both
fail translation entirely today. Silently letting \G through would be
worse than rejecting it: the affected context just never closes,
corrupting everything parsed afterward, with no error at all.
Capture indexes that syntect’s own YAML loader can’t line up with a
“consuming” group in the regex (via its get_consuming_capture_indexes,
backed by the regex-syntax crate rather than true Oniguruma semantics)
are silently dropped by syntect itself, not by this crate.
Enums§
Functions§
- load
- Parses and translates a
.tmLanguage.jsongrammar into asyntectSyntaxDefinitionin one step. - load_
file - Reads and translates a
.tmLanguage.jsongrammar from an arbitrary local file at runtime — the “load a custom grammar the user dropped in, no bundling required” case from the project’s Goal. - to_
sublime_ syntax_ yaml - Renders
grammaras.sublime-syntax-shaped YAML text — the same intermediatetranslatefeeds tosyntect’s own loader, exposed directly because it’s also valid to write out as a.sublime-syntaxfile for offline/build-time use. - translate