From 8ca6c8118ccd779b25835681c5c0c45b3a2625f9 Mon Sep 17 00:00:00 2001 From: Myriad-Dreamin <35292584+Myriad-Dreamin@users.noreply.github.com> Date: Mon, 20 Jan 2025 23:00:31 +0800 Subject: [PATCH] feat: merge fs and std crate (#1203) * feat: merge fs and std crate * fix: errors --- Cargo.lock | 22 +- Cargo.toml | 5 +- crates/tinymist-fs/Cargo.toml | 41 ---- crates/tinymist-fs/src/errors.rs | 2 - crates/tinymist-project/Cargo.toml | 3 +- crates/tinymist-project/src/args.rs | 7 +- crates/tinymist-project/src/compiler.rs | 4 +- crates/tinymist-project/src/lock.rs | 2 +- crates/tinymist-project/src/model.rs | 40 ++-- crates/tinymist-project/src/model/task.rs | 1 - crates/tinymist-project/src/world.rs | 15 +- crates/tinymist-std/Cargo.toml | 58 +++-- crates/tinymist-std/src/adt/fmap.rs | 9 +- crates/tinymist-std/src/adt/mod.rs | 2 + crates/tinymist-std/src/concepts/cow_mut.rs | 10 + crates/tinymist-std/src/concepts/hash.rs | 61 ----- crates/tinymist-std/src/concepts/marker.rs | 1 + crates/tinymist-std/src/concepts/mod.rs | 6 +- crates/tinymist-std/src/concepts/query.rs | 3 + crates/tinymist-std/src/concepts/read.rs | 2 + crates/tinymist-std/src/debug_loc.rs | 28 ++- crates/tinymist-std/src/error.rs | 215 +++++++++++++----- .../src/lib.rs => tinymist-std/src/fs.rs} | 5 +- .../src => tinymist-std/src/fs}/flock.rs | 6 +- .../src => tinymist-std/src/fs}/paths.rs | 3 +- crates/tinymist-std/src/hash.rs | 81 ++++++- crates/tinymist-std/src/lib.rs | 9 +- crates/tinymist-std/src/path.rs | 2 + crates/tinymist-std/src/time.rs | 2 + crates/tinymist-world/src/entry.rs | 2 +- crates/tinymist-world/src/font/system.rs | 2 +- crates/tinymist-world/src/font/web/mod.rs | 12 +- crates/tinymist-world/src/system.rs | 4 +- crates/tinymist-world/src/world.rs | 2 +- crates/tinymist/Cargo.toml | 1 - crates/tinymist/src/lib.rs | 3 +- crates/tinymist/src/main.rs | 26 +-- crates/tinymist/src/project.rs | 29 +-- crates/tinymist/src/server.rs | 26 +-- crates/tinymist/src/tool/preview.rs | 4 +- crates/tinymist/src/tool/project.rs | 18 +- 41 files changed, 449 insertions(+), 325 deletions(-) delete mode 100644 crates/tinymist-fs/Cargo.toml delete mode 100644 crates/tinymist-fs/src/errors.rs delete mode 100644 crates/tinymist-std/src/concepts/hash.rs rename crates/{tinymist-fs/src/lib.rs => tinymist-std/src/fs.rs} (55%) rename crates/{tinymist-fs/src => tinymist-std/src/fs}/flock.rs (99%) rename crates/{tinymist-fs/src => tinymist-std/src/fs}/paths.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index 787826d4..7de74db4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4007,7 +4007,6 @@ dependencies = [ "sync-lsp", "tinymist-assets 0.12.18 (registry+https://github.com/rust-lang/crates.io-index)", "tinymist-core", - "tinymist-fs", "tinymist-project", "tinymist-query", "tinymist-render", @@ -4078,19 +4077,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "tinymist-fs" -version = "0.12.18" -dependencies = [ - "anyhow", - "core-foundation", - "libc", - "log", - "same-file", - "tempfile", - "windows-sys 0.59.0", -] - [[package]] name = "tinymist-project" version = "0.12.18" @@ -4111,7 +4097,6 @@ dependencies = [ "serde", "serde_json", "tinymist-derive", - "tinymist-fs", "tinymist-std", "tinymist-world", "tokio", @@ -4190,27 +4175,34 @@ dependencies = [ name = "tinymist-std" version = "0.12.18" dependencies = [ + "anyhow", "base64", "bitvec", "comemo", + "core-foundation", "dashmap", "ecow", "fxhash", "hex", "js-sys", + "libc", + "log", "parking_lot", "path-clean", "rkyv", "rustc-hash 2.1.0", + "same-file", "serde", "serde_json", "serde_repr", "serde_with", "siphasher 1.0.1", + "tempfile", "typst", "typst-shim", "wasm-bindgen", "web-time", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 519f6a01..c09c2b47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,6 +85,7 @@ sha2 = "0.10.6" nohash-hasher = "0.2.0" # Data Structures +bitvec = "1" comemo = "0.4" # We need to freeze the version of the crate, as the raw-api feature is considered unstable dashmap = { version = "=5.5.3", features = ["raw-api"] } @@ -109,8 +110,9 @@ rkyv = "0.7.42" semver = "1" serde = { version = "1", features = ["derive"] } serde_json = "1" -serde_yaml = "0.9" +serde_repr = "0.1" serde_with = { version = "3.6", features = ["base64"] } +serde_yaml = "0.9" serde-wasm-bindgen = "^0.6" toml = { version = "0.8", default-features = false, features = [ "parse", @@ -184,7 +186,6 @@ tinymist-vfs = { path = "./crates/tinymist-vfs/", default-features = false } tinymist-core = { path = "./crates/tinymist-core/", default-features = false } tinymist-world = { path = "./crates/tinymist-world/", default-features = false } tinymist-project = { path = "./crates/tinymist-project/" } -tinymist-fs = { path = "./crates/tinymist-fs/" } tinymist-derive = { path = "./crates/tinymist-derive/" } tinymist-analysis = { path = "./crates/tinymist-analysis/" } tinymist-query = { path = "./crates/tinymist-query/" } diff --git a/crates/tinymist-fs/Cargo.toml b/crates/tinymist-fs/Cargo.toml deleted file mode 100644 index 23d79cec..00000000 --- a/crates/tinymist-fs/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "tinymist-fs" -description = "Filesystem support for tinymist." -categories = ["compilers", "command-line-utilities"] -keywords = ["tool"] -authors.workspace = true -version.workspace = true -license.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true -rust-version.workspace = true - -[dependencies] - -anyhow.workspace = true -log.workspace = true -tempfile.workspace = true -same-file.workspace = true - -[target.'cfg(target_os = "macos")'.dependencies] -core-foundation.workspace = true - -[target.'cfg(unix)'.dependencies] -libc.workspace = true - -[target.'cfg(windows)'.dependencies] -windows-sys = { workspace = true, features = [ - "Win32_Foundation", - "Win32_Security", - "Win32_Storage_FileSystem", - "Win32_System_IO", - "Win32_System_Console", - "Win32_System_JobObjects", - "Win32_System_Threading", -] } - -[features] - -[lints] -workspace = true diff --git a/crates/tinymist-fs/src/errors.rs b/crates/tinymist-fs/src/errors.rs deleted file mode 100644 index a3bf2def..00000000 --- a/crates/tinymist-fs/src/errors.rs +++ /dev/null @@ -1,2 +0,0 @@ -/// Type alias for `anyhow::Result`. -pub type Result = anyhow::Result; diff --git a/crates/tinymist-project/Cargo.toml b/crates/tinymist-project/Cargo.toml index 9ee5a3b2..0ffeee6b 100644 --- a/crates/tinymist-project/Cargo.toml +++ b/crates/tinymist-project/Cargo.toml @@ -28,8 +28,7 @@ semver.workspace = true serde.workspace = true serde_json.workspace = true tinymist-world = { workspace = true, features = ["system"] } -tinymist-fs.workspace = true -tinymist-std.workspace = true +tinymist-std = { workspace = true, features = ["system"] } tinymist-derive.workspace = true toml.workspace = true typst.workspace = true diff --git a/crates/tinymist-project/src/args.rs b/crates/tinymist-project/src/args.rs index 78935b8a..95da7424 100644 --- a/crates/tinymist-project/src/args.rs +++ b/crates/tinymist-project/src/args.rs @@ -2,11 +2,10 @@ use crate::DocIdArgs; use core::fmt; use serde::{Deserialize, Serialize}; use std::{num::NonZeroUsize, ops::RangeInclusive, path::Path, str::FromStr, sync::OnceLock}; -use tinymist_std::{bail, error::prelude::ZResult}; +use tinymist_std::{bail, error::prelude::Result}; pub use tinymist_world::args::{CompileFontArgs, CompilePackageArgs}; pub use typst_preview::{PreviewArgs, PreviewMode}; -use anyhow::Result; use clap::{ValueEnum, ValueHint}; use crate::model::*; @@ -271,11 +270,11 @@ pub struct TaskCompileArgs { pub ppi: f32, #[clap(skip)] - pub output_format: OnceLock>, + pub output_format: OnceLock>, } impl TaskCompileArgs { - pub fn to_task(self, doc_id: Id) -> ZResult { + pub fn to_task(self, doc_id: Id) -> Result { let new_task_id = self.task_name.map(Id::new); let task_id = new_task_id.unwrap_or(doc_id.clone()); diff --git a/crates/tinymist-project/src/compiler.rs b/crates/tinymist-project/src/compiler.rs index 2e067805..2b3d0226 100644 --- a/crates/tinymist-project/src/compiler.rs +++ b/crates/tinymist-project/src/compiler.rs @@ -18,7 +18,7 @@ use reflexo_typst::{ features::{CompileFeature, FeatureSet, WITH_COMPILING_STATUS_FEATURE}, CompileEnv, CompileReport, Compiler, TypstDocument, }; -use tinymist_std::error::prelude::ZResult; +use tinymist_std::error::prelude::Result; use tokio::sync::mpsc; use typst::diag::{SourceDiagnostic, SourceResult}; @@ -606,7 +606,7 @@ impl ProjectCom } } - pub fn restart_dedicate(&mut self, group: &str, entry: EntryState) -> ZResult { + pub fn restart_dedicate(&mut self, group: &str, entry: EntryState) -> Result { let id = ProjectInsId(group.into()); let verse = CompilerUniverse::::new_raw( diff --git a/crates/tinymist-project/src/lock.rs b/crates/tinymist-project/src/lock.rs index c09132dc..935b06d5 100644 --- a/crates/tinymist-project/src/lock.rs +++ b/crates/tinymist-project/src/lock.rs @@ -121,7 +121,7 @@ impl ProjectLockUpdater { let data = serde_json::to_string(&mat).unwrap(); let path = cache_dir.join("path-material.json"); - let result = tinymist_fs::paths::write_atomic(path, data); + let result = tinymist_std::fs::paths::write_atomic(path, data); if let Err(err) = result { log::error!("ProjectCompiler: write material error: {err}"); } diff --git a/crates/tinymist-project/src/model.rs b/crates/tinymist-project/src/model.rs index 301b7ecb..dc8868c4 100644 --- a/crates/tinymist-project/src/model.rs +++ b/crates/tinymist-project/src/model.rs @@ -4,18 +4,16 @@ use std::io::{Read, Seek, SeekFrom, Write}; use std::path::PathBuf; use std::{cmp::Ordering, path::Path, str::FromStr}; -use anyhow::{bail, Context}; use clap::ValueHint; use ecow::{eco_vec, EcoVec}; use serde::{Deserialize, Serialize}; +use tinymist_std::error::prelude::*; use tinymist_std::path::unix_slash; -use tinymist_std::ImmutPath; +use tinymist_std::{bail, ImmutPath}; use tinymist_world::EntryReader; use typst::diag::EcoString; use typst::syntax::FileId; -pub use anyhow::Result; - pub mod task; pub use task::*; @@ -39,17 +37,17 @@ pub enum LockFileCompat { } impl LockFileCompat { - pub fn version(&self) -> anyhow::Result<&str> { + pub fn version(&self) -> Result<&str> { match self { LockFileCompat::Version010Beta0(..) => Ok(LOCK_VERSION), LockFileCompat::Other(v) => v .get("version") .and_then(|v| v.as_str()) - .ok_or_else(|| anyhow::anyhow!("missing version field")), + .context("missing version field"), } } - pub fn migrate(self) -> anyhow::Result { + pub fn migrate(self) -> Result { match self { LockFileCompat::Version010Beta0(v) => Ok(v), this @ LockFileCompat::Other(..) => { @@ -189,12 +187,14 @@ impl LockFile { } pub fn update(cwd: &Path, f: impl FnOnce(&mut Self) -> Result<()>) -> Result<()> { - let fs = tinymist_fs::flock::Filesystem::new(cwd.to_owned()); + let fs = tinymist_std::fs::flock::Filesystem::new(cwd.to_owned()); - let mut lock_file = fs.open_rw_exclusive_create(LOCK_FILENAME, "project commands")?; + let mut lock_file = fs + .open_rw_exclusive_create(LOCK_FILENAME, "project commands") + .context("tinymist.lock")?; let mut data = vec![]; - lock_file.read_to_end(&mut data)?; + lock_file.read_to_end(&mut data).context("read lock")?; let old_data = std::str::from_utf8(&data).context("tinymist.lock file is not valid utf-8")?; @@ -207,7 +207,7 @@ impl LockFile { } } else { let old_state = toml::from_str::(old_data) - .context("tinymist.lock file is not a valid TOML file")?; + .context_ut("tinymist.lock file is not a valid TOML file")?; let version = old_state.version()?; match Version(version).partial_cmp(&Version(LOCK_VERSION)) { @@ -244,25 +244,29 @@ impl LockFile { // while writing the lock file. This is sensible because `Cargo.lock` is // only a "resolved result" of the `Cargo.toml`. Thus, we should inform // users that don't only persist configuration in the lock file. - lock_file.file().set_len(0)?; - lock_file.seek(SeekFrom::Start(0))?; - lock_file.write_all(new_data.as_bytes())?; + lock_file.file().set_len(0).context(LOCK_FILENAME)?; + lock_file.seek(SeekFrom::Start(0)).context(LOCK_FILENAME)?; + lock_file + .write_all(new_data.as_bytes()) + .context(LOCK_FILENAME)?; Ok(()) } pub fn read(dir: &Path) -> Result { - let fs = tinymist_fs::flock::Filesystem::new(dir.to_owned()); + let fs = tinymist_std::fs::flock::Filesystem::new(dir.to_owned()); - let mut lock_file = fs.open_ro_shared(LOCK_FILENAME, "project commands")?; + let mut lock_file = fs + .open_ro_shared(LOCK_FILENAME, "project commands") + .context(LOCK_FILENAME)?; let mut data = vec![]; - lock_file.read_to_end(&mut data)?; + lock_file.read_to_end(&mut data).context(LOCK_FILENAME)?; let data = std::str::from_utf8(&data).context("tinymist.lock file is not valid utf-8")?; let state = toml::from_str::(data) - .context("tinymist.lock file is not a valid TOML file")?; + .context_ut("tinymist.lock file is not a valid TOML file")?; state.migrate() } diff --git a/crates/tinymist-project/src/model/task.rs b/crates/tinymist-project/src/model/task.rs index af9a01f0..8c0fb32a 100644 --- a/crates/tinymist-project/src/model/task.rs +++ b/crates/tinymist-project/src/model/task.rs @@ -1,6 +1,5 @@ use std::hash::Hash; -pub use anyhow::Result; use serde::{Deserialize, Serialize}; use tinymist_derive::toml_model; diff --git a/crates/tinymist-project/src/world.rs b/crates/tinymist-project/src/world.rs index 445529b4..c6999687 100644 --- a/crates/tinymist-project/src/world.rs +++ b/crates/tinymist-project/src/world.rs @@ -15,7 +15,6 @@ use std::path::Path; use std::{borrow::Cow, sync::Arc}; use ::typst::utils::LazyHash; -use anyhow::Context; use tinymist_std::error::prelude::*; use tinymist_std::ImmutPath; use tinymist_world::font::system::SystemFontSearcher; @@ -47,13 +46,13 @@ pub type TypstSystemWorldExtend = CompilerWorld; pub trait WorldProvider { /// Get the entry options from the arguments. - fn entry(&self) -> anyhow::Result; + fn entry(&self) -> Result; /// Get a universe instance from the given arguments. - fn resolve(&self) -> anyhow::Result; + fn resolve(&self) -> Result; } impl WorldProvider for CompileOnceArgs { - fn resolve(&self) -> anyhow::Result { + fn resolve(&self) -> Result { let entry = self.entry()?.try_into()?; let inputs = self .inputs @@ -75,7 +74,7 @@ impl WorldProvider for CompileOnceArgs { .context("failed to create universe") } - fn entry(&self) -> anyhow::Result { + fn entry(&self) -> Result { let input = self.input.as_ref().context("entry file must be provided")?; let input = Path::new(&input); let entry = if input.is_absolute() { @@ -134,7 +133,7 @@ impl LspUniverseBuilder { inputs: ImmutDict, font_resolver: Arc, package_registry: HttpRegistry, - ) -> ZResult { + ) -> Result { let registry = Arc::new(package_registry); let resolver = Arc::new(RegistryPathMapper::new(registry.clone())); @@ -148,7 +147,7 @@ impl LspUniverseBuilder { } /// Resolve fonts from given options. - pub fn only_embedded_fonts() -> ZResult { + pub fn only_embedded_fonts() -> Result { let mut searcher = SystemFontSearcher::new(); searcher.resolve_opts(CompileFontOpts { font_profile_cache_path: Default::default(), @@ -160,7 +159,7 @@ impl LspUniverseBuilder { } /// Resolve fonts from given options. - pub fn resolve_fonts(args: CompileFontArgs) -> ZResult { + pub fn resolve_fonts(args: CompileFontArgs) -> Result { let mut searcher = SystemFontSearcher::new(); searcher.resolve_opts(CompileFontOpts { font_profile_cache_path: Default::default(), diff --git a/crates/tinymist-std/Cargo.toml b/crates/tinymist-std/Cargo.toml index 01ec5d4f..fa42dda3 100644 --- a/crates/tinymist-std/Cargo.toml +++ b/crates/tinymist-std/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tinymist-std" -description = "Additional functions wrapping Rust's std library." +description = "Additional functions wrapping Rust's standard library." authors.workspace = true version.workspace = true license.workspace = true @@ -10,32 +10,54 @@ repository.workspace = true [dependencies] -comemo.workspace = true -ecow.workspace = true -parking_lot.workspace = true -web-time.workspace = true -wasm-bindgen = { workspace = true, optional = true } -js-sys = { workspace = true, optional = true } - -bitvec = { version = "1" } -dashmap = { version = "5" } -# tiny-skia-path.workspace = true - -path-clean.workspace = true +anyhow.workspace = true base64.workspace = true +bitvec.workspace = true +comemo.workspace = true +dashmap.workspace = true +ecow.workspace = true fxhash.workspace = true +log.workspace = true +path-clean.workspace = true +parking_lot.workspace = true rustc-hash.workspace = true -siphasher.workspace = true - serde = { workspace = true, features = ["derive"] } -serde_repr = "0.1" +serde_repr.workspace = true serde_json.workspace = true serde_with.workspace = true +siphasher.workspace = true +web-time.workspace = true +tempfile = { workspace = true, optional = true } +same-file = { workspace = true, optional = true } + +# feature = "web" +js-sys = { workspace = true, optional = true } +wasm-bindgen = { workspace = true, optional = true } + +# feature = "rkyv" rkyv = { workspace = true, optional = true } +# feature = "typst" typst = { workspace = true, optional = true } typst-shim = { workspace = true, optional = true } +[target.'cfg(target_os = "macos")'.dependencies] +core-foundation.workspace = true + +[target.'cfg(unix)'.dependencies] +libc.workspace = true + +[target.'cfg(windows)'.dependencies] +windows-sys = { workspace = true, features = [ + "Win32_Foundation", + "Win32_Security", + "Win32_Storage_FileSystem", + "Win32_System_IO", + "Win32_System_Console", + "Win32_System_JobObjects", + "Win32_System_Threading", +] } + [dev-dependencies] hex.workspace = true @@ -48,13 +70,11 @@ typst = ["dep:typst", "dep:typst-shim"] rkyv = ["dep:rkyv", "rkyv/alloc", "rkyv/archive_le"] rkyv-validation = ["dep:rkyv", "rkyv/validation"] -# flat-vector = ["rkyv", "rkyv-validation"] __web = ["dep:wasm-bindgen", "dep:js-sys"] web = ["__web"] -system = [] +system = ["dep:tempfile", "dep:same-file"] bi-hash = [] -item-dashmap = [] [lints] workspace = true diff --git a/crates/tinymist-std/src/adt/fmap.rs b/crates/tinymist-std/src/adt/fmap.rs index 3da19fb7..896c4460 100644 --- a/crates/tinymist-std/src/adt/fmap.rs +++ b/crates/tinymist-std/src/adt/fmap.rs @@ -1,3 +1,5 @@ +//! A map that shards items by their fingerprint. + use std::{collections::HashMap, num::NonZeroU32}; use crate::hash::Fingerprint; @@ -31,12 +33,13 @@ fn default_shard_size() -> NonZeroU32 { type FMapBase = parking_lot::RwLock>; -/// A map that shards items by their fingerprint. +/// A map that shards items by their fingerprint. This is faster +/// than the dashmap in some cases. /// /// It is fast since a fingerprint could split items into different shards /// efficiently. /// -/// Note: If a fingerprint is calculated from a hash function, it is not +/// Note: If a fingerprint is not calculated from a hash function, it is not /// guaranteed that the fingerprint is evenly distributed. Thus, in that case, /// the performance of this map is not guaranteed. pub struct FingerprintMap { @@ -76,6 +79,7 @@ impl FingerprintMap { .flat_map(|shard| shard.into_inner().into_iter()) } + /// Get the shard pub fn shard(&self, fg: Fingerprint) -> &FMapBase { let shards = &self.shards; let route_idx = (fg.lower32() & self.mask) as usize; @@ -92,6 +96,7 @@ impl FingerprintMap { &mut self.shards } + /// Checks if the map is empty. pub fn contains_key(&self, fg: &Fingerprint) -> bool { self.shard(*fg).read().contains_key(fg) } diff --git a/crates/tinymist-std/src/adt/mod.rs b/crates/tinymist-std/src/adt/mod.rs index e6363513..c851c27b 100644 --- a/crates/tinymist-std/src/adt/mod.rs +++ b/crates/tinymist-std/src/adt/mod.rs @@ -1,3 +1,5 @@ +//! This module contains the implementation of the abstract data types. + pub mod fmap; pub use fmap::FingerprintMap; diff --git a/crates/tinymist-std/src/concepts/cow_mut.rs b/crates/tinymist-std/src/concepts/cow_mut.rs index 41afd78c..08f110a4 100644 --- a/crates/tinymist-std/src/concepts/cow_mut.rs +++ b/crates/tinymist-std/src/concepts/cow_mut.rs @@ -1,6 +1,16 @@ +//! +//! +//! This module provides a `CowMut` type, which is a mutable version of `Cow`. +//! Although it is strange that we can have a `CowMut`, because it should "copy +//! on write", we also don't love the `Cow` API and use `Cow` without even +//! touching its `DerefMut` feature. + +/// A mutable version of [Cow][`std::borrow::Cow`]. #[derive(Debug)] pub enum CowMut<'a, T> { + /// An owned data. Owned(T), + /// A borrowed mut data. Borrowed(&'a mut T), } diff --git a/crates/tinymist-std/src/concepts/hash.rs b/crates/tinymist-std/src/concepts/hash.rs deleted file mode 100644 index cab55ba3..00000000 --- a/crates/tinymist-std/src/concepts/hash.rs +++ /dev/null @@ -1,61 +0,0 @@ -//!todo: move to core/src/hash.rs - -use std::{ - hash::{Hash, Hasher}, - ops::Deref, -}; - -use crate::hash::item_hash128; - -pub trait StaticHash128 { - fn get_hash(&self) -> u128; -} - -impl Hash for dyn StaticHash128 { - #[inline] - fn hash(&self, state: &mut H) { - state.write_u128(self.get_hash()); - } -} - -pub struct HashedTrait { - hash: u128, - t: Box, -} - -impl HashedTrait { - pub fn new(hash: u128, t: Box) -> Self { - Self { hash, t } - } -} - -impl Deref for HashedTrait { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.t - } -} - -impl Hash for HashedTrait { - #[inline] - fn hash(&self, state: &mut H) { - state.write_u128(self.hash); - } -} - -impl Default for HashedTrait { - fn default() -> Self { - let t = T::default(); - Self { - hash: item_hash128(&t), - t: Box::new(t), - } - } -} - -impl StaticHash128 for HashedTrait { - fn get_hash(&self) -> u128 { - self.hash - } -} diff --git a/crates/tinymist-std/src/concepts/marker.rs b/crates/tinymist-std/src/concepts/marker.rs index 7a4ed51e..d7c2b56a 100644 --- a/crates/tinymist-std/src/concepts/marker.rs +++ b/crates/tinymist-std/src/concepts/marker.rs @@ -7,6 +7,7 @@ use serde_with::{ }; use serde_with::{DeserializeAs, SerializeAs}; +/// A marker type for serializing and deserializing `Cow<[u8]>` as base64. pub struct AsCowBytes; type StdBase64 = Base64; diff --git a/crates/tinymist-std/src/concepts/mod.rs b/crates/tinymist-std/src/concepts/mod.rs index f2717656..f400fbef 100644 --- a/crates/tinymist-std/src/concepts/mod.rs +++ b/crates/tinymist-std/src/concepts/mod.rs @@ -3,9 +3,6 @@ use std::{path::Path, sync::Arc}; pub use takable::*; -mod hash; -pub use hash::*; - pub mod cow_mut; mod query; @@ -17,6 +14,9 @@ pub use read::*; mod marker; pub use marker::*; +/// An immutable string. pub type ImmutStr = Arc; +/// An immutable byte slice. pub type ImmutBytes = Arc<[u8]>; +/// An immutable path. pub type ImmutPath = Arc; diff --git a/crates/tinymist-std/src/concepts/query.rs b/crates/tinymist-std/src/concepts/query.rs index 02166248..3dd6979f 100644 --- a/crates/tinymist-std/src/concepts/query.rs +++ b/crates/tinymist-std/src/concepts/query.rs @@ -15,6 +15,7 @@ pub struct QueryRef { } impl QueryRef { + /// Create a new query reference with the given value. pub fn with_value(value: T) -> Self { let cell = OnceLock::new(); cell.get_or_init(|| Ok(value)); @@ -24,6 +25,8 @@ impl QueryRef { } } + /// Create a new query reference with the given context to execute the + /// query. pub fn with_context(ctx: QC) -> Self { Self { ctx: Mutex::new(Some(ctx)), diff --git a/crates/tinymist-std/src/concepts/read.rs b/crates/tinymist-std/src/concepts/read.rs index e5c67a5e..0841a7f8 100644 --- a/crates/tinymist-std/src/concepts/read.rs +++ b/crates/tinymist-std/src/concepts/read.rs @@ -1,3 +1,5 @@ +/// A trait for reading all data from a source into a buffer. pub trait ReadAllOnce { + /// Reads all data from the source into the buffer. fn read_all(self, buf: &mut Vec) -> std::io::Result; } diff --git a/crates/tinymist-std/src/debug_loc.rs b/crates/tinymist-std/src/debug_loc.rs index d2f1ba22..2be65ebe 100644 --- a/crates/tinymist-std/src/debug_loc.rs +++ b/crates/tinymist-std/src/debug_loc.rs @@ -1,3 +1,6 @@ +//! The debug location that can be used to locate a position in a document or a +//! file. + use core::fmt; use serde::{Deserialize, Serialize}; @@ -38,6 +41,7 @@ pub type RawSourceSpan = u64; /// See [`CharPosition`] for the definition of the position inside a file. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FileLocation { + /// The file path. pub filepath: String, } @@ -73,11 +77,14 @@ impl From> for CharPosition { /// See [`CharPosition`] for the definition of the position inside a file. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SourceLocation { + /// The file path. pub filepath: String, + /// The position in the file. pub pos: CharPosition, } impl SourceLocation { + /// Create a new source location. pub fn from_flat( flat: FlatSourceLocation, i: &impl std::ops::Index, @@ -94,16 +101,20 @@ impl SourceLocation { /// See [`CharPosition`] for the definition of the position inside a file. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FlatSourceLocation { + /// The file path. pub filepath: u32, + /// The position in the file. pub pos: CharPosition, } -// /// A resolved file range. -// /// -// /// See [`CharPosition`] for the definition of the position inside a file. +/// A resolved file range. +/// +/// See [`CharPosition`] for the definition of the position inside a file. #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct CharRange { + /// The start position. pub start: CharPosition, + /// The end position. pub end: CharPosition, } @@ -117,12 +128,14 @@ impl fmt::Display for CharRange { } } -// /// A resolved source (text) range. -// /// -// /// See [`CharPosition`] for the definition of the position inside a file. +/// A resolved source (text) range. +/// +/// See [`CharPosition`] for the definition of the position inside a file. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SourceRange { + /// The file path. pub path: String, + /// The range in the file. pub range: CharRange, } @@ -144,7 +157,10 @@ mod typst_ext { /// text or string content. #[derive(Debug, Clone, Copy)] pub struct SourceSpanOffset { + /// The source span. pub span: SourceSpan, + /// The offset relative to the start of the span. This is usually useful + /// if the location is not a span created by the parser. pub offset: usize, } diff --git a/crates/tinymist-std/src/error.rs b/crates/tinymist-std/src/error.rs index e66e75c3..7ed3085b 100644 --- a/crates/tinymist-std/src/error.rs +++ b/crates/tinymist-std/src/error.rs @@ -1,3 +1,5 @@ +//! Error handling utilities for the `tinymist` crate. + use core::fmt; use ecow::EcoString; @@ -5,12 +7,17 @@ use serde::{Deserialize, Serialize}; use crate::debug_loc::CharRange; +/// The severity of a diagnostic message, following the LSP specification. #[derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr, Debug, Clone)] #[repr(u8)] pub enum DiagSeverity { + /// An error message. Error = 1, + /// A warning message. Warning = 2, + /// An information message. Information = 3, + /// A hint message. Hint = 4, } @@ -26,30 +33,41 @@ impl fmt::Display for DiagSeverity { } /// +/// The `owner` and `source` fields are not included in the struct, but they +/// could be added to `ErrorImpl::arguments`. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DiagMessage { + /// The typst package specifier. pub package: String, + /// The file path relative to the root of the workspace or the package. pub path: String, + /// The diagnostic message. pub message: String, + /// The severity of the diagnostic message. pub severity: DiagSeverity, + /// The char range in the file. The position encoding must be negotiated. pub range: Option, - // These field could be added to ErrorImpl::arguments - // owner: Option, - // source: ImmutStr, } impl DiagMessage {} +/// ALl kind of errors that can occur in the `tinymist` crate. #[derive(Debug, Clone)] #[non_exhaustive] pub enum ErrKind { + /// No message. None, + /// A string message. Msg(EcoString), + /// A source diagnostic message. Diag(Box), + /// An inner error. Inner(Error), } +/// A trait to convert an error kind into an error kind. pub trait ErrKindExt { + /// Convert the error kind into an error kind. fn to_error_kind(self) -> ErrKind; } @@ -65,6 +83,12 @@ impl ErrKindExt for std::io::Error { } } +impl ErrKindExt for std::str::Utf8Error { + fn to_error_kind(self) -> ErrKind { + ErrKind::Msg(self.to_string().into()) + } +} + impl ErrKindExt for String { fn to_error_kind(self) -> ErrKind { ErrKind::Msg(self.into()) @@ -101,11 +125,27 @@ impl ErrKindExt for serde_json::Error { } } +impl ErrKindExt for anyhow::Error { + fn to_error_kind(self) -> ErrKind { + ErrKind::Msg(self.to_string().into()) + } +} + +impl ErrKindExt for Error { + fn to_error_kind(self) -> ErrKind { + ErrKind::Msg(self.to_string().into()) + } +} + +/// The internal error implementation. #[derive(Debug, Clone)] pub struct ErrorImpl { + /// A static error identifier. loc: &'static str, + /// The kind of error. kind: ErrKind, - arguments: Box<[(&'static str, String)]>, + /// Additional extractable arguments for the error. + args: Option>, } /// This type represents all possible errors that can occur in typst.ts @@ -118,43 +158,65 @@ pub struct Error { } impl Error { - pub fn new(loc: &'static str, kind: ErrKind, arguments: Box<[(&'static str, String)]>) -> Self { + /// Creates a new error. + pub fn new( + loc: &'static str, + kind: ErrKind, + args: Option>, + ) -> Self { Self { - err: Box::new(ErrorImpl { - loc, - kind, - arguments, - }), + err: Box::new(ErrorImpl { loc, kind, args }), } } + /// Returns the location of the error. pub fn loc(&self) -> &'static str { self.err.loc } + /// Returns the kind of the error. pub fn kind(&self) -> &ErrKind { &self.err.kind } + /// Returns the arguments of the error. pub fn arguments(&self) -> &[(&'static str, String)] { - &self.err.arguments + self.err.args.as_deref().unwrap_or_default() } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let err = &self.err; - match &err.kind { - ErrKind::Msg(msg) => write!(f, "{}: {} with {:?}", err.loc, msg, err.arguments), - ErrKind::Diag(diag) => { - write!(f, "{}: {} with {:?}", err.loc, diag.message, err.arguments) + + if err.loc.is_empty() { + match &err.kind { + ErrKind::Msg(msg) => write!(f, "{msg} with {:?}", err.args), + ErrKind::Diag(diag) => { + write!(f, "{} with {:?}", diag.message, err.args) + } + ErrKind::Inner(e) => write!(f, "{e} with {:?}", err.args), + ErrKind::None => write!(f, "error with {:?}", err.args), + } + } else { + match &err.kind { + ErrKind::Msg(msg) => write!(f, "{}: {} with {:?}", err.loc, msg, err.args), + ErrKind::Diag(diag) => { + write!(f, "{}: {} with {:?}", err.loc, diag.message, err.args) + } + ErrKind::Inner(e) => write!(f, "{}: {} with {:?}", err.loc, e, err.args), + ErrKind::None => write!(f, "{}: with {:?}", err.loc, err.args), } - ErrKind::Inner(e) => write!(f, "{}: {} with {:?}", err.loc, e, err.arguments), - ErrKind::None => write!(f, "{}: with {:?}", err.loc, err.arguments), } } } +impl From for Error { + fn from(e: anyhow::Error) -> Self { + Error::new("", e.to_string().to_error_kind(), None) + } +} + impl std::error::Error for Error {} #[cfg(feature = "web")] @@ -178,47 +240,94 @@ impl From<&Error> for wasm_bindgen::JsValue { } } +/// The result type used in the `tinymist` crate. +pub type Result = std::result::Result; + +/// A trait to add context to a result. +pub trait WithContext: Sized { + /// Add a context to the result. + fn context(self, loc: &'static str) -> Result; + + /// Add a context to the result with additional arguments. + fn with_context(self, loc: &'static str, f: F) -> Result + where + F: FnOnce() -> Option>; +} + +impl WithContext for Result { + fn context(self, loc: &'static str) -> Result { + self.map_err(|e| Error::new(loc, e.to_error_kind(), None)) + } + + fn with_context(self, loc: &'static str, f: F) -> Result + where + F: FnOnce() -> Option>, + { + self.map_err(|e| Error::new(loc, e.to_error_kind(), f())) + } +} + +impl WithContext for Option { + fn context(self, loc: &'static str) -> Result { + self.ok_or_else(|| Error::new(loc, ErrKind::None, None)) + } + + fn with_context(self, loc: &'static str, f: F) -> Result + where + F: FnOnce() -> Option>, + { + self.ok_or_else(|| Error::new(loc, ErrKind::None, f())) + } +} + +/// A trait to add context to a result without a specific error type. +pub trait WithContextUntyped: Sized { + /// Add a context to the result. + fn context_ut(self, loc: &'static str) -> Result; + + /// Add a context to the result with additional arguments. + fn with_context_ut(self, loc: &'static str, f: F) -> Result + where + F: FnOnce() -> Option>; +} + +impl WithContextUntyped for Result { + fn context_ut(self, loc: &'static str) -> Result { + self.map_err(|e| Error::new(loc, ErrKind::Msg(ecow::eco_format!("{e}")), None)) + } + + fn with_context_ut(self, loc: &'static str, f: F) -> Result + where + F: FnOnce() -> Option>, + { + self.map_err(|e| Error::new(loc, ErrKind::Msg(ecow::eco_format!("{e}")), f())) + } +} + +/// The error prelude. pub mod prelude { + #![allow(missing_docs)] + use super::ErrKindExt; use crate::Error; - pub type ZResult = Result; - - pub trait WithContext: Sized { - fn context(self, loc: &'static str) -> ZResult; - - fn with_context(self, loc: &'static str, f: F) -> ZResult - where - F: FnOnce() -> Box<[(&'static str, String)]>; - } - - impl WithContext for Result { - fn context(self, loc: &'static str) -> ZResult { - self.map_err(|e| Error::new(loc, e.to_error_kind(), Box::new([]))) - } - - fn with_context(self, loc: &'static str, f: F) -> ZResult - where - F: FnOnce() -> Box<[(&'static str, String)]>, - { - self.map_err(|e| Error::new(loc, e.to_error_kind(), f())) - } - } + pub use super::{WithContext, WithContextUntyped}; + pub use crate::Result; pub fn map_string_err(loc: &'static str) -> impl Fn(T) -> Error { - move |e| Error::new(loc, e.to_string().to_error_kind(), Box::new([])) + move |e| Error::new(loc, e.to_string().to_error_kind(), None) } pub fn map_into_err>(loc: &'static str) -> impl Fn(T) -> Error { - move |e| Error::new(loc, e.into().to_error_kind(), Box::new([])) + move |e| Error::new(loc, e.into().to_error_kind(), None) } pub fn map_err(loc: &'static str) -> impl Fn(T) -> Error { - move |e| Error::new(loc, e.to_error_kind(), Box::new([])) + move |e| Error::new(loc, e.to_error_kind(), None) } pub fn wrap_err(loc: &'static str) -> impl Fn(Error) -> Error { - move |e| Error::new(loc, crate::ErrKind::Inner(e), Box::new([])) + move |e| Error::new(loc, crate::ErrKind::Inner(e), None) } pub fn map_string_err_with_args< @@ -226,13 +335,13 @@ pub mod prelude { Args: IntoIterator, >( loc: &'static str, - arguments: Args, + args: Args, ) -> impl FnOnce(T) -> Error { move |e| { Error::new( loc, e.to_string().to_error_kind(), - arguments.into_iter().collect::>().into_boxed_slice(), + Some(args.into_iter().collect::>().into_boxed_slice()), ) } } @@ -243,49 +352,49 @@ pub mod prelude { Args: IntoIterator, >( loc: &'static str, - arguments: Args, + args: Args, ) -> impl FnOnce(T) -> Error { move |e| { Error::new( loc, e.into().to_error_kind(), - arguments.into_iter().collect::>().into_boxed_slice(), + Some(args.into_iter().collect::>().into_boxed_slice()), ) } } pub fn map_err_with_args>( loc: &'static str, - arguments: Args, + args: Args, ) -> impl FnOnce(T) -> Error { move |e| { Error::new( loc, e.to_error_kind(), - arguments.into_iter().collect::>().into_boxed_slice(), + Some(args.into_iter().collect::>().into_boxed_slice()), ) } } pub fn wrap_err_with_args>( loc: &'static str, - arguments: Args, + args: Args, ) -> impl FnOnce(Error) -> Error { move |e| { Error::new( loc, crate::ErrKind::Inner(e), - arguments.into_iter().collect::>().into_boxed_slice(), + Some(args.into_iter().collect::>().into_boxed_slice()), ) } } pub fn _error_once(loc: &'static str, args: Box<[(&'static str, String)]>) -> Error { - Error::new(loc, crate::ErrKind::None, args) + Error::new(loc, crate::ErrKind::None, Some(args)) } pub fn _msg(loc: &'static str, msg: EcoString) -> Error { - Error::new(loc, crate::ErrKind::Msg(msg), Box::new([])) + Error::new(loc, crate::ErrKind::Msg(msg), None) } pub use ecow::eco_format as _eco_format; diff --git a/crates/tinymist-fs/src/lib.rs b/crates/tinymist-std/src/fs.rs similarity index 55% rename from crates/tinymist-fs/src/lib.rs rename to crates/tinymist-std/src/fs.rs index 70260ac4..27355ec9 100644 --- a/crates/tinymist-fs/src/lib.rs +++ b/crates/tinymist-std/src/fs.rs @@ -1,7 +1,6 @@ //! Filesystem support for tinymist. +#[cfg(feature = "system")] pub mod flock; +#[cfg(feature = "system")] pub mod paths; - -mod errors; -pub use errors::*; diff --git a/crates/tinymist-fs/src/flock.rs b/crates/tinymist-std/src/fs/flock.rs similarity index 99% rename from crates/tinymist-fs/src/flock.rs rename to crates/tinymist-std/src/fs/flock.rs index 8c087144..3d296244 100644 --- a/crates/tinymist-fs/src/flock.rs +++ b/crates/tinymist-std/src/fs/flock.rs @@ -13,10 +13,10 @@ use std::io; use std::io::{Read, Seek, SeekFrom, Write}; use std::path::{Display, Path, PathBuf}; -use crate::errors::Result; -use crate::paths; +use self::sys::*; +use super::paths; use anyhow::Context as _; -use sys::*; +use anyhow::Result; /// A locked file. /// diff --git a/crates/tinymist-fs/src/paths.rs b/crates/tinymist-std/src/fs/paths.rs similarity index 99% rename from crates/tinymist-fs/src/paths.rs rename to crates/tinymist-std/src/fs/paths.rs index f461a94d..91b87031 100644 --- a/crates/tinymist-fs/src/paths.rs +++ b/crates/tinymist-std/src/fs/paths.rs @@ -1,7 +1,6 @@ //! Upstream: //! Various utilities for working with files and paths. -use anyhow::{Context, Result}; use std::env; use std::ffi::{OsStr, OsString}; use std::fs::{self, File, Metadata, OpenOptions}; @@ -9,6 +8,8 @@ use std::io; use std::io::prelude::*; use std::iter; use std::path::{Component, Path, PathBuf}; + +use anyhow::{Context, Result}; use tempfile::Builder as TempFileBuilder; /// Joins paths into a string suitable for the `PATH` environment variable. diff --git a/crates/tinymist-std/src/hash.rs b/crates/tinymist-std/src/hash.rs index e0c624b5..f356f9fc 100644 --- a/crates/tinymist-std/src/hash.rs +++ b/crates/tinymist-std/src/hash.rs @@ -1,8 +1,10 @@ +//! The hash extension module. It provides extra concepts like `Fingerprint` and +//! `HashedTrait`. + use core::fmt; -use std::{ - any::Any, - hash::{Hash, Hasher}, -}; +use std::any::Any; +use std::hash::{Hash, Hasher}; +use std::ops::Deref; use base64::Engine; use fxhash::FxHasher32; @@ -11,12 +13,13 @@ use siphasher::sip128::{Hasher128, SipHasher13}; #[cfg(feature = "rkyv")] use rkyv::{Archive, Deserialize as rDeser, Serialize as rSer}; -use crate::error::prelude::ZResult; +use crate::error::prelude::Result; pub(crate) type FxBuildHasher = std::hash::BuildHasherDefault; pub use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; // pub type FxIndexSet = indexmap::IndexSet; // pub type FxIndexMap = indexmap::IndexMap; +/// A dashmap that uses the FxHasher as the underlying hasher. pub type FxDashMap = dashmap::DashMap; /// See @@ -85,7 +88,7 @@ impl Fingerprint { } /// Creates a new `Fingerprint` from a svg id that **doesn't have prefix**. - pub fn try_from_str(s: &str) -> ZResult { + pub fn try_from_str(s: &str) -> Result { let bytes = base64::engine::general_purpose::STANDARD_NO_PAD .decode(&s.as_bytes()[..11]) .expect("invalid base64 string"); @@ -135,9 +138,11 @@ pub struct FingerprintSipHasher { data: Vec, } +/// The base hasher for the [`FingerprintSipHasher`]. pub type FingerprintSipHasherBase = SipHasher13; impl FingerprintSipHasher { + /// Get the fast hash value and the underlying data. pub fn fast_hash(&self) -> (u32, &Vec) { let mut inner = FxHasher32::default(); self.data.hash(&mut inner); @@ -187,6 +192,7 @@ pub struct FingerprintBuilder { #[cfg(not(feature = "bi-hash"))] impl FingerprintBuilder { + /// Resolve the fingerprint without checking the conflict. pub fn resolve_unchecked(&self, item: &T) -> Fingerprint { let mut s = FingerprintSipHasher { data: Vec::new() }; item.hash(&mut s); @@ -194,6 +200,7 @@ impl FingerprintBuilder { fingerprint } + /// Resolve the fingerprint and check the conflict. pub fn resolve(&self, item: &T) -> Fingerprint { let mut s = FingerprintSipHasher { data: Vec::new() }; item.type_id().hash(&mut s); @@ -216,6 +223,7 @@ impl FingerprintBuilder { #[cfg(feature = "bi-hash")] impl FingerprintBuilder { + /// Resolve the fingerprint without checking the conflict. pub fn resolve_unchecked(&self, item: &T) -> Fingerprint { let mut s = FingerprintSipHasher { data: Vec::new() }; item.hash(&mut s); @@ -233,6 +241,7 @@ impl FingerprintBuilder { fingerprint } + /// Resolve the fingerprint and check the conflict. pub fn resolve(&self, item: &T) -> Fingerprint { let mut s = FingerprintSipHasher { data: Vec::new() }; item.type_id().hash(&mut s); @@ -297,6 +306,66 @@ pub fn hash64(v: &T) -> u64 { // todo: rustc hash doesn't have 32-bit hash pub use fxhash::hash32; +/// A trait that provides a static prehashed 128-bit hash. +pub trait StaticHash128 { + /// Get the prehashed 128-bit hash. + fn get_hash(&self) -> u128; +} + +impl Hash for dyn StaticHash128 { + #[inline] + fn hash(&self, state: &mut H) { + state.write_u128(self.get_hash()); + } +} + +/// A trait that provides a static prehashed 64-bit hash for any internal `T`. +/// +/// Please ensure that the `T` is really mapped to the hash. Use it at your own +/// risk. +pub struct HashedTrait { + hash: u128, + t: Box, +} + +impl HashedTrait { + /// Create a new `HashedTrait` with the given hash and the trait object. + pub fn new(hash: u128, t: Box) -> Self { + Self { hash, t } + } +} + +impl Deref for HashedTrait { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.t + } +} + +impl Hash for HashedTrait { + #[inline] + fn hash(&self, state: &mut H) { + state.write_u128(self.hash); + } +} + +impl Default for HashedTrait { + fn default() -> Self { + let t = T::default(); + Self { + hash: item_hash128(&t), + t: Box::new(t), + } + } +} + +impl StaticHash128 for HashedTrait { + fn get_hash(&self) -> u128 { + self.hash + } +} + #[test] fn test_fingerprint() { let t = Fingerprint::from_pair(0, 1); diff --git a/crates/tinymist-std/src/lib.rs b/crates/tinymist-std/src/lib.rs index 037b3911..d29082ab 100644 --- a/crates/tinymist-std/src/lib.rs +++ b/crates/tinymist-std/src/lib.rs @@ -1,8 +1,9 @@ -#![allow(missing_docs)] +//! Additional functions wrapping Rust's standard library. pub mod adt; pub mod debug_loc; pub mod error; +pub mod fs; pub mod hash; pub mod path; pub mod time; @@ -11,7 +12,7 @@ pub(crate) mod concepts; pub use concepts::*; -pub use error::{ErrKind, Error}; +pub use error::{ErrKind, Error, Result}; #[cfg(feature = "typst")] pub use typst_shim; @@ -19,8 +20,8 @@ pub use typst_shim; #[cfg(feature = "rkyv")] use rkyv::{Archive, Deserialize as rDeser, Serialize as rSer}; -/// The local id of a svg item. -/// This id is only unique within the svg document. +/// The local id of an item. +/// This id is only unique within a task or process. #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] #[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))] #[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] diff --git a/crates/tinymist-std/src/path.rs b/crates/tinymist-std/src/path.rs index 9f4a0bb3..a6707e7e 100644 --- a/crates/tinymist-std/src/path.rs +++ b/crates/tinymist-std/src/path.rs @@ -1,3 +1,5 @@ +//! Path utilities. + use std::path::{Component, Path}; pub use path_clean::PathClean; diff --git a/crates/tinymist-std/src/time.rs b/crates/tinymist-std/src/time.rs index 0a439fe8..69272878 100644 --- a/crates/tinymist-std/src/time.rs +++ b/crates/tinymist-std/src/time.rs @@ -1,3 +1,5 @@ +//! Cross platform time utilities. + pub use std::time::SystemTime as Time; pub use web_time::Duration; pub use web_time::Instant; diff --git a/crates/tinymist-world/src/entry.rs b/crates/tinymist-world/src/entry.rs index b9137a0b..913f2633 100644 --- a/crates/tinymist-world/src/entry.rs +++ b/crates/tinymist-world/src/entry.rs @@ -113,7 +113,7 @@ impl EntryState { } } - pub fn try_select_path_in_workspace(&self, p: &Path) -> ZResult> { + pub fn try_select_path_in_workspace(&self, p: &Path) -> Result> { Ok(match self.workspace_root() { Some(root) => match p.strip_prefix(&root) { Ok(p) => Some(EntryState::new_rooted( diff --git a/crates/tinymist-world/src/font/system.rs b/crates/tinymist-world/src/font/system.rs index 418494a8..1d3703eb 100644 --- a/crates/tinymist-world/src/font/system.rs +++ b/crates/tinymist-world/src/font/system.rs @@ -105,7 +105,7 @@ impl SystemFontSearcher { } /// Resolve fonts from given options. - pub fn resolve_opts(&mut self, opts: CompileFontOpts) -> ZResult<()> { + pub fn resolve_opts(&mut self, opts: CompileFontOpts) -> Result<()> { if opts .font_profile_cache_path .to_str() diff --git a/crates/tinymist-world/src/font/web/mod.rs b/crates/tinymist-world/src/font/web/mod.rs index a4ff1a6b..7dadf2d4 100644 --- a/crates/tinymist-world/src/font/web/mod.rs +++ b/crates/tinymist-world/src/font/web/mod.rs @@ -21,7 +21,7 @@ pub(crate) fn convert_pair(pair: JsValue) -> (JsValue, JsValue) { } struct FontBuilder {} -fn font_family_web_to_typst(family: &str, full_name: &str) -> ZResult { +fn font_family_web_to_typst(family: &str, full_name: &str) -> Result { let mut family = family; if family.starts_with("Noto") || family.starts_with("NewCM") @@ -51,7 +51,7 @@ fn infer_info_from_web_font( postscript_name, style, }: WebFontInfo, -) -> ZResult { +) -> Result { let family = font_family_web_to_typst(&family, &full_name)?; let mut full = full_name; @@ -237,7 +237,7 @@ impl FontBuilder { // {:?}", field, val))) .unwrap()) // } - fn to_string(&self, field: &str, val: &JsValue) -> ZResult { + fn to_string(&self, field: &str, val: &JsValue) -> Result { Ok(val .as_string() .ok_or_else(|| JsValue::from_str(&format!("expected string for {field}, got {val:?}"))) @@ -247,7 +247,7 @@ impl FontBuilder { fn font_web_to_typst( &self, val: &JsValue, - ) -> ZResult<(JsValue, js_sys::Function, Vec)> { + ) -> Result<(JsValue, js_sys::Function, Vec)> { let mut postscript_name = String::new(); let mut family = String::new(); let mut full_name = String::new(); @@ -414,7 +414,7 @@ impl BrowserFontSearcher { } } - pub async fn add_web_fonts(&mut self, fonts: js_sys::Array) -> ZResult<()> { + pub async fn add_web_fonts(&mut self, fonts: js_sys::Array) -> Result<()> { let font_builder = FontBuilder {}; for v in fonts.iter() { @@ -451,7 +451,7 @@ impl BrowserFontSearcher { } } - pub async fn add_glyph_pack(&mut self) -> ZResult<()> { + pub async fn add_glyph_pack(&mut self) -> Result<()> { Err(error_once!( "BrowserFontSearcher.add_glyph_pack is not implemented" )) diff --git a/crates/tinymist-world/src/system.rs b/crates/tinymist-world/src/system.rs index d8792805..4c81d68b 100644 --- a/crates/tinymist-world/src/system.rs +++ b/crates/tinymist-world/src/system.rs @@ -32,7 +32,7 @@ impl TypstSystemUniverse { /// Create [`TypstSystemWorld`] with the given options. /// See SystemCompilerFeat for instantiation details. /// See [`CompileOpts`] for available options. - pub fn new(mut opts: CompileOpts) -> ZResult { + pub fn new(mut opts: CompileOpts) -> Result { let registry: Arc = Arc::default(); let resolver = Arc::new(RegistryPathMapper::new(registry.clone())); let inputs = std::mem::take(&mut opts.inputs); @@ -46,7 +46,7 @@ impl TypstSystemUniverse { } /// Resolve fonts from given options. - fn resolve_fonts(opts: CompileOpts) -> ZResult { + fn resolve_fonts(opts: CompileOpts) -> Result { let mut searcher = SystemFontSearcher::new(); searcher.resolve_opts(opts.into())?; Ok(searcher.into()) diff --git a/crates/tinymist-world/src/world.rs b/crates/tinymist-world/src/world.rs index 868b0a70..f43a7f06 100644 --- a/crates/tinymist-world/src/world.rs +++ b/crates/tinymist-world/src/world.rs @@ -190,7 +190,7 @@ impl CompilerUniverse { &self, file_path: Option, encoding: OffsetEncoding, - ) -> ZResult>> { + ) -> Result>> { let world = match file_path { Some(e) => { let path = Path::new(&e); diff --git a/crates/tinymist/Cargo.toml b/crates/tinymist/Cargo.toml index 136c2cb9..836ad2f9 100644 --- a/crates/tinymist/Cargo.toml +++ b/crates/tinymist/Cargo.toml @@ -60,7 +60,6 @@ strum.workspace = true sync-lsp.workspace = true tinymist-assets = { workspace = true } tinymist-query.workspace = true -tinymist-fs.workspace = true tinymist-std.workspace = true tinymist-core = { workspace = true, default-features = false, features = [] } tinymist-project.workspace = true diff --git a/crates/tinymist/src/lib.rs b/crates/tinymist/src/lib.rs index dbd23cb0..e436a5ae 100644 --- a/crates/tinymist/src/lib.rs +++ b/crates/tinymist/src/lib.rs @@ -42,13 +42,14 @@ pub use world::{CompileFontArgs, CompileOnceArgs, CompilePackageArgs}; use lsp_server::{RequestId, ResponseError}; use serde_json::from_value; use sync_lsp::*; +use tinymist_std::error::Result; use utils::*; use world::*; use tinymist_query::CompilerQueryResponse; /// The future type for a lsp query. -pub type QueryFuture = anyhow::Result>>; +pub type QueryFuture = Result>>; trait LspClientExt { fn schedule_query(&self, req_id: RequestId, query_fut: QueryFuture) -> ScheduledResult; diff --git a/crates/tinymist/src/main.rs b/crates/tinymist/src/main.rs index 58604efd..9d0d3b06 100644 --- a/crates/tinymist/src/main.rs +++ b/crates/tinymist/src/main.rs @@ -9,7 +9,6 @@ use std::{ sync::Arc, }; -use anyhow::{bail, Context}; use clap::Parser; use clap_builder::CommandFactory; use clap_complete::generate; @@ -32,6 +31,7 @@ use tinymist::{world::TaskInputs, world::WorldProvider}; use tinymist_core::LONG_VERSION; use tinymist_project::EntryResolver; use tinymist_query::package::PackageInfo; +use tinymist_std::{bail, error::prelude::*}; use typst::foundations::IntoValue; use typst_shim::utils::LazyHash; @@ -61,7 +61,7 @@ impl Default for Runtimes { static RUNTIMES: Lazy = Lazy::new(Default::default); /// The main entry point. -fn main() -> anyhow::Result<()> { +fn main() -> Result<()> { #[cfg(feature = "dhat-heap")] let _profiler = dhat::Profiler::new_heap(); @@ -103,9 +103,9 @@ fn main() -> anyhow::Result<()> { } /// Generates completion script to stdout. -pub fn completion(args: ShellCompletionArgs) -> anyhow::Result<()> { +pub fn completion(args: ShellCompletionArgs) -> Result<()> { let Some(shell) = args.shell.or_else(Shell::from_env) else { - anyhow::bail!("could not infer shell"); + tinymist_std::bail!("could not infer shell"); }; let mut cmd = CliArguments::command(); @@ -115,7 +115,7 @@ pub fn completion(args: ShellCompletionArgs) -> anyhow::Result<()> { } /// Runs compilation -pub fn compile(args: CompileArgs) -> anyhow::Result<()> { +pub fn compile(args: CompileArgs) -> Result<()> { use std::io::Write; let input = args @@ -150,7 +150,7 @@ pub fn compile(args: CompileArgs) -> anyhow::Result<()> { } /// The main entry point for the language server. -pub fn lsp_main(args: LspArgs) -> anyhow::Result<()> { +pub fn lsp_main(args: LspArgs) -> Result<()> { let pairs = LONG_VERSION.trim().split('\n'); let pairs = pairs .map(|e| e.splitn(2, ":").map(|e| e.trim()).collect::>()) @@ -178,19 +178,19 @@ pub fn lsp_main(args: LspArgs) -> anyhow::Result<()> { } /// The main entry point for the compiler. -pub fn trace_lsp_main(args: TraceLspArgs) -> anyhow::Result<()> { +pub fn trace_lsp_main(args: TraceLspArgs) -> Result<()> { let mut input = PathBuf::from(match args.compile.input { Some(value) => value, - None => return Err(anyhow::anyhow!("provide a valid path")), + None => Err(anyhow::anyhow!("provide a valid path"))?, }); let mut root_path = args.compile.root.unwrap_or(PathBuf::from(".")); if root_path.is_relative() { - root_path = std::env::current_dir()?.join(root_path); + root_path = std::env::current_dir().context("cwd")?.join(root_path); } if input.is_relative() { - input = std::env::current_dir()?.join(input); + input = std::env::current_dir().context("cwd")?.join(input); } if !input.starts_with(&root_path) { bail!("input file is not within the root path: {input:?} not in {root_path:?}"); @@ -233,7 +233,7 @@ pub fn trace_lsp_main(args: TraceLspArgs) -> anyhow::Result<()> { let resp = service.ready(()).unwrap(); let MaybeDone::Done(resp) = resp else { - bail!("internal error: not sync init") + anyhow::bail!("internal error: not sync init") }; resp.unwrap(); @@ -274,7 +274,7 @@ pub fn trace_lsp_main(args: TraceLspArgs) -> anyhow::Result<()> { } /// The main entry point for language server queries. -pub fn query_main(cmds: QueryCommands) -> anyhow::Result<()> { +pub fn query_main(cmds: QueryCommands) -> Result<()> { use tinymist_project::package::PackageRegistry; with_stdio_transport(MirrorArgs::default(), |conn| { @@ -297,7 +297,7 @@ pub fn query_main(cmds: QueryCommands) -> anyhow::Result<()> { let resp = service.ready(()).unwrap(); let MaybeDone::Done(resp) = resp else { - bail!("internal error: not sync init") + anyhow::bail!("internal error: not sync init") }; resp.unwrap(); diff --git a/crates/tinymist/src/project.rs b/crates/tinymist/src/project.rs index c6f9ab14..262c1534 100644 --- a/crates/tinymist/src/project.rs +++ b/crates/tinymist/src/project.rs @@ -23,8 +23,7 @@ pub use tinymist_project::*; use std::sync::Arc; -use anyhow::bail; -use log::{error, info, trace}; +use log::{error, trace}; use parking_lot::Mutex; use reflexo::{hash::FxHashMap, path::unix_slash}; use reflexo_typst::{typst::prelude::EcoVec, CompileReport}; @@ -34,9 +33,9 @@ use tinymist_query::{ CompilerQueryRequest, CompilerQueryResponse, DiagnosticsMap, SemanticRequest, StatefulRequest, VersionedDocument, }; -use tinymist_std::error::prelude::*; +use tinymist_std::{bail, error::prelude::*}; use tokio::sync::{mpsc, oneshot}; -use typst::{diag::SourceDiagnostic, World}; +use typst::diag::SourceDiagnostic; use crate::actor::editor::{CompileStatus, DocVersion, EditorRequest, TinymistCompileStatusEnum}; use crate::stats::{CompilerQueryStats, QueryStatGuard}; @@ -100,7 +99,7 @@ pub struct Project { impl Project { /// Snapshot the compiler thread for tasks - pub fn snapshot(&mut self) -> ZResult { + pub fn snapshot(&mut self) -> Result { let (tx, rx) = oneshot::channel(); let snap = self.state.snapshot(); let _ = tx.send(snap); @@ -109,7 +108,7 @@ impl Project { } /// Snapshot the compiler thread for language queries - pub fn query_snapshot(&mut self, q: Option<&CompilerQueryRequest>) -> ZResult { + pub fn query_snapshot(&mut self, q: Option<&CompilerQueryRequest>) -> Result { let fut = self.snapshot()?; let analysis = self.analysis.clone(); let rev_lock = analysis.lock_revision(q); @@ -150,7 +149,7 @@ impl Project { &mut self, group: &str, entry: EntryState, - ) -> ZResult { + ) -> Result { self.state.restart_dedicate(group, entry) } } @@ -367,7 +366,7 @@ pub struct WorldSnapFut { impl WorldSnapFut { /// wait for the snapshot to be ready - pub async fn receive(self) -> ZResult> { + pub async fn receive(self) -> Result> { self.rx .await .map_err(map_string_err("failed to get snapshot")) @@ -382,7 +381,7 @@ pub struct QuerySnapFut { impl QuerySnapFut { /// wait for the snapshot to be ready - pub async fn receive(self) -> ZResult { + pub async fn receive(self) -> Result { let snap = self.fut.receive().await?; Ok(QuerySnap { snap, @@ -416,7 +415,7 @@ impl QuerySnap { self, query: T, wrapper: fn(Option) -> CompilerQueryResponse, - ) -> anyhow::Result { + ) -> Result { let doc = self.snap.success_doc.as_ref().map(|doc| VersionedDocument { version: self.world.revision().get(), document: doc.clone(), @@ -429,20 +428,16 @@ impl QuerySnap { self, query: T, wrapper: fn(Option) -> CompilerQueryResponse, - ) -> anyhow::Result { + ) -> Result { self.run_analysis(|ctx| query.request(ctx)).map(wrapper) } - pub fn run_analysis(self, f: impl FnOnce(&mut LocalContextGuard) -> T) -> anyhow::Result { + pub fn run_analysis(self, f: impl FnOnce(&mut LocalContextGuard) -> T) -> Result { let world = self.snap.world; - let Some(main) = world.main_id() else { + let Some(..) = world.main_id() else { error!("Project: main file is not set"); bail!("main file is not set"); }; - world.source(main).map_err(|err| { - info!("Project: failed to prepare main file: {err:?}"); - anyhow::anyhow!("failed to get source: {err}") - })?; let mut analysis = self.analysis.snapshot_(world, self.rev_lock); Ok(f(&mut analysis)) diff --git a/crates/tinymist/src/server.rs b/crates/tinymist/src/server.rs index fd016e1a..a29f9898 100644 --- a/crates/tinymist/src/server.rs +++ b/crates/tinymist/src/server.rs @@ -6,8 +6,6 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use actor::editor::EditorActor; -use anyhow::anyhow; -use anyhow::Context; use log::{error, info, trace}; use lsp_server::RequestId; use lsp_types::request::{GotoDeclarationParams, WorkspaceConfiguration}; @@ -304,7 +302,7 @@ impl LanguageState { impl LanguageState { // todo: handle error - fn register_capability(&self, registrations: Vec) -> anyhow::Result<()> { + fn register_capability(&self, registrations: Vec) -> Result<()> { self.client.send_request_::( RegistrationParams { registrations }, |_, resp| { @@ -316,7 +314,7 @@ impl LanguageState { Ok(()) } - fn unregister_capability(&self, unregisterations: Vec) -> anyhow::Result<()> { + fn unregister_capability(&self, unregisterations: Vec) -> Result<()> { self.client.send_request_::( UnregistrationParams { unregisterations }, |_, resp| { @@ -329,7 +327,7 @@ impl LanguageState { } /// Registers or unregisters semantic tokens. - fn enable_sema_token_caps(&mut self, enable: bool) -> anyhow::Result<()> { + fn enable_sema_token_caps(&mut self, enable: bool) -> Result<()> { if !self.const_config().tokens_dynamic_registration { trace!("skip register semantic by config"); return Ok(()); @@ -375,7 +373,7 @@ impl LanguageState { } /// Registers or unregisters document formatter. - fn enable_formatter_caps(&mut self, enable: bool) -> anyhow::Result<()> { + fn enable_formatter_caps(&mut self, enable: bool) -> Result<()> { if !self.const_config().doc_fmt_dynamic_registration { trace!("skip dynamic register formatter by config"); return Ok(()); @@ -980,7 +978,7 @@ impl LanguageState { } /// Snapshot the compiler thread for tasks - pub fn snapshot(&mut self) -> ZResult { + pub fn snapshot(&mut self) -> Result { self.project.snapshot() } @@ -990,7 +988,7 @@ impl LanguageState { } /// Snapshot the compiler thread for language queries - pub fn query_snapshot(&mut self) -> ZResult { + pub fn query_snapshot(&mut self) -> Result { self.project.query_snapshot(None) } @@ -998,7 +996,7 @@ impl LanguageState { pub fn query_snapshot_with_stat( &mut self, q: &CompilerQueryRequest, - ) -> ZResult { + ) -> Result { let name: &'static str = q.into(); let path = q.associated_path(); let stat = self.project.stats.query_stat(path, name); @@ -1116,7 +1114,7 @@ impl LanguageState { impl LanguageState { /// Restart the primary server. - pub fn restart_primary(&mut self) -> ZResult { + pub fn restart_primary(&mut self) -> Result { let entry = self.entry_resolver().resolve_default(); let config = &self.config; @@ -1148,7 +1146,7 @@ impl LanguageState { &mut self, dedicate: &str, entry: Option, - ) -> ZResult { + ) -> Result { let entry = self.config.compile.entry_resolver.resolve(entry); self.project.restart_dedicate(dedicate, entry) } @@ -1367,10 +1365,10 @@ impl LanguageState { pub fn query_source( &self, path: ImmutPath, - f: impl FnOnce(Source) -> anyhow::Result, - ) -> anyhow::Result { + f: impl FnOnce(Source) -> Result, + ) -> Result { let snapshot = self.memory_changes.get(&path); - let snapshot = snapshot.ok_or_else(|| anyhow!("file missing {path:?}"))?; + let snapshot = snapshot.ok_or_else(|| anyhow::anyhow!("file missing {path:?}"))?; let source = snapshot.content.clone(); f(source) } diff --git a/crates/tinymist/src/tool/preview.rs b/crates/tinymist/src/tool/preview.rs index 5ef251ee..6003f9c6 100644 --- a/crates/tinymist/src/tool/preview.rs +++ b/crates/tinymist/src/tool/preview.rs @@ -555,7 +555,7 @@ pub async fn make_http_server( } /// Entry point of the preview tool. -pub async fn preview_main(args: PreviewCliArgs) -> anyhow::Result<()> { +pub async fn preview_main(args: PreviewCliArgs) -> Result<()> { log::info!("Arguments: {args:#?}"); let handle = tokio::runtime::Handle::current(); @@ -615,7 +615,7 @@ pub async fn preview_main(args: PreviewCliArgs) -> anyhow::Result<()> { ); let registered = preview_state.register(&server.primary.id, previewer.compile_watcher()); if !registered { - anyhow::bail!("failed to register preview"); + tinymist_std::bail!("failed to register preview"); } let handle = Arc::new(PreviewProjectHandler { diff --git a/crates/tinymist/src/tool/project.rs b/crates/tinymist/src/tool/project.rs index 6f8fb2d9..f43a7280 100644 --- a/crates/tinymist/src/tool/project.rs +++ b/crates/tinymist/src/tool/project.rs @@ -3,13 +3,13 @@ use std::path::Path; use crate::project::*; -use prelude::ZResult; +use prelude::Result; trait LockFileExt { fn declare(&mut self, args: &DocNewArgs) -> Id; - fn preview(&mut self, doc_id: Id, args: &TaskPreviewArgs) -> ZResult; - fn compile(&mut self, args: TaskCompileArgs) -> ZResult; - fn export(&mut self, doc_id: Id, args: TaskCompileArgs) -> ZResult; + fn preview(&mut self, doc_id: Id, args: &TaskPreviewArgs) -> Result; + fn compile(&mut self, args: TaskCompileArgs) -> Result; + fn export(&mut self, doc_id: Id, args: TaskCompileArgs) -> Result; } impl LockFileExt for LockFile { @@ -56,12 +56,12 @@ impl LockFileExt for LockFile { id } - fn compile(&mut self, args: TaskCompileArgs) -> ZResult { + fn compile(&mut self, args: TaskCompileArgs) -> Result { let id = self.declare(&args.declare); self.export(id, args) } - fn export(&mut self, doc_id: Id, args: TaskCompileArgs) -> ZResult { + fn export(&mut self, doc_id: Id, args: TaskCompileArgs) -> Result { let task = args.to_task(doc_id)?; let task_id = task.id().clone(); @@ -70,7 +70,7 @@ impl LockFileExt for LockFile { Ok(task_id) } - fn preview(&mut self, doc_id: Id, args: &TaskPreviewArgs) -> ZResult { + fn preview(&mut self, doc_id: Id, args: &TaskPreviewArgs) -> Result { let task_id = args .name .as_ref() @@ -91,7 +91,7 @@ impl LockFileExt for LockFile { } /// Project document commands' main -pub fn project_main(args: DocCommands) -> anyhow::Result<()> { +pub fn project_main(args: DocCommands) -> Result<()> { LockFile::update(Path::new("."), |state| { match args { DocCommands::New(args) => { @@ -112,7 +112,7 @@ pub fn project_main(args: DocCommands) -> anyhow::Result<()> { } /// Project task commands' main -pub fn task_main(args: TaskCommands) -> anyhow::Result<()> { +pub fn task_main(args: TaskCommands) -> Result<()> { LockFile::update(Path::new("."), |state| { match args { TaskCommands::Compile(args) => {