diff --git a/cli/src/compile.rs b/cli/src/compile.rs index ec3c3a53..b88b3c29 100644 --- a/cli/src/compile.rs +++ b/cli/src/compile.rs @@ -2,7 +2,10 @@ use std::path::Path; use typst::doc::Document; use typst_ts_compiler::{ - service::{CompileActor, CompileDriver, CompileExporter, Compiler, DynamicLayoutCompiler}, + service::{ + features::{FeatureSet, DIAG_FMT_FEATURE}, + CompileActor, CompileDriver, CompileExporter, Compiler, DynamicLayoutCompiler, + }, TypstSystemWorld, }; use typst_ts_core::{config::CompileOpts, exporter_builtins::GroupExporter, path::PathClean}; @@ -101,10 +104,14 @@ pub fn compile_export(args: CompileArgs, exporter: GroupExporter) -> ! let watch_root = driver.world().root.as_ref().to_owned(); + let feature_set = + FeatureSet::default().configure(&DIAG_FMT_FEATURE, args.diagnostic_format.into()); + // CompileExporter + DynamicLayoutCompiler + WatchDriver let driver = CompileExporter::new(driver).with_exporter(exporter); let driver = DynamicLayoutCompiler::new(driver, output_dir).with_enable(args.dynamic_layout); - let actor = CompileActor::new(driver, watch_root).with_watch(args.watch); + let actor = + CompileActor::new_with_features(driver, watch_root, feature_set).with_watch(args.watch); utils::async_continue(async move { utils::logical_exit(actor.run()); diff --git a/cli/src/lib.rs b/cli/src/lib.rs index ae1624f8..68f605d4 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -1,3 +1,4 @@ +use core::fmt; use std::path::PathBuf; pub mod compile; @@ -148,6 +149,14 @@ pub struct CompileArgs { #[clap(long)] pub format: Vec, + /// In which format to emit diagnostics + #[clap( + long, + default_value_t = DiagnosticFormat::Human, + value_parser = clap::value_parser!(DiagnosticFormat) + )] + pub diagnostic_format: DiagnosticFormat, + /// Enable tracing. /// Possible usage: --trace=verbosity={0..3} /// where verbosity: {0..3} -> {warning, info, debug, trace} @@ -280,6 +289,37 @@ pub struct GenPackagesDocArgs { pub dynamic_layout: bool, } +/// Which format to use for diagnostics. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, ValueEnum)] +pub enum DiagnosticFormat { + Human, + Short, +} + +impl From for typst_ts_compiler::service::DiagnosticFormat { + fn from(fmt: DiagnosticFormat) -> Self { + match fmt { + DiagnosticFormat::Human => Self::Human, + DiagnosticFormat::Short => Self::Short, + } + } +} + +impl Default for DiagnosticFormat { + fn default() -> Self { + Self::Human + } +} + +impl fmt::Display for DiagnosticFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.to_possible_value() + .expect("no values are skipped") + .get_name() + .fmt(f) + } +} + pub fn get_cli(sub_command_required: bool) -> Command { let cli = Command::new("$").disable_version_flag(true); Opts::augment_args(cli).subcommand_required(sub_command_required) diff --git a/compiler/src/service/compile.rs b/compiler/src/service/compile.rs index 7ef0760e..005417fd 100644 --- a/compiler/src/service/compile.rs +++ b/compiler/src/service/compile.rs @@ -85,6 +85,8 @@ pub struct CompileActor { estimated_shadow_files: HashSet>, /// The latest compiled document. latest_doc: Option>, + /// feature set for compile_once mode. + once_feature_set: FeatureSet, /// Shared feature set for watch mode. watch_feature_set: Arc, @@ -101,13 +103,15 @@ impl CompileActor where C::World: for<'files> codespan_reporting::files::Files<'files, FileId = TypstFileId>, { - /// Create a new compiler thread. - pub fn new(compiler: C, root: PathBuf) -> Self { + pub fn new_with_features(compiler: C, root: PathBuf, feature_set: FeatureSet) -> Self { let (steal_send, steal_recv) = mpsc::unbounded_channel(); let (memory_send, memory_recv) = mpsc::unbounded_channel(); - let watch_feature_set = - Arc::new(FeatureSet::default().configure(&WITH_COMPILING_STATUS_FEATURE, true)); + let watch_feature_set = Arc::new( + feature_set + .clone() + .configure(&WITH_COMPILING_STATUS_FEATURE, true), + ); Self { compiler: CompileReporter::new(compiler) @@ -120,6 +124,7 @@ where estimated_shadow_files: Default::default(), latest_doc: None, + once_feature_set: feature_set, watch_feature_set, steal_send, @@ -130,6 +135,11 @@ where } } + /// Create a new compiler thread. + pub fn new(compiler: C, root: PathBuf) -> Self { + Self::new_with_features(compiler, root, FeatureSet::default()) + } + /// Run the compiler thread synchronously. pub fn run(self) -> bool { use tokio::runtime::Handle; @@ -146,7 +156,9 @@ where /// until it exits. async fn block_run_inner(mut self) -> bool { if !self.enable_watch { - let compiled = self.compiler.compile(&mut Default::default()); + let compiled = self + .compiler + .compile(&mut CompileEnv::default().configure(self.once_feature_set)); return compiled.is_ok(); } @@ -162,7 +174,9 @@ where /// Spawn the compiler thread. pub async fn spawn(mut self) -> Option> { if !self.enable_watch { - self.compiler.compile(&mut Default::default()).ok(); + self.compiler + .compile(&mut CompileEnv::default().configure(self.once_feature_set)) + .ok(); return None; } diff --git a/compiler/src/service/diag.rs b/compiler/src/service/diag.rs index 32d59128..f4782e63 100644 --- a/compiler/src/service/diag.rs +++ b/compiler/src/service/diag.rs @@ -18,7 +18,9 @@ use typst::{diag::SourceDiagnostic, World}; use typst::eval::eco_format; use typst_ts_core::{GenericExporter, PhantomParamData, TakeAs, TypstFileId}; -use super::features::{CompileFeature, FeatureSet, WITH_COMPILING_STATUS_FEATURE}; +use super::features::{ + CompileFeature, FeatureSet, DIAG_FMT_FEATURE, WITH_COMPILING_STATUS_FEATURE, +}; use super::{CompileReport, DiagStatus}; /// Which format to use for diagnostics. @@ -47,12 +49,20 @@ fn color_stream() -> StandardStream { pub fn print_diagnostics<'files, W: World + Files<'files, FileId = TypstFileId>>( world: &'files W, errors: ecow::EcoVec, + diagnostic_format: DiagnosticFormat, ) -> Result<(), codespan_reporting::files::Error> { - let mut w = color_stream(); - let config = term::Config { + let mut w = match diagnostic_format { + DiagnosticFormat::Human => color_stream(), + DiagnosticFormat::Short => StandardStream::stderr(ColorChoice::Never), + }; + + let mut config = term::Config { tab_width: 2, ..Default::default() }; + if diagnostic_format == DiagnosticFormat::Short { + config.display_style = term::DisplayStyle::Short; + } for diagnostic in errors { let diag = match diagnostic.severity { @@ -150,7 +160,7 @@ where } if let Some(diag) = output.diagnostics() { - let _err = print_diagnostics(world, diag); + let _err = print_diagnostics(world, diag, DIAG_FMT_FEATURE.retrieve(&features)); // todo: log in browser compiler #[cfg(feature = "system-compile")] if _err.is_err() { diff --git a/compiler/src/service/features.rs b/compiler/src/service/features.rs index d23ff00e..b1b208a5 100644 --- a/compiler/src/service/features.rs +++ b/compiler/src/service/features.rs @@ -1,6 +1,8 @@ use ecow::EcoString; use once_cell::sync::Lazy; +use super::diag::DiagnosticFormat; + #[derive(Debug, Clone, Copy)] pub struct FeatureSlot(u16); @@ -20,7 +22,7 @@ impl From<&LazyFeatureSlot> for FeatureSlot { } } -#[derive(Default)] +#[derive(Default, Clone)] pub struct FeatureSet { features: Vec, } @@ -52,6 +54,30 @@ pub trait CompileFeature { fn retrieve(&self, features: &FeatureSet) -> T; } +pub struct DiagFmtFeature; +const DIAG_FEATURE: FeatureSlot = FeatureSlot(0); +pub static DIAG_FMT_FEATURE: DiagFmtFeature = DiagFmtFeature; + +impl CompileFeature for DiagFmtFeature { + fn configure(&self, features: FeatureSet, value: DiagnosticFormat) -> FeatureSet { + features.configure_slot( + DIAG_FEATURE, + match value { + DiagnosticFormat::Human => "", + DiagnosticFormat::Short => "s", + } + .into(), + ) + } + + fn retrieve(&self, features: &FeatureSet) -> DiagnosticFormat { + features + .slot(DIAG_FEATURE) + .and_then(|s| (s == "s").then_some(DiagnosticFormat::Short)) + .unwrap_or_default() + } +} + #[derive(Default)] pub struct BuiltinFeature(LazyFeatureSlot, std::marker::PhantomData); diff --git a/compiler/src/service/mod.rs b/compiler/src/service/mod.rs index 8f1fe687..0dc1aa9f 100644 --- a/compiler/src/service/mod.rs +++ b/compiler/src/service/mod.rs @@ -44,7 +44,7 @@ pub(crate) mod session; #[cfg(feature = "system-compile")] pub use session::*; -use self::features::FeatureSet; +pub use self::{diag::DiagnosticFormat, features::FeatureSet}; #[cfg(feature = "system-compile")] pub type CompileDriver = CompileDriverImpl; @@ -358,7 +358,7 @@ where &self, errors: ecow::EcoVec, ) -> Result<(), codespan_reporting::files::Error> { - diag::print_diagnostics(self.world(), errors) + diag::print_diagnostics(self.world(), errors, DiagnosticFormat::Human) } /// Print status message to the terminal.