[Tree-sitter]: https://github.com/tree-sitter/tree-sitter
[syncat-themes]: https://github.com/foxfriends/syncat-themes
# Syncat Stylesheets
This documentation consists only of the usage of the `syncat_stylesheet` crate.
For documentation on the syntax and semantics of these stylesheets, see the
[syncat-themes][] repository.
## Stylesheets
Stylesheets are loaded from files. All imports will be resolved relative to the file
during the loading process.
```syncat
// colours.syncat
$variable: red;
$value: bryellow;
// syncat.syncat
import "./colours.syncat";
declaration variable { color: $variable; }
declaration value { color: $value; }
```
```rust
let stylesheet = Stylesheet::from_file("syncat.syncat");
```
## Queries
Queries take the form of trees, as these stylesheets are typically used to apply
properties (i.e. styles) to trees (as in parse trees).
Each node of a `Query` has a `kind` and a `token`, which must be provided. These
nodes may also contain child nodes, which are also just `Query`, which can have
more subqueries.
When matching against rules, the nodes of the rule are checked with the right-most
branch of the tree.
For example, with the following source code:
```syncat
$hello: world;
```
You could construct this `Query`:
```rust
let mut query = Query::new("declaration", "$hello: world;");
query.add_child(Query::new("variable", "$hello"));
query.add_child(Query::new("value", "$world"));
```
Which corresponds to a tree like this:
```
(declaration (variable "$hello")
(value "world"))
```
Which can then be matched against a `Stylesheet` by using the `Stylesheet::style` method.
This will return `Some(style)` if there were matches, or `None` if there were
none. Often this distinction does not matter, so you can unwrap to the default
`Style`.
```rust
let style = stylesheet.style(&query).unwrap_or_default();
```
```syncat
// successfully:
declaration value {} // because the value is on the right-most branch, and has the declaration as a parent
variable + value {} // because the value is on the right-most branch, and a direct sibling of the variable
declaration {} // because the declaration is a parent of the rightmost branch
value & "world" {} // because the value on the right-most branch has token "world"
// unsuccessfully
declaration > variable {} // because the variable is not on the right-most branch
value & "wor" {} // because the token "wor" does not match the token "world"
declaration variable value {} // because the value is not nested within the variable
declaration > {} // because the declaration is not the node directly at the end of the right-most branch
```
## Applications
Once you have a `Style` value, its properties can be extracted using the `Style::get`
or `Style::try_get` methods. `Style::get` will retrieve the raw value, while `Style::try_get`
will attempt to convert that value to a contextual type.
With the `ansi_term` feature enabled (it is not by default), conversions to
`ansi_term::Style` are provided. Alternatively, you can simply extract whatever
properties you expect from the `Style` and interpret them however you choose.
```rust
let content: String = style.try_get("content")?.unwrap_or_else(|| String::from("hello world"));
let ansi_style: ansi_term::Style = style.try_into()?;
println!("{}", ansi_style.paint(content));
```