Skip to main content

Grammar

Struct Grammar 

Source
pub struct Grammar { /* private fields */ }
Expand description

A TextMate grammar, built in Rust and emitted as tmLanguage.json.

A grammar is a scope name (source.demo) and a list of top-level patterns — the Rules the tokenizer tries at each position — plus a repository of named rules that patterns include by name, and optionally injections into other grammars. Build one by chaining: every method takes the grammar by value and returns it, and nothing is checked until it is emitted.

to_json validates the whole grammar — every regular expression against the supported Oniguruma subset, every scope name, every include, every capture — and returns the JSON, or the first problem as an Error that says where it is. Output is deterministic: the same grammar always produces the same bytes, whatever order its repository entries, injections, and captures were added in.

§Examples

use textmate_lang::{BeginEnd, Grammar, Match, Rule};

let json = Grammar::new("source.demo")
    .name("Demo")
    .file_type("demo")
    .pattern(Rule::include("comments"))
    .pattern(Rule::include("strings"))
    .pattern(Match::new(r"\b(if|else|while|return)\b").scope("keyword.control.demo"))
    .pattern(Match::new(r"\b\d+(\.\d+)?\b").scope("constant.numeric.demo"))
    .repository("comments", Match::new("#.*$").scope("comment.line.number-sign.demo"))
    .repository(
        "strings",
        BeginEnd::new("\"", "\"")
            .scope("string.quoted.double.demo")
            .pattern(Match::new(r"\\.").scope("constant.character.escape.demo")),
    )
    .to_json()?;

assert!(json.starts_with("{\n  \"name\": \"Demo\",\n  \"scopeName\": \"source.demo\","));
assert!(json.contains("\"match\": \"\\\\b(if|else|while|return)\\\\b\""));

Implementations§

Source§

impl Grammar

Source

