From 9b72ee4d221d1e9e8031e53631aaccd06841ff04 Mon Sep 17 00:00:00 2001 From: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com> Date: Mon, 17 Jul 2023 22:43:33 +0800 Subject: Load theme for raw code (#1675) --- crates/typst-library/Cargo.toml | 2 +- crates/typst-library/src/text/raw.rs | 91 ++++++++++++++++++++++++++++++------ 2 files changed, 79 insertions(+), 14 deletions(-) (limited to 'crates') diff --git a/crates/typst-library/Cargo.toml b/crates/typst-library/Cargo.toml index 86c3ab5a..da0fae5f 100644 --- a/crates/typst-library/Cargo.toml +++ b/crates/typst-library/Cargo.toml @@ -39,7 +39,7 @@ rustybuzz = "0.7" serde_json = "1" serde_yaml = "0.8" smallvec = "1.10" -syntect = { version = "5", default-features = false, features = ["parsing", "regex-fancy", "yaml-load"] } +syntect = { version = "5", default-features = false, features = ["parsing", "regex-fancy", "plist-load", "yaml-load"] } time = { version = "0.3.20", features = ["formatting"] } toml = { version = "0.7.3", default-features = false, features = ["parse"] } tracing = "0.1.37" diff --git a/crates/typst-library/src/text/raw.rs b/crates/typst-library/src/text/raw.rs index 4d4cb710..af1607ab 100644 --- a/crates/typst-library/src/text/raw.rs +++ b/crates/typst-library/src/text/raw.rs @@ -149,17 +149,39 @@ pub struct RawElem { /// ``` /// ```` #[parse( - let (syntaxes, data) = parse_syntaxes(vm, args)?; + let (syntaxes, syntaxes_data) = parse_syntaxes(vm, args)?; syntaxes )] #[fold] pub syntaxes: SyntaxPaths, - /// The raw file buffers. + /// The raw file buffers of syntax definition files. #[internal] - #[parse(data)] + #[parse(syntaxes_data)] #[fold] - pub data: Vec, + pub syntaxes_data: Vec, + + /// The theme to use for syntax highlighting. Theme files should be in the in the + /// `tmTheme` file format. + /// + /// ````example + /// #set raw(theme: "halcyon.tmTheme") + /// + /// ```typ + /// = Chapter 1 + /// #let hi = "Hello World" + /// ``` + /// ```` + #[parse( + let (theme_path, theme_data) = parse_theme(vm, args)?; + theme_path.map(Some) + )] + pub theme: Option, + + /// The raw file buffer of syntax theme file. + #[internal] + #[parse(theme_data.map(Some))] + pub theme_data: Option, } impl RawElem { @@ -190,16 +212,28 @@ impl Show for RawElem { #[tracing::instrument(name = "RawElem::show", skip_all)] fn show(&self, _: &mut Vt, styles: StyleChain) -> SourceResult { let text = self.text(); - let lang = self.lang(styles).as_ref().map(|s| s.to_lowercase()); - let foreground = THEME + let lang = self + .lang(styles) + .as_ref() + .map(|s| s.to_lowercase()) + .or(Some("txt".into())); + + let extra_syntaxes = UnsyncLazy::new(|| { + load_syntaxes(&self.syntaxes(styles), &self.syntaxes_data(styles)).unwrap() + }); + + let theme = self.theme(styles).map(|theme_path| { + load_theme(theme_path, self.theme_data(styles).unwrap()).unwrap() + }); + + let theme = theme.as_deref().unwrap_or(&THEME); + + let foreground = theme .settings .foreground .map(to_typst) .map_or(Color::BLACK, Color::from); - let extra_syntaxes = - UnsyncLazy::new(|| load(&self.syntaxes(styles), &self.data(styles)).unwrap()); - let mut realized = if matches!(lang.as_deref(), Some("typ" | "typst" | "typc")) { let root = match lang.as_deref() { Some("typc") => syntax::parse_code(&text), @@ -207,7 +241,7 @@ impl Show for RawElem { }; let mut seq = vec![]; - let highlighter = synt::Highlighter::new(&THEME); + let highlighter = synt::Highlighter::new(theme); highlight_themed( &LinkedNode::new(&root), vec![], @@ -229,7 +263,7 @@ impl Show for RawElem { }) }) { let mut seq = vec![]; - let mut highlighter = syntect::easy::HighlightLines::new(syntax, &THEME); + let mut highlighter = syntect::easy::HighlightLines::new(syntax, theme); for (i, line) in text.lines().enumerate() { if i != 0 { seq.push(LinebreakElem::new().pack()); @@ -385,7 +419,7 @@ impl Fold for SyntaxPaths { /// Load a syntax set from a list of syntax file paths. #[comemo::memoize] -fn load(paths: &SyntaxPaths, bytes: &[Bytes]) -> StrResult> { +fn load_syntaxes(paths: &SyntaxPaths, bytes: &[Bytes]) -> StrResult> { let mut out = SyntaxSetBuilder::new(); // We might have multiple sublime-syntax/yaml files @@ -423,11 +457,42 @@ fn parse_syntaxes( .collect::>>()?; // Check that parsing works. - let _ = load(&paths, &data).at(span)?; + let _ = load_syntaxes(&paths, &data).at(span)?; Ok((Some(paths), Some(data))) } +#[comemo::memoize] +fn load_theme(path: EcoString, bytes: Bytes) -> StrResult> { + let mut cursor = std::io::Cursor::new(bytes.as_slice()); + + synt::ThemeSet::load_from_reader(&mut cursor) + .map(Arc::new) + .map_err(|e| eco_format!("failed to parse theme file `{path}`: {e}")) +} + +/// Function to parse the theme argument. +/// Much nicer than having it be part of the `element` macro. +fn parse_theme( + vm: &mut Vm, + args: &mut Args, +) -> SourceResult<(Option, Option)> { + let Some(Spanned { v: path, span }) = + args.named::>("theme")? + else { + return Ok((None, None)); + }; + + // Load theme file. + let id = vm.location().join(&path).at(span)?; + let data = vm.world().file(id).at(span)?; + + // Check that parsing works. + let _ = load_theme(path.clone(), data.clone()).at(span)?; + + Ok((Some(path), Some(data))) +} + /// The syntect syntax definitions. /// /// Code for syntax set generation is below. The `syntaxes` directory is from -- cgit v1.2.3