revert: undo "feat: add package related arguments to typstExtraArgs (#832 and #833)" (#891)

* Revert "fix: cross build with `vendor-openssl` feature and bump version to 0.12.4-rc2 (#884)"

This reverts commit 254c4ce087.

* Revert "feat: add package related arguments to `typstExtraArgs` (#833)"

This reverts commit 8e36f25cf2.

* Revert "feat: use typst-kit's package functions (#832)"

This reverts commit d2afe78fee.

* build: bump version to 0.12.4-rc3

* docs: update changelog
This commit is contained in:
Myriad-Dreamin 2024-11-23 14:17:40 +08:00 committed by GitHub
parent 254c4ce087
commit e1848488f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 672 additions and 386 deletions

View file

@ -8,7 +8,7 @@ use parking_lot::Mutex;
use reflexo_typst::typst::prelude::*;
use reflexo_typst::{package::PackageSpec, TypstFileId};
use serde::{Deserialize, Serialize};
use tinymist_world::package::HttpsRegistry;
use tinymist_world::https::HttpsRegistry;
use typst::diag::{EcoString, StrResult};
use typst::syntax::package::PackageManifest;
use typst::syntax::VirtualPath;

View file

@ -131,7 +131,6 @@ pub fn run_with_sources<T>(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,
@ -139,7 +138,8 @@ pub fn run_with_sources<T>(source: &str, f: impl FnOnce(&mut LspUniverse, PathBu
})
.unwrap(),
),
LspUniverseBuilder::resolve_package(None, None),
Default::default(),
None,
)
.unwrap();
let sources = source.split("-----");

View file

@ -23,13 +23,28 @@ log.workspace = true
reflexo-typst.workspace = true
reflexo-typst-shim = { workspace = true, features = ["nightly"] }
typst.workspace = true
typst-kit.workspace = true
tinymist-assets = { workspace = true }
typst-assets = { workspace = true, features = ["fonts"] }
dirs.workspace = true
parking_lot.workspace = true
flate2 = "1"
tar = "0.4"
[target.'cfg(not(any(target_arch = "riscv64", target_arch = "wasm32", all(target_os = "windows", target_arch = "aarch64"))))'.dependencies]
reqwest = { version = "^0.11", default-features = false, features = [
"rustls-tls",
"blocking",
"multipart",
] }
[target.'cfg(any(target_arch = "riscv64", all(target_os = "windows", target_arch = "aarch64")))'.dependencies]
reqwest = { version = "^0.11", default-features = false, features = [
"native-tls",
"blocking",
"multipart",
] }
[features]
no-content-hint = ["reflexo-typst/no-content-hint"]

View file

