diff --git a/crates/tinymist-query/src/tests.rs b/crates/tinymist-query/src/tests.rs index 49e26525..be7713b5 100644 --- a/crates/tinymist-query/src/tests.rs +++ b/crates/tinymist-query/src/tests.rs @@ -124,6 +124,7 @@ pub fn run_with_sources(source: &str, f: impl FnOnce(&mut LspUniverse, PathBu }; let mut world = LspUniverseBuilder::build( EntryState::new_rooted(root.as_path().into(), None), + Default::default(), Arc::new( LspUniverseBuilder::resolve_fonts(CompileFontArgs { ignore_system_fonts: true, @@ -131,8 +132,7 @@ pub fn run_with_sources(source: &str, f: impl FnOnce(&mut LspUniverse, PathBu }) .unwrap(), ), - Default::default(), - None, + LspUniverseBuilder::resolve_package(None, None), ) .unwrap(); let sources = source.split("-----"); diff --git a/crates/tinymist-world/src/lib.rs b/crates/tinymist-world/src/lib.rs index 8331031c..65b37f4f 100644 --- a/crates/tinymist-world/src/lib.rs +++ b/crates/tinymist-world/src/lib.rs @@ -65,6 +65,22 @@ pub struct CompileFontArgs { pub ignore_system_fonts: bool, } +/// Arguments related to where packages are stored in the system. +#[derive(Debug, Clone, Parser, Default, PartialEq, Eq)] +pub struct CompilePackageArgs { + /// Custom path to local packages, defaults to system-dependent location + #[clap(long = "package-path", env = "TYPST_PACKAGE_PATH", value_name = "DIR")] + pub package_path: Option, + + /// Custom path to package cache, defaults to system-dependent location + #[clap( + long = "package-cache-path", + env = "TYPST_PACKAGE_CACHE_PATH", + value_name = "DIR" + )] + pub package_cache_path: Option, +} + /// Common arguments of compile, watch, and query. #[derive(Debug, Clone, Parser, Default)] pub struct CompileOnceArgs { @@ -89,6 +105,10 @@ pub struct CompileOnceArgs { #[clap(flatten)] pub font: CompileFontArgs, + /// Package related arguments. + #[clap(flatten)] + pub package: CompilePackageArgs, + /// The document's creation date formatted as a UNIX timestamp. /// /// For more information, see . @@ -104,26 +124,26 @@ pub struct CompileOnceArgs { /// Path to CA certificate file for network access, especially for /// downloading typst packages. #[clap(long = "cert", env = "TYPST_CERT", value_name = "CERT_PATH")] - pub certification: Option, + pub cert: Option, } impl CompileOnceArgs { /// Get a universe instance from the given arguments. pub fn resolve(&self) -> anyhow::Result { let entry = self.entry()?.try_into()?; - let fonts = LspUniverseBuilder::resolve_fonts(self.font.clone())?; let inputs = self .inputs .iter() .map(|(k, v)| (Str::from(k.as_str()), Value::Str(Str::from(v.as_str())))) .collect(); - let cert_path = self.certification.clone(); + let fonts = LspUniverseBuilder::resolve_fonts(self.font.clone())?; + let package = LspUniverseBuilder::resolve_package(self.cert.clone(), Some(&self.package)); LspUniverseBuilder::build( entry, - Arc::new(fonts), Arc::new(LazyHash::new(inputs)), - cert_path, + Arc::new(fonts), + package, ) .context("failed to create universe") } @@ -185,15 +205,15 @@ impl LspUniverseBuilder { /// See [`LspCompilerFeat`] for instantiation details. pub fn build( entry: EntryState, - font_resolver: Arc, inputs: ImmutDict, - cert_path: Option, + font_resolver: Arc, + package_registry: HttpsRegistry, ) -> ZResult { Ok(LspUniverse::new_raw( entry, Some(inputs), Vfs::new(SystemAccessModel {}), - HttpsRegistry::new(cert_path), + package_registry, font_resolver, )) } @@ -209,6 +229,14 @@ impl LspUniverseBuilder { })?; Ok(searcher.into()) } + + /// Resolve package registry from given options. + pub fn resolve_package( + cert_path: Option, + args: Option<&CompilePackageArgs>, + ) -> HttpsRegistry { + HttpsRegistry::new(cert_path, args) + } } /// Parses key/value pairs split by the first equal sign. diff --git a/crates/tinymist-world/src/package.rs b/crates/tinymist-world/src/package.rs index 46ae3020..c489ab14 100644 --- a/crates/tinymist-world/src/package.rs +++ b/crates/tinymist-world/src/package.rs @@ -10,6 +10,8 @@ use reflexo_typst::ImmutPath; use typst_kit::download::{DownloadState, Downloader}; use typst_kit::package::PackageStorage; +use crate::CompilePackageArgs; + /// The https package registry for tinymist. pub struct HttpsRegistry { /// The path at which local packages (`@local` packages) are stored. @@ -53,9 +55,21 @@ impl std::ops::Deref for HttpsRegistry { impl HttpsRegistry { /// Create a new registry. - pub fn new(cert_path: Option) -> Self { + pub fn new(cert_path: Option, package_args: Option<&CompilePackageArgs>) -> Self { + let local_dir = OnceLock::new(); + if let Some(dir) = package_args.and_then(|args| args.package_path.as_deref()) { + let _ = local_dir.set(Some(dir.into())); + } + + let cache_dir = OnceLock::new(); + if let Some(dir) = package_args.and_then(|args| args.package_cache_path.as_deref()) { + let _ = cache_dir.set(Some(dir.into())); + } + Self { cert_path, + local_dir, + cache_dir, ..Default::default() } } diff --git a/crates/tinymist/src/actor/mod.rs b/crates/tinymist/src/actor/mod.rs index c047ce10..f9be4f47 100644 --- a/crates/tinymist/src/actor/mod.rs +++ b/crates/tinymist/src/actor/mod.rs @@ -133,12 +133,16 @@ impl LanguageState { let compile_handle = handle.clone(); let cache = self.cache.clone(); let cert_path = self.compile_config().determine_certification_path(); + let package = self.compile_config().determine_package_opts(); self.client.handle.spawn_blocking(move || { // Create the world let font_resolver = font_resolver.wait().clone(); - let verse = LspUniverseBuilder::build(entry_.clone(), font_resolver, inputs, cert_path) - .expect("incorrect options"); + let package_registry = + LspUniverseBuilder::resolve_package(cert_path.clone(), Some(&package)); + let verse = + LspUniverseBuilder::build(entry_.clone(), inputs, font_resolver, package_registry) + .expect("incorrect options"); // Create the actor let server = CompileServerActor::new_with( diff --git a/crates/tinymist/src/init.rs b/crates/tinymist/src/init.rs index 4daafb44..d59df670 100644 --- a/crates/tinymist/src/init.rs +++ b/crates/tinymist/src/init.rs @@ -553,8 +553,9 @@ impl CompileConfig { root_dir: command.root, inputs: Arc::new(LazyHash::new(inputs)), font: command.font, + package: command.package, creation_timestamp: command.creation_timestamp, - cert: command.certification, + cert: command.cert, }); } @@ -700,6 +701,14 @@ impl CompileConfig { opts } + /// Determines the package options. + pub fn determine_package_opts(&self) -> CompilePackageArgs { + if let Some(extras) = &self.typst_extra_args { + return extras.package.clone(); + } + CompilePackageArgs::default() + } + /// Determines the font resolver. pub fn determine_fonts(&self) -> Deferred> { // todo: on font resolving failure, downgrade to a fake font book @@ -857,6 +866,8 @@ pub struct CompileExtraOpts { pub inputs: ImmutDict, /// Additional font paths. pub font: CompileFontArgs, + /// Package related arguments. + pub package: CompilePackageArgs, /// The creation timestamp for various output. pub creation_timestamp: Option>, /// Path to certification file diff --git a/crates/typlite/src/tests.rs b/crates/typlite/src/tests.rs index 3f1ed595..1c3d4860 100644 --- a/crates/typlite/src/tests.rs +++ b/crates/typlite/src/tests.rs @@ -10,20 +10,19 @@ use typst_syntax::Source; use super::*; fn conv_(s: &str, for_docs: bool) -> EcoString { - static FONT_RESOLVER: LazyLock>> = LazyLock::new(|| { - Ok(Arc::new( + static FONT_RESOLVER: LazyLock> = LazyLock::new(|| { + Arc::new( LspUniverseBuilder::resolve_fonts(CompileFontArgs::default()) - .map_err(|e| format!("{e:?}"))?, - )) + .expect("cannot resolve default fonts"), + ) }); - let font_resolver = FONT_RESOLVER.clone(); let cwd = std::env::current_dir().unwrap(); let main = Source::detached(s); let mut universe = LspUniverseBuilder::build( EntryState::new_rooted(cwd.as_path().into(), Some(main.id())), - font_resolver.unwrap(), Default::default(), + FONT_RESOLVER.clone(), Default::default(), ) .unwrap();