Expand description
§textmate_lang
Generate TextMate grammars
(tmLanguage.json) from Rust: describe a language’s tokens with
Grammar and Rules, and emit the JSON that VS Code, Sublime Text,
TextMate, Shiki, and other editors and highlighters load to highlight it,
no tree-sitter required.
Emitting validates everything a grammar file cannot check for itself. Every regular expression is held to a documented subset of Oniguruma syntax, and refused with a byte offset and a reason when it strays outside. Scope names, scope selectors, captures, and includes are checked; includes that would send a tokenizer into endless recursion are found. The JSON writer is the crate’s own and escapes every string it writes, so no name, pattern, or scope can change the structure of the output.
§The pieces
Grammar— the scope name, top-level patterns, the repository of named rules, and injections.Grammar::to_jsonvalidates it and returns the JSON.Rule— an entry of a pattern list: aMatch, aBeginEndorBeginWhileregion, an include (Rule::includeand friends), or a list (Rule::patterns).Capturesays what to do with a capture group.Error— what validation found, and where, as a JSON Pointer into the output.validate_regexandescape_regex— the regular-expression check on its own, and literal text made safe to match.
§Example
use textmate_lang::{BeginEnd, Grammar, Match, Rule, escape_regex};
let operators = ["+", "-", "*", "/", "=="].map(escape_regex).join("|");
let json = Grammar::new("source.calc")
.name("Calc")
.file_type("calc")
.pattern(Rule::include("comment"))
.pattern(Match::new(r"\b\d+(\.\d+)?\b").scope("constant.numeric.calc"))
.pattern(Match::new(operators).scope("keyword.operator.calc"))
.pattern(
Match::new(r"\b(let)\s+([a-z_]\w*)")
.capture(1, "storage.type.calc")
.capture(2, "variable.other.calc"),
)
.repository(
"comment",
BeginEnd::new("#", "$")
.scope("comment.line.number-sign.calc")
.begin_capture(0, "punctuation.definition.comment.calc"),
)
.to_json()?;
assert!(json.contains(r#""match": "\\+|\\-|\\*|\\/|\\=\\=""#));
// std::fs::write("syntaxes/calc.tmLanguage.json", json)?;§What is checked
- Regular expressions (
match,begin,end,while,firstLineMatch) are non-empty and in the subsetdocs/API.mddocuments: balanced groups and classes, known escapes, quantifiers with one meaning in every engine, fixed-length look-behind, back-references to groups that exist (inendandwhile, to groups ofbegin), and no mixing of named and numbered groups. - Scope names are dot-separated segments of ASCII letters, digits,
_,+, and-; rule names may list several scopes and substitute capture text with$n, which must name an existing group. - Captures name groups the pattern has, each at most once.
- Includes of repository entries resolve; includes that form a cycle of pattern lists, which tokenizers expand forever, are refused, while recursion through a region (how nested constructs are written) is accepted.
- Selectors (
injectionSelector, injection keys) parse. - Rules nest at most
MAX_RULE_DEPTHdeep and patterns at mostMAX_REGEX_DEPTH; validation and emission are iterative.
What cannot be checked here: rules in other grammars that external includes name, and whether a pattern matches what its author intended.
§Features
std(default) — the standard library. Without it the crate isno_stdand needs onlyalloc. There are no dependencies.
Structs§
- Begin
End - A region from a
beginmatch to anendmatch ("begin"/"end"). - Begin
While - A region that opens at a
beginmatch and continues, line by line, for as long as awhilepattern matches at the start of each following line ("begin"/"while"), as Markdown block quotes and lists do. - Capture
- What to do with the text a capture group matched: give it a scope, run patterns over it, or both.
- Grammar
- A TextMate grammar, built in Rust and emitted as
tmLanguage.json. - Match
- A rule that matches one regular expression (
"match"). - Regex
Error - Why a pattern is outside the accepted subset, and where.
- Rule
- One entry of a pattern list: what the tokenizer tries at each position.
Enums§
- Error
- Why
Grammarrefused to emit a grammar. - Regex
Error Kind - What is wrong with a pattern. See
RegexError. - Style
- How
Grammar::write_jsonlays out its output.
Constants§
- MAX_
REGEX_ DEPTH - How deep groups and character classes may nest in one pattern.
- MAX_
RULE_ DEPTH - How deep rules may nest: regions inside regions, captures with patterns, and pattern lists inside either. Includes do not count.
Functions§
- escape_
regex - Escapes
textso that it is a regular expression matching exactlytext, in every context a grammar uses: plain and extended ((?x)) mode, andend/whilepatterns. - validate_
regex - Checks that
patternis in the accepted Oniguruma subset and returns its number of capture groups.