@ -0,0 +1,257 @@
//! Https registry for tinymist.
use std::path::Path;
use std::sync::OnceLock;
use std::{path::PathBuf, sync::Arc};
use log::error;
use parking_lot::Mutex;
use reflexo_typst::package::{DummyNotifier, Notifier, PackageError, PackageRegistry, PackageSpec};
use reflexo_typst::typst::{
diag::{eco_format, EcoString},
syntax::package::PackageVersion,
};
use reqwest::{blocking::Response, Certificate};
/// The http registry without typst.ts to implement more for tinymist.
pub struct HttpsRegistry {
notifier: Arc<Mutex<dyn Notifier + Send>>,
packages: OnceLock<Vec<(PackageSpec, Option<EcoString>)>>,
cert_path: Option<PathBuf>,
data_dir_cache: OnceLock<Option<Arc<Path>>>,
cache_dir_cache: OnceLock<Option<Arc<Path>>>,
// package_dir_cache: RwLock<HashMap<PackageSpec, Result<Arc<Path>, PackageError>>>,
}
impl Default for HttpsRegistry {
fn default() -> Self {
Self {
notifier: Arc::new(Mutex::<DummyNotifier>::default()),
// todo: reset cache
packages: OnceLock::new(),
// Default to None
cert_path: None,
data_dir_cache: OnceLock::new(),
cache_dir_cache: OnceLock::new(),
// package_dir_cache: RwLock::new(HashMap::new()),
}
}
}
impl HttpsRegistry {
/// Create a new registry.
pub fn new(cert_path: Option<PathBuf>) -> Self {
Self {
cert_path,
..Default::default()
}
}
/// Get local path option
pub fn local_path(&self) -> Option<Box<Path>> {
if let Some(data_dir) = self.data_dir() {
if data_dir.exists() {
return Some(data_dir.join("typst/packages").into());
}
}
None
}
fn data_dir(&self) -> Option<&Arc<Path>> {
self.data_dir_cache
.get_or_init(|| dirs::data_dir().map(From::from))
.as_ref()
}
fn cache_dir(&self) -> Option<&Arc<Path>> {
self.cache_dir_cache
.get_or_init(|| dirs::cache_dir().map(From::from))
.as_ref()
}
/// Get data & cache dir
pub fn paths(&self) -> Vec<Box<Path>> {
let mut res = vec![];
if let Some(data_dir) = self.data_dir() {
let dir: Box<Path> = data_dir.join("typst/packages").into();
if dir.exists() {
res.push(dir);
}
}
if let Some(cache_dir) = self.cache_dir() {
let dir: Box<Path> = cache_dir.join("typst/packages").into();
if dir.exists() {
res.push(dir);
}
}
res
}
/// Make a package available in the on-disk cache.
pub fn prepare_package(&self, spec: &PackageSpec) -> Result<Arc<Path>, PackageError> {
// let cache = self.package_dir_cache.read();
// if let Some(dir) = cache.get(spec) {
// return dir.clone();
// }
// drop(cache);
// let mut cache = self.package_dir_cache.write();
// if let Some(dir) = cache.get(spec) {
// return dir.clone();
// }
// let dir = self.prepare_package_(spec);
// cache.insert(spec.clone(), dir.clone());
// dir
self.prepare_package_(spec)
}
/// Make a package available in the on-disk cache.
pub fn prepare_package_(&self, spec: &PackageSpec) -> Result<Arc<Path>, PackageError> {
let subdir = format!(
"typst/packages/{}/{}/{}",
spec.namespace, spec.name, spec.version
);
if let Some(data_dir) = self.data_dir() {
let dir = data_dir.join(&subdir);
if dir.exists() {
return Ok(dir.into());
}
}
if let Some(cache_dir) = self.cache_dir() {
let dir = cache_dir.join(&subdir);
// Download from network if it doesn't exist yet.
if spec.namespace == "preview" && !dir.exists() {
self.download_package(spec, &dir)?;
}
if dir.exists() {
return Ok(dir.into());
}
}
Err(PackageError::NotFound(spec.clone()))
}
/// Download a package over the network.
fn download_package(&self, spec: &PackageSpec, package_dir: &Path) -> Result<(), PackageError> {
let url = format!(
"https://packages.typst.org/preview/{}-{}.tar.gz",
spec.name, spec.version
);
self.notifier.lock().downloading(spec);
threaded_http(&url, self.cert_path.as_deref(), |resp| {
let reader = match resp.and_then(|r| r.error_for_status()) {
Ok(response) => response,
Err(err) if matches!(err.status().map(|s| s.as_u16()), Some(404)) => {
return Err(PackageError::NotFound(spec.clone()))
}
Err(err) => return Err(PackageError::NetworkFailed(Some(eco_format!("{err}")))),
};
let decompressed = flate2::read::GzDecoder::new(reader);
tar::Archive::new(decompressed)
.unpack(package_dir)
.map_err(|err| {
std::fs::remove_dir_all(package_dir).ok();
PackageError::MalformedArchive(Some(eco_format!("{err}")))
})
})
.ok_or_else(|| PackageError::Other(Some(eco_format!("cannot spawn http thread"))))?
}
}
impl PackageRegistry for HttpsRegistry {
fn resolve(&self, spec: &PackageSpec) -> Result<std::sync::Arc<Path>, PackageError> {
self.prepare_package(spec)
}
fn packages(&self) -> &[(PackageSpec, Option<EcoString>)] {
self.packages.get_or_init(|| {
let url = "https://packages.typst.org/preview/index.json";
threaded_http(url, self.cert_path.as_deref(), |resp| {
let reader = match resp.and_then(|r| r.error_for_status()) {
Ok(response) => response,
Err(err) => {
// todo: silent error
error!("Failed to fetch package index: {err} from {url}");
return vec![];
}
};
#[derive(serde::Deserialize)]
struct RemotePackageIndex {
name: EcoString,
version: PackageVersion,
description: Option<EcoString>,
}
let index: Vec<RemotePackageIndex> = match serde_json::from_reader(reader) {
Ok(index) => index,
Err(err) => {
error!("Failed to parse package index: {err} from {url}");
return vec![];
}
};
index
.into_iter()
.map(|e| {
(
PackageSpec {
namespace: "preview".into(),
name: e.name,
version: e.version,
},
e.description,
)
})
.collect::<Vec<_>>()
})
.unwrap_or_default()
})
}
}
fn threaded_http<T: Send + Sync>(
url: &str,
cert_path: Option<&Path>,
f: impl FnOnce(Result<Response, reqwest::Error>) -> T + Send + Sync,
) -> Option<T> {
std::thread::scope(|s| {
s.spawn(move || {
let client_builder = reqwest::blocking::Client::builder();
let client = if let Some(cert_path) = cert_path {
let cert = std::fs::read(cert_path)
.ok()
.and_then(|buf| Certificate::from_pem(&buf).ok());
if let Some(cert) = cert {
client_builder.add_root_certificate(cert).build().unwrap()
} else {
client_builder.build().unwrap()
}
} else {
client_builder.build().unwrap()
};
f(client.get(url).send())
})
.join()
.ok()
})
}

