summaryrefslogtreecommitdiff
path: root/crates/typst-library/src
diff options
context:
space:
mode:
authorMyriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com>2023-07-17 22:43:33 +0800
committerGitHub <noreply@github.com>2023-07-17 16:43:33 +0200
commit9b72ee4d221d1e9e8031e53631aaccd06841ff04 (patch)
treea5b5d66a573155d35ef3e46f1e8dea53ef4d54a3 /crates/typst-library/src
parente0e797c27d4eff3fbca2a182aee2700f20ed6c21 (diff)
Load theme for raw code (#1675)
Diffstat (limited to 'crates/typst-library/src')
-rw-r--r--crates/typst-library/src/text/raw.rs91
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