pub fn new(scope_name: impl Into<Cow<'static, str>>) -> Grammar

Starts an empty grammar whose root scope is scope_name ("scopeName").

The scope name is how editors and other grammars refer to this one: by convention source.<language> for programming languages and text.<format> for markup. It is a single scope without spaces or substitutions (Error::InvalidScope), checked when the grammar is emitted.

ParameterMeaning
scope_nameThe root scope, such as source.rust: a &'static str (not copied) or a String.
§Examples
use textmate_lang::Grammar;

let json = Grammar::new("source.toml").to_json_compact()?;
assert_eq!(json, r#"{"scopeName":"source.toml","patterns":[]}"#);
Source

pub fn scope_name(&self) -> &str

The grammar’s root scope, as given to new.

§Examples
use textmate_lang::Grammar;

assert_eq!(Grammar::new("source.demo").scope_name(), "source.demo");
Source

pub fn name(self, name: impl Into<Cow<'static, str>>) -> Grammar

Sets the display name ("name"), shown in editors’ language pickers. Any text; it is escaped on output.

§Examples
use textmate_lang::Grammar;

let json = Grammar::new("source.demo").name("Demo \"Lang\"").to_json_compact()?;
assert!(json.starts_with(r#"{"name":"Demo \"Lang\"","#));
Source

pub fn file_type(self, extension: impl Into<Cow<'static, str>>) -> Grammar

Adds a file extension the grammar applies to ("fileTypes"), such as rs or tmLanguage.json. Must not be empty (Error::InvalidName).

§Examples
use textmate_lang::Grammar;

let json = Grammar::new("source.demo").file_type("dm").file_type("demo").to_json_compact()?;
assert!(json.contains(r#""fileTypes":["dm","demo"]"#));
Source

pub fn first_line_match(self, regex: impl Into<Cow<'static, str>>) -> Grammar

Sets a pattern matched against a file’s first line to recognize the language when the extension does not ("firstLineMatch"), such as a shebang.

§Examples
use textmate_lang::Grammar;

let json = Grammar::new("source.demo").first_line_match(r"^#!.*\bdemo\b").to_json()?;
assert!(json.contains(r#""firstLineMatch": "^#!.*\\bdemo\\b""#));
Source

pub fn injection_selector( self, selector: impl Into<Cow<'static, str>>, ) -> Grammar

Makes this an injection grammar ("injectionSelector"): its top-level patterns are injected into the scopes the selector matches in other grammars, as VS Code does for grammars that contribute injectTo.

ParameterMeaning
selectorA scope selector, such as L:source.js -comment -string.
§Examples
use textmate_lang::{Grammar, Match};

let json = Grammar::new("todo.injection")
    .injection_selector("L:comment")
    .pattern(Match::new(r"\bTODO\b").scope("keyword.todo"))
    .to_json()?;
assert!(json.contains(r#""injectionSelector": "L:comment""#));
Source

pub fn pattern(self, rule: impl Into<Rule>) -> Grammar

Adds a top-level pattern ("patterns"). Patterns are tried in the order they are added: at each position the earliest match wins, and the first pattern wins a tie.

§Examples
use textmate_lang::{Grammar, Match};

let json = Grammar::new("source.demo")
    .pattern(Match::new("==|!=").scope("keyword.operator.comparison.demo"))
    .pattern(Match::new("=").scope("keyword.operator.assignment.demo"))
    .to_json()?;
let comparison = json.find("comparison").unwrap();
let assignment = json.find("assignment").unwrap();
assert!(comparison < assignment);
Source

pub fn repository( self, name: impl Into<Cow<'static, str>>, rule: impl Into<Rule>, ) -> Grammar

Adds a repository entry called name ("repository"), which rules include with Rule::include. Names are ASCII letters, digits, _, -, and . (Error::InvalidName) and unique (Error::DuplicateRepository). Entries are emitted sorted by name.

§Examples
use textmate_lang::{Grammar, Match, Rule};

let mut grammar = Grammar::new("source.demo");
for (name, regex) in [("numbers", r"\d+"), ("booleans", r"\b(true|false)\b")] {
    grammar = grammar
        .pattern(Rule::include(name))
        .repository(name, Match::new(regex).scope(format!("constant.{name}.demo")));
}
let json = grammar.to_json()?;
assert!(json.find("\"booleans\": {").unwrap() < json.find("\"numbers\": {").unwrap());
Source

pub fn injection( self, selector: impl Into<Cow<'static, str>>, rule: impl Into<Rule>, ) -> Grammar

Adds an injection ("injections"): rule is tried, inside this grammar, wherever the scope stack matches selector. Selectors are unique (Error::DuplicateInjection); entries are emitted sorted.

§Examples
use textmate_lang::{Grammar, Match, Rule};

let json = Grammar::new("source.demo")
    .injection(
        "L:string.quoted.demo",
        Rule::patterns([Match::new(r"\{\w+\}").scope("variable.interpolation.demo").into()]),
    )
    .to_json()?;
assert!(json.contains(r#""L:string.quoted.demo": {"#));
Source

pub fn validate(&self) -> Result<(), Error>

Checks the whole grammar without emitting it.

§Errors

The first problem found, as an Error; see its documentation for the order of the checks.

§Examples
use textmate_lang::{Error, Grammar, Match};

let grammar = Grammar::new("source.demo").pattern(Match::new("(a)").capture(2, "x"));
assert_eq!(
    grammar.validate(),
    Err(Error::CaptureOutOfRange { path: "/patterns/0/captures/2".into(), group: 2, groups: 1 }),
);
Source

pub fn to_json(&self) -> Result<String, Error>

Validates the grammar and returns it as pretty-printed tmLanguage.json: two-space indentation, a final newline. The Tier-1 call: build a grammar, call this, write the file.

§Errors

The first problem found, as an Error.

§Examples
use textmate_lang::{Grammar, Match};

let json = Grammar::new("source.demo")
    .pattern(Match::new(r"\bdemo\b").scope("keyword.demo"))
    .to_json()?;
assert_eq!(json, r#"{
  "scopeName": "source.demo",
  "patterns": [
    {
      "name": "keyword.demo",
      "match": "\\bdemo\\b"
    }
  ]
}
"#);
// std::fs::write("syntaxes/demo.tmLanguage.json", json)?;
Source

pub fn to_json_compact(&self) -> Result<String, Error>

Validates the grammar and returns it as compact JSON, with no whitespace.

§Errors

The first problem found, as an Error.

§Examples
use textmate_lang::{Grammar, Match};

let json = Grammar::new("source.demo")
    .pattern(Match::new(r"\bdemo\b").scope("keyword.demo"))
    .to_json_compact()?;
assert_eq!(json, r#"{"scopeName":"source.demo","patterns":[{"name":"keyword.demo","match":"\\bdemo\\b"}]}"#);
Source

pub fn write_json(&self, out: &mut String, style: Style) -> Result<(), Error>

Validates the grammar and appends it to out in style, reusing the buffer’s capacity. On an error nothing is appended.

§Errors

The first problem found, as an Error.

§Examples
use textmate_lang::{Grammar, Style};

let mut out = String::with_capacity(4096);
for scope in ["source.a", "source.b"] {
    out.clear();
    Grammar::new(scope).write_json(&mut out, Style::Compact)?;
    assert!(out.contains(scope));
}
assert!(Grammar::new("bad scope").write_json(&mut out, Style::Compact).is_err());
assert!(out.contains("source.b"));

Trait Implementations§

Source§

impl Clone for Grammar

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Grammar

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for Grammar

Source§

impl PartialEq for Grammar

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Grammar

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.