shiki 0.0.1

Native TextMate grammar tokenization and Shiki-compatible highlighting
Documentation

Native TextMate syntax highlighting with Shiki-compatible language and theme bundles.

Grammars are compiled into numeric runtime structures and matched with Oniguruma. Bundled definitions live in the companion shiki-langs and shiki-themes crates; runtime TextMate JSON is supported through [HighlighterBuilder::json_language] and [HighlighterBuilder::json_theme]. Reuse a [Highlighter] or [HighlighterEngine] because compiled scanners and scope/style transitions are cached on demand.

Basic highlighting

use shiki::{Highlighter, LanguageBundle};

static LANGUAGES: LanguageBundle = shiki_langs::languages![rust];

let mut highlighter = Highlighter::builder()
    .bundle(&LANGUAGES)
    .languages(["rust"])
    .theme(&shiki_themes::CATPPUCCIN_MOCHA)
    .build()?;

let html = highlighter.code_to_html("fn main() {}", "rust")?;
assert!(html.contains("fn"));
# Ok::<(), shiki::Error>(())

Enabling every bundled language

shiki_langs::all() provides the convenient all-language bundle. Building it is intentionally more expensive, so applications should normally create one shared engine.

use shiki::Highlighter;

let languages = shiki_langs::all();
let engine = Highlighter::builder()
    .bundle(&languages)
    .theme(&shiki_themes::CATPPUCCIN_MOCHA)
    .build_engine()?;

let mut highlighter = engine.highlighter();
let html = highlighter.code_to_html("const value = 1", "javascript")?;
# Ok::<(), shiki::Error>(())

Incremental documents and parallel sessions

An engine shares immutable grammar IR and themes. Each session owns its dynamic scanner, scope and style caches, so documents can advance independently.

use shiki::{Highlighter, LanguageBundle};

static LANGUAGES: LanguageBundle = shiki_langs::languages![rust];
let engine = Highlighter::builder()
    .bundle(&LANGUAGES)
    .languages(["rust"])
    .theme(&shiki_themes::GITHUB_DARK)
    .build_engine()?;

let mut session = engine.session("rust")?;
let mut state = session.initial_state();
let tokens = session.tokenize_line("/* open", &mut state, true)?;
assert!(!tokens.is_empty());
# Ok::<(), shiki::Error>(())