View file

@ -21,8 +21,8 @@ use reflexo_typst::vfs::{system::SystemAccessModel, Vfs};
use reflexo_typst::{CompilerFeat, CompilerUniverse, CompilerWorld, TypstDict};
use serde::{Deserialize, Serialize};
pub mod package;
use package::HttpsRegistry;
pub mod https;
use https::HttpsRegistry;
const ENV_PATH_SEP: char = if cfg!(windows) { ';' } else { ':' };
@ -65,22 +65,6 @@ 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<PathBuf>,
/// 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<PathBuf>,
}
/// Common arguments of compile, watch, and query.
#[derive(Debug, Clone, Parser, Default)]
pub struct CompileOnceArgs {
@ -105,10 +89,6 @@ 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 <https://reproducible-builds.org/specs/source-date-epoch/>.
@ -124,26 +104,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 cert: Option<PathBuf>,
pub certification: Option<PathBuf>,
}
impl CompileOnceArgs {
/// Get a universe instance from the given arguments.
pub fn resolve(&self) -> anyhow::Result<LspUniverse> {
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 fonts = LspUniverseBuilder::resolve_fonts(self.font.clone())?;
let package = LspUniverseBuilder::resolve_package(self.cert.clone(), Some(&self.package));
let cert_path = self.certification.clone();
LspUniverseBuilder::build(
entry,
Arc::new(LazyHash::new(inputs)),
Arc::new(fonts),
package,
Arc::new(LazyHash::new(inputs)),
cert_path,
)
.context("failed to create universe")
}
@ -205,15 +185,15 @@ impl LspUniverseBuilder {
/// See [`LspCompilerFeat`] for instantiation details.
pub fn build(
entry: EntryState,
inputs: ImmutDict,
font_resolver: Arc<FontResolverImpl>,
package_registry: HttpsRegistry,
inputs: ImmutDict,
cert_path: Option<PathBuf>,
) -> ZResult<LspUniverse> {
Ok(LspUniverse::new_raw(
entry,
Some(inputs),
Vfs::new(SystemAccessModel {}),
package_registry,
HttpsRegistry::new(cert_path),
font_resolver,
))
}
@ -229,14 +209,6 @@ impl LspUniverseBuilder {
})?;
Ok(searcher.into())
}
/// Resolve package registry from given options.
pub fn resolve_package(
cert_path: Option<PathBuf>,
args: Option<&CompilePackageArgs>,
) -> HttpsRegistry {
HttpsRegistry::new(cert_path, args)
}
}
/// Parses key/value pairs split by the first equal sign.

View file

