diff options
| author | Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com> | 2023-07-17 22:43:33 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-07-17 16:43:33 +0200 |
| commit | 9b72ee4d221d1e9e8031e53631aaccd06841ff04 (patch) | |
| tree | a5b5d66a573155d35ef3e46f1e8dea53ef4d54a3 /crates/typst-library/src | |
| parent | e0e797c27d4eff3fbca2a182aee2700f20ed6c21 (diff) | |
Load theme for raw code (#1675)
Diffstat (limited to 'crates/typst-library/src')
| -rw-r--r-- | crates/typst-library/src/text/raw.rs | 91 |
1 files changed, 78 insertions, 13 deletions
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<Bytes>, + pub syntaxes_data: Vec<Bytes>, + + /// 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<EcoString>, + + /// The raw file buffer of syntax theme file. + #[internal] + #[parse(theme_data.map(Some))] + pub theme_data: Option<Bytes>, } 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<Content> { 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<Arc<SyntaxSet>> { +fn load_syntaxes(paths: &SyntaxPaths, bytes: &[Bytes]) -> StrResult<Arc<SyntaxSet>> { let mut out = SyntaxSetBuilder::new(); // We might have multiple sublime-syntax/yaml files @@ -423,11 +457,42 @@ fn parse_syntaxes( .collect::<SourceResult<Vec<Bytes>>>()?; // 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<Arc<synt::Theme>> { + 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<EcoString>, Option<Bytes>)> { + let Some(Spanned { v: path, span }) = + args.named::<Spanned<EcoString>>("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 |
