syntastica

Modern and easy syntax highlighting using tree-sitter

Note: If viewing this file on GitHub or crates.io, some links might not be working. Go to the custom docs page or the docs.rs page instead, which additionally include the Features section.

Overview

To use syntastica, you probably want to depend on three crates:

  1. The main syntastica crate for all the logic.
  2. A parser collection to provide language support (see parser collections)
  3. The theme collection for some default themes (see theme collection)

So for example:

toml syntastica = "<version>" syntastica-parsers = { version = "<version>", features = ["some"] } syntastica-themes = "<version>"

Use cases

syntastica has three main ways of highlighting code, for three different use cases:

  1. Highlight one input exactly once: see [highlight] and this example
  2. Highlight one input multiple times (e.g. with different themes or renderers): see [Processor::process_once], [render], and this example
  3. Highlight multiple different inputs any number of times: see [Processor], [render], and this example

Subprojects

Besides the main syntastica crate, many other crates for different purposes were developed and are included in the repository. This section aims to provide a good overview.

Parser collections

The main syntastica crate provides no tree-sitter parsers and queries by itself. However, the project does provide three different parser collections with different advantages and drawbacks each. All three collections depend on syntastica-queries for the tree-sitter queries. Choose one, and add it as a dependency next to syntastica itself.

All three parser collections also provide the same public API and provide features for all supported languages, as well as the three feature groups some, most, and all. Take a look at the respective crate documentation for more information.

If you want to additionally use languages that are not in any of these parser collections, one approach is shown in the custom languages example.

Theme collection

To render highlighted code to end users, a theme is needed, which specifies the colors to use for which theme key. The syntastica project comes with a separate crate containing a few default themes: syntastica-themes.

If you wish to create your own theme, have a look at the custom theme example and the documentation for the [theme!] macro.

Crates for internal use

The syntastica repository/workspace also includes some crates which are not meant for outside use, but are instead used internally. These are listed below.

Note: There are no guarantees about the public API of these crates! If, for any reason, you have to depend on one of them, then pin the exact version using <crate> = "=<version>".

General side-products

This list includes crates which were developed for syntastica but have no direct association with the main project and can be used completely separately.

WebAssembly support

TODO: WebAssembly support

Examples

This section contains some basic usage examples. More specific examples can be found in the documentation of some items such as the [Processor] type or the [render] function. Additionally, the examples directory contains a few complete examples.

This is the list of examples found here:

Example: highlight once

This example shows the easiest and quickest way to use syntastica. See the section about use cases for when it is appropriate to use syntastica this way.

```rust use syntastica::renderer::TerminalRenderer; use syntastica_parsers::{Lang, LanguageSetImpl};

let output = syntastica::highlight( // the code to highlight r#"fn main() { println!("42"); }"#, // the input's language Lang::Rust, // use syntastica-parsers language set &LanguageSetImpl::new(), // use the TerminalRenderer with no background color &mut TerminalRenderer::new(None), // use the gruvbox dark theme from syntastica-themes syntasticathemes::gruvbox::dark(), ) .unwrapor_else(|err| panic!("highlighting failed: {err}"));

println!("{output}"); ```

Example: highlight the same input multiple times

This example shows how to render the same input with two different themes using two different renderers.

```rust use syntastica::{Processor, style::Color, renderer::*}; use syntastica_parsers::{Lang, LanguageSetImpl};

// process the input once, but store the raw highlight information let highlights = Processor::processonce( // the code to highlight r#"fn main() { println!("42"); }"#, // the input's language Lang::Rust, // use syntastica-parsers language set &LanguageSetImpl::new(), ) .unwrapor_else(|err| panic!("highlighting failed: {err}"));

// render the highlights to the terminal using the // gruvbox dark theme on a dark gray background println!("{}", syntastica::render( &highlights, &mut TerminalRenderer::new(Some(Color::new(40, 40, 40))), syntastica_themes::gruvbox::dark(), ));

// render the same input to HTML using the onelight theme let html = syntastica::render( &highlights, &mut HtmlRenderer::new(), syntastica_themes::one::light(), ); // you could for example write that to a file called index.html: // std::fs::write("index.html", html).unwrap(); ```