@ -1,164 +0,0 @@
//! Https registry for tinymist.
use std::sync::OnceLock;
use std::{path::PathBuf, sync::Arc};
use parking_lot::Mutex;
use reflexo_typst::package::{DummyNotifier, Notifier, PackageError, PackageRegistry, PackageSpec};
use reflexo_typst::typst::diag::EcoString;
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.
local_dir: OnceLock<Option<ImmutPath>>,
/// The path at which non-local packages (`@preview` packages) should be
/// stored when downloaded.
cache_dir: OnceLock<Option<ImmutPath>>,
/// The cached index of the preview namespace.
index: OnceLock<Vec<(PackageSpec, Option<EcoString>)>>,
/// lazily initialized package storage.
storage: OnceLock<PackageStorage>,
cert_path: Option<PathBuf>,
notifier: Arc<Mutex<dyn Notifier + Send>>,
// package_dir_cache: RwLock<HashMap<PackageSpec, Result<ImmutPath, PackageError>>>,
}
impl Default for HttpsRegistry {
fn default() -> Self {
Self {
notifier: Arc::new(Mutex::<DummyNotifier>::default()),
// todo: reset cache
index: OnceLock::new(),
// Default to None
cert_path: None,
local_dir: OnceLock::new(),
cache_dir: OnceLock::new(),
storage: OnceLock::new(),
// package_dir_cache: RwLock::new(HashMap::new()),
}
}
}
impl std::ops::Deref for HttpsRegistry {
type Target = PackageStorage;
fn deref(&self) -> &Self::Target {
self.storage()
}
}
impl HttpsRegistry {
/// Create a new registry.
pub fn new(cert_path: Option<PathBuf>, 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()
}
}
/// Get local path option
pub fn local_path(&self) -> Option<ImmutPath> {
self.data_dir().cloned()
}
fn data_dir(&self) -> Option<&ImmutPath> {
self.local_dir
.get_or_init(|| Some(dirs::data_dir()?.join("typst/packages").into()))
.as_ref()
}
fn cache_dir(&self) -> Option<&ImmutPath> {
self.cache_dir
.get_or_init(|| Some(dirs::cache_dir()?.join("typst/packages").into()))
.as_ref()
}
/// Get data & cache dir
pub fn paths(&self) -> Vec<ImmutPath> {
let mut res = Vec::with_capacity(2);
if let Some(data_dir) = self.data_dir() {
res.push(data_dir.clone());
}
if let Some(cache_dir) = self.cache_dir() {
res.push(cache_dir.clone())
}
res
}
/// Get `typst-kit` implementing package storage
pub fn storage(&self) -> &PackageStorage {
self.storage.get_or_init(|| {
let user_agent = concat!("typst/", env!("CARGO_PKG_VERSION"));
let downloader = match self.cert_path.clone() {
Some(cert) => Downloader::with_path(user_agent, cert),
None => Downloader::new(user_agent),
};
PackageStorage::new(
self.cache_dir().map(|s| s.as_ref().into()),
self.data_dir().map(|s| s.as_ref().into()),
downloader,
)
})
}
/// Make a package available in the on-disk cache.
pub fn prepare_package(&self, spec: &PackageSpec) -> Result<PathBuf, PackageError> {
self.storage()
.prepare_package(spec, &mut NotifierProgress(self.notifier.clone(), spec))
}
}
impl PackageRegistry for HttpsRegistry {
fn resolve(&self, spec: &PackageSpec) -> Result<ImmutPath, PackageError> {
self.prepare_package(spec).map(From::from)
}
fn packages(&self) -> &[(PackageSpec, Option<EcoString>)] {
self.index.get_or_init(|| {
let packages = self.storage().download_index().unwrap_or_default().iter();
packages
.map(|e| {
(
PackageSpec {
namespace: "preview".into(),
name: e.name.clone(),
version: e.version,
},
e.description.clone(),
)
})
.collect()
})
}
}
struct NotifierProgress<'a>(Arc<Mutex<dyn Notifier + Send>>, &'a PackageSpec);
impl typst_kit::download::Progress for NotifierProgress<'_> {
fn print_start(&mut self) {
self.0.lock().downloading(self.1);
}
fn print_progress(&mut self, _state: &DownloadState) {}
fn print_finish(&mut self, _state: &DownloadState) {}
}

View file

