1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! # textmate_lang
//!
//! Generate [TextMate grammars](https://macromates.com/manual/en/language_grammars)
//! (`tmLanguage.json`) from Rust: describe a language's tokens with
//! [`Grammar`] and [`Rule`]s, 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_json`] validates it and
//! returns the JSON.
//! - [`Rule`] — an entry of a pattern list: a [`Match`], a [`BeginEnd`] or
//! [`BeginWhile`] region, an include ([`Rule::include`] and friends), or a
//! list ([`Rule::patterns`]). [`Capture`] says what to do with a capture
//! group.
//! - [`Error`] — what validation found, and where, as a JSON Pointer into
//! the output.
//! - [`validate_regex`] and [`escape_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)?;
//! # Ok::<(), textmate_lang::Error>(())
//! ```
//!
//! ## What is checked
//!
//! - **Regular expressions** (`match`, `begin`, `end`, `while`,
//! `firstLineMatch`) are non-empty and in the subset `docs/API.md`
//! documents: balanced groups and classes, known escapes, quantifiers with
//! one meaning in every engine, fixed-length look-behind, back-references
//! to groups that exist (in `end` and `while`, to groups of `begin`), 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_DEPTH`] deep and patterns at most
//! [`MAX_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 is `no_std`
//! and needs only `alloc`. There are no dependencies.
extern crate alloc;
pub use MAX_RULE_DEPTH;
pub use Error;
pub use Grammar;
pub use Style;
pub use ;
pub use ;
/// Compiles and runs the `rust` code blocks in `README.md` and `docs/API.md` as
/// part of `cargo test`, so the published examples cannot drift from the API.
///
/// Present only while collecting doctests (`#[cfg(doctest)]`); it is not part of
/// the public surface and does not appear in the built library or its docs.
;