feat: export to format regardless of export target (#1549)

* feat: export to format regardless of export target

* chore: move todo comment
This commit is contained in:
Myriad-Dreamin 2025-03-20 17:11:55 +08:00 committed by GitHub
parent fadf85e68a
commit d4f00b86b0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 77 additions and 45 deletions

View file

@ -12,7 +12,7 @@ use typst::ecow::EcoVec;
use typst::syntax::Span;
use crate::snapshot::CompileSnapshot;
use crate::{CompilerFeat, EntryReader};
use crate::{CompilerFeat, CompilerWorld, EntryReader};
type AnyArc = Arc<dyn std::any::Any + Send + Sync>;
@ -218,6 +218,43 @@ pub type HtmlCompilationTask = CompilationTask<TypstHtmlDocument>;
pub struct CompilationTask<D>(std::marker::PhantomData<D>);
impl<D: typst::Document + Send + Sync + 'static> CompilationTask<D> {
pub fn execute<F: CompilerFeat>(world: &CompilerWorld<F>) -> Warned<SourceResult<Arc<D>>> {
let is_paged_compilation = TypeId::of::<D>() == TypeId::of::<TypstPagedDocument>();
let is_html_compilation = TypeId::of::<D>() == TypeId::of::<TypstHtmlDocument>();
let mut world = if is_paged_compilation {
world.paged_task()
} else if is_html_compilation {
// todo: create html world once
world.html_task()
} else {
Cow::Borrowed(world)
};
world.to_mut().set_is_compiling(true);
let compiled = typst::compile::<D>(world.as_ref());
world.to_mut().set_is_compiling(false);
let exclude_html_warnings = if !is_html_compilation {
compiled.warnings
} else if compiled.warnings.len() == 1
&& compiled.warnings[0]
.message
.starts_with("html export is under active development")
{
EcoVec::new()
} else {
compiled.warnings
};
Warned {
output: compiled.output.map(Arc::new),
warnings: exclude_html_warnings,
}
}
}
impl<F: CompilerFeat, D> WorldComputable<F> for CompilationTask<D>
where
D: typst::Document + Send + Sync + 'static,
@ -227,40 +264,7 @@ where
fn compute(graph: &Arc<WorldComputeGraph<F>>) -> Result<Self::Output> {
let enabled = graph.must_get::<FlagTask<CompilationTask<D>>>()?.enabled;
Ok(enabled.then(|| {
// todo: create html world once
let is_paged_compilation = TypeId::of::<D>() == TypeId::of::<TypstPagedDocument>();
let is_html_compilation = TypeId::of::<D>() == TypeId::of::<TypstHtmlDocument>();
let mut world = if is_paged_compilation {
graph.snap.world.paged_task()
} else if is_html_compilation {
graph.snap.world.html_task()
} else {
Cow::Borrowed(&graph.snap.world)
};
world.to_mut().set_is_compiling(true);
let compiled = typst::compile::<D>(world.as_ref());
world.to_mut().set_is_compiling(false);
let exclude_html_warnings = if !is_html_compilation {
compiled.warnings
} else if compiled.warnings.len() == 1
&& compiled.warnings[0]
.message
.starts_with("html export is under active development")
{
EcoVec::new()
} else {
compiled.warnings
};
Warned {
output: compiled.output.map(Arc::new),
warnings: exclude_html_warnings,
}
}))
Ok(enabled.then(|| CompilationTask::<D>::execute(&graph.snap.world)))
}
}

View file

@ -1,10 +1,13 @@
//! The actor that handles various document export, like PDF and SVG export.
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::atomic::AtomicUsize;
use std::{path::PathBuf, sync::Arc};
use std::sync::{Arc, OnceLock};
use reflexo::ImmutPath;
use reflexo_typst::CompilationTask;
use tinymist_project::LspWorld;
use tinymist_std::error::prelude::*;
use tinymist_std::typst::TypstDocument;
use tinymist_task::{convert_datetime, get_page_selection, ExportTarget, TextExport};
@ -186,17 +189,30 @@ impl ExportTask {
let doc = &doc;
// static BLANK: Lazy<Page> = Lazy::new(Page::default);
let html_doc = || {
Ok(match &doc {
TypstDocument::Html(html_doc) => html_doc,
TypstDocument::Paged(_) => bail!("expected html document, found Paged"),
})
// todo: check warnings and errors inside
let html_once = OnceLock::new();
let html_doc = || -> Result<_> {
html_once
.get_or_init(|| -> Result<_> {
Ok(match &doc {
TypstDocument::Html(html_doc) => html_doc.clone(),
TypstDocument::Paged(_) => extra_compile_for_export(&snap.world)?,
})
})
.as_ref()
.map_err(|e| e.clone())
};
let page_once = OnceLock::new();
let paged_doc = || {
Ok(match &doc {
TypstDocument::Paged(paged_doc) => paged_doc,
TypstDocument::Html(_) => bail!("expected paged document, found HTML"),
})
page_once
.get_or_init(|| -> Result<_> {
Ok(match &doc {
TypstDocument::Paged(paged_doc) => paged_doc.clone(),
TypstDocument::Html(_) => extra_compile_for_export(&snap.world)?,
})
})
.as_ref()
.map_err(|e| e.clone())
};
let first_page = || {
paged_doc()?
@ -373,6 +389,18 @@ fn log_err<T>(artifact: Result<T>) -> Option<T> {
}
}
fn extra_compile_for_export<D: typst::Document + Send + Sync + 'static>(
world: &LspWorld,
) -> Result<Arc<D>> {
let res = tokio::task::block_in_place(|| CompilationTask::<D>::execute(world));
match res.output {
Ok(v) => Ok(v),
Err(e) if e.is_empty() => bail!("failed to compile: internal error"),
Err(e) => bail!("failed to compile: {}", e[0].message),
}
}
/// Serialize data to the output format.
fn serialize(data: &impl serde::Serialize, format: &str, pretty: bool) -> Result<String> {
Ok(match format {