@ -81,7 +81,6 @@ open = { workspace = true, optional = true }
dirs.workspace = true
base64.workspace = true
rayon.workspace = true
typst-kit.workspace = true
typst-ansi-hl = "0.2.0"
@ -100,9 +99,6 @@ dhat-heap = ["dhat"]
# into the binary.
embed-fonts = ["typst-assets/fonts"]
# Whether to vendor OpenSSL. Not applicable to Windows and macOS builds.
vendor-openssl = ["typst-kit/vendor-openssl"]
# Disable the default content hint.
# This requires modifying typst.
no-content-hint = [

View file

@ -134,16 +134,12 @@ 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 package_registry =
LspUniverseBuilder::resolve_package(cert_path.clone(), Some(&package));
let verse =
LspUniverseBuilder::build(entry_.clone(), inputs, font_resolver, package_registry)
.expect("incorrect options");
let verse = LspUniverseBuilder::build(entry_.clone(), font_resolver, inputs, cert_path)
.expect("incorrect options");
// Create the actor
let server = CompileServerActor::new_with(

View file

@ -334,7 +334,7 @@ impl LanguageState {
/// Initialize a new template.
pub fn init_template(&mut self, mut args: Vec<JsonValue>) -> AnySchedulableResponse {
use crate::tool::package::{self, TemplateSource};
use crate::tool::package::{self, determine_latest_version, TemplateSource};
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
@ -359,7 +359,7 @@ impl LanguageState {
// Try to parse without version, but prefer the error message of the
// normal package spec parsing if it fails.
let spec: VersionlessPackageSpec = from_source.parse().map_err(|_| err)?;
let version = snap.world.registry.determine_latest_version(&spec)?;
let version = determine_latest_version(&snap.world, &spec)?;
StrResult::Ok(spec.at(version))
})
.map_err(map_string_err("failed to parse package spec"))
@ -386,7 +386,7 @@ impl LanguageState {
/// Get the entry of a template.
pub fn get_template_entry(&mut self, mut args: Vec<JsonValue>) -> AnySchedulableResponse {
use crate::tool::package::{self, TemplateSource};
use crate::tool::package::{self, determine_latest_version, TemplateSource};
let from_source = get_arg!(args[0] as String);
@ -404,7 +404,7 @@ impl LanguageState {
// Try to parse without version, but prefer the error message of the
// normal package spec parsing if it fails.
let spec: VersionlessPackageSpec = from_source.parse().map_err(|_| err)?;
let version = snap.world.registry.determine_latest_version(&spec)?;
let version = determine_latest_version(&snap.world, &spec)?;
StrResult::Ok(spec.at(version))
})
.map_err(map_string_err("failed to parse package spec"))
@ -547,7 +547,6 @@ impl LanguageState {
just_future(async move {
let snap = snap.receive().await.map_err(z_internal_error)?;
let paths = snap.world.registry.paths();
let paths = paths.iter().map(|p| p.as_ref()).collect::<Vec<_>>();
serde_json::to_value(paths).map_err(|e| internal_error(e.to_string()))
})
}
@ -560,8 +559,12 @@ impl LanguageState {
let snap = self.primary().snapshot().map_err(z_internal_error)?;
just_future(async move {
let snap = snap.receive().await.map_err(z_internal_error)?;
let paths = snap.world.registry.local_path();
let paths = paths.as_deref().into_iter().collect::<Vec<_>>();
let paths = snap
.world
.registry
.local_path()
.into_iter()
.collect::<Vec<_>>();
serde_json::to_value(paths).map_err(|e| internal_error(e.to_string()))
})
}

View file

@ -576,9 +576,8 @@ 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.cert,
cert: command.certification,
});
}
@ -724,14 +723,6 @@ 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<Arc<FontResolverImpl>> {
// todo: on font resolving failure, downgrade to a fake font book
@ -889,8 +880,6 @@ 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<chrono::DateTime<chrono::Utc>>,
/// Path to certification file

View file

@ -1,4 +1,42 @@
//! Package management tools.
use reflexo_typst::package::PackageRegistry;
use typst::diag::{eco_format, StrResult};
use typst::syntax::package::{PackageVersion, VersionlessPackageSpec};
use crate::LspWorld;
mod init;
pub use init::*;
/// Try to determine the latest version of a package.
pub fn determine_latest_version(
world: &LspWorld,
spec: &VersionlessPackageSpec,
) -> StrResult<PackageVersion> {
if spec.namespace == "preview" {
let packages = world.registry.packages();
packages
.iter()
.filter(|(package, _)| package.namespace == "preview" && package.name == spec.name)
.map(|(package, _)| package.version)
.max()
.ok_or_else(|| eco_format!("failed to find package {spec}"))
} else {
// For other namespaces, search locally. We only search in the data
// directory and not the cache directory, because the latter is not
// intended for storage of local packages.
let subdir = format!("typst/packages/{}/{}", spec.namespace, spec.name);
world
.registry
.local_path()
.into_iter()
.flat_map(|dir| std::fs::read_dir(dir.join(&subdir)).ok())
.flatten()
.filter_map(|entry| entry.ok())
.map(|entry| entry.path())
.filter_map(|path| path.file_name()?.to_string_lossy().parse().ok())
.max()
.ok_or_else(|| eco_format!("please specify the desired version"))
}
}

View file

@ -10,19 +10,20 @@ use typst_syntax::Source;
use super::*;
fn conv_(s: &str, for_docs: bool) -> EcoString {
static FONT_RESOLVER: LazyLock<Arc<FontResolverImpl>> = LazyLock::new(|| {
Arc::new(
static FONT_RESOLVER: LazyLock<Result<Arc<FontResolverImpl>>> = LazyLock::new(|| {
Ok(Arc::new(
LspUniverseBuilder::resolve_fonts(CompileFontArgs::default())
.expect("cannot resolve default fonts"),
)
.map_err(|e| format!("{e:?}"))?,
))
});
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();