Example: highlight multiple different inputs

This example shows how a [Processor] can be reused if multiple different inputs should be highlighted.

```rust use syntastica::{Processor, style::Color, renderer::*}; use syntastica_parsers::{Lang, LanguageSetImpl};

// create a language set and a Processor let languageset = LanguageSetImpl::new(); let mut processor = Processor::new(&languageset); // Note: language_set has to be stored in a variable, because the processor // is bound to the lifetime of the reference passed to new

// process some input let highlightsrust = processor.process( // the code to highlight r#"fn main() { println!("42"); }"#, // the input's language Lang::Rust, ) .unwrapor_else(|err| panic!("highlighting failed: {err}"));

// process some other input in another language let highlightsjs = processor.process(r"console.log('42')", Lang::Javascript) .unwrapor_else(|err| panic!("highlighting failed: {err}"));

// render the rust code to the terminal using the // gruvbox dark theme on a dark gray background println!("{}", syntastica::render( &highlightsrust, &mut TerminalRenderer::new(Some(Color::new(40, 40, 40))), syntasticathemes::gruvbox::dark(), ));

// render the same rust code to HTML using the onelight theme let html = syntastica::render( &highlightsrust, &mut HtmlRenderer::new(), syntasticathemes::one::light(), ); // you could for example write that to a file called index.html: // std::fs::write("index.html", html).unwrap();

// now render the javascript code to the terminal using the // onedark theme and no background color println!("{}", syntastica::render( &highlightsjs, &mut TerminalRenderer::new(None), syntasticathemes::one::dark(), )); ```

Example: detect language from file type

This is an alteration of the first example showing how to detect the language to use based on a file type. See that first example for explanations of the rest of the code.

syntastica uses tft for file types which provides automatic detection.

```rust use syntastica::{renderer::TerminalRenderer, languageset::{LanguageSet, SupportedLanguage}}; use syntasticaparsers::{Lang, LanguageSetImpl};

// detect the file type given a file's path and content. // this requires a dependency on tft let ft = tft::detect("main.rs", "");

let languageset = LanguageSetImpl::new(); let output = syntastica::highlight( r#"fn main() { println!("42"); }"#, // the SupportedLanguage trait provides a for_file_type function // which returns an Option<Lang> // make sure to have the trait in scope Lang::forfiletype(ft).unwrap(), &languageset, &mut TerminalRenderer::new(None), syntasticathemes::gruvbox::dark(), ) .unwrapor_else(|err| panic!("highlighting failed: {err}"));

println!("{output}"); ```

Example: custom theme

This is an alteration of the first example showing how to create a simple custom theme. See that first example for explanations of the rest of the code, and see the documentation of the [theme!] macro for more information.

```rust use syntastica::{renderer::TerminalRenderer, theme}; use syntastica_parsers::{Lang, LanguageSetImpl};

let theme = theme! { // specify colors using hex literals "purple": "#c678dd", "blue": "#61afef", "green": "#98c379",

// link to other keys using a `$` sign
"keyword": "$purple",
"function": "$blue",

// specify more styling options in curly braces
// (note that currently this order is required by the macro)
"string": {
    color: None,
    underline: false,
    strikethrough: false,
    italic: true,
    bold: false,
    link: "green",
},

};

let output = syntastica::highlight( r#"fn main() { println!("42"); }"#, Lang::Rust, &LanguageSetImpl::new(), &mut TerminalRenderer::new(None), theme, ) .unwraporelse(|err| panic!("highlighting failed: {err}"));

println!("{output}"); ```

Versioning

All crates in this workspace whose names start with syntastica share the same version. The typical semantic versioning rules are used across the public APIs of all of these, except for the ones listed as internal. The other crates in this workspace have their own separate versions.

Versions are specified as MAJOR.MINOR.PATCH. As long as the MAJOR version specifier is still at 0, changes to the MINOR version may also be breaking changes. The PATCH part is only incremented if the public API stays exactly the same.

Inspiration

TODO: shortly explain origins (lirstings)

TODO