mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-04 02:08:17 +00:00
feat: prepublish tinymist-world (#1248)
This commit is contained in:
parent
d534325c83
commit
88912bc12e
17 changed files with 220 additions and 78 deletions
|
@ -17,13 +17,19 @@ crate-type = ["cdylib", "rlib"]
|
|||
[features]
|
||||
default = ["web", "no-content-hint"]
|
||||
|
||||
web = ["wasm-bindgen", "no-content-hint", "tinymist-world/web"]
|
||||
web = [
|
||||
"wasm-bindgen",
|
||||
"no-content-hint",
|
||||
"tinymist-world/web",
|
||||
"reflexo-typst/web",
|
||||
]
|
||||
|
||||
no-content-hint = ["tinymist-world/no-content-hint"]
|
||||
no-content-hint = ["reflexo-typst/no-content-hint"]
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = { version = "0.2.92", optional = true }
|
||||
tinymist-world.workspace = true
|
||||
reflexo-typst.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
anyhow.workspace = true
|
||||
|
|
|
@ -40,8 +40,7 @@ notify.workspace = true
|
|||
[features]
|
||||
|
||||
fonts = ["typst-assets/fonts"]
|
||||
# "reflexo-typst/no-content-hint"
|
||||
no-content-hint = []
|
||||
no-content-hint = ["reflexo-typst/no-content-hint"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
@ -56,23 +56,14 @@ pub trait WorldProvider {
|
|||
impl WorldProvider for CompileOnceArgs {
|
||||
fn resolve(&self) -> Result<LspUniverse> {
|
||||
let entry = self.entry()?.try_into()?;
|
||||
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 inputs = self.resolve_inputs().unwrap_or_default();
|
||||
let fonts = Arc::new(LspUniverseBuilder::resolve_fonts(self.font.clone())?);
|
||||
let package = LspUniverseBuilder::resolve_package(
|
||||
self.cert.as_deref().map(From::from),
|
||||
Some(&self.package),
|
||||
);
|
||||
|
||||
Ok(LspUniverseBuilder::build(
|
||||
entry,
|
||||
Arc::new(LazyHash::new(inputs)),
|
||||
Arc::new(fonts),
|
||||
package,
|
||||
))
|
||||
Ok(LspUniverseBuilder::build(entry, inputs, fonts, package))
|
||||
}
|
||||
|
||||
fn entry(&self) -> Result<EntryOpts> {
|
||||
|
|
|
@ -11,10 +11,6 @@ homepage.workspace = true
|
|||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["no-content-hint"]
|
||||
no-content-hint = []
|
||||
|
||||
[dependencies]
|
||||
|
||||
anyhow.workspace = true
|
||||
|
@ -59,7 +55,7 @@ hashbrown.workspace = true
|
|||
triomphe.workspace = true
|
||||
base64.workspace = true
|
||||
typlite.workspace = true
|
||||
tinymist-world = { workspace = true, features = ["no-content-hint"] }
|
||||
tinymist-world = { workspace = true }
|
||||
tinymist-project.workspace = true
|
||||
tinymist-analysis.workspace = true
|
||||
tinymist-derive.workspace = true
|
||||
|
@ -74,5 +70,8 @@ typst-assets = { workspace = true, features = ["fonts"] }
|
|||
sha2 = { version = "0.10" }
|
||||
hex = { version = "0.4" }
|
||||
|
||||
[features]
|
||||
no-content-hint = ["tinymist-project/no-content-hint"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
//! Additional functions wrapping Rust's standard library.
|
||||
|
||||
pub mod adt;
|
||||
pub use tinymist_analysis::debug_loc;
|
||||
pub mod error;
|
||||
pub mod fs;
|
||||
pub mod hash;
|
||||
|
@ -14,6 +13,8 @@ pub use concepts::*;
|
|||
|
||||
pub use error::{ErrKind, Error, Result};
|
||||
|
||||
#[cfg(feature = "typst")]
|
||||
pub use tinymist_analysis::debug_loc;
|
||||
#[cfg(feature = "typst")]
|
||||
pub use typst_shim;
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# tinymist-vfs
|
||||
|
||||
Vfs for [tinymist.](https://github.com/Myriad-Dreamin/tinymist)
|
||||
Virtual file system for [tinymist.](https://github.com/Myriad-Dreamin/tinymist)
|
||||
|
|
|
@ -41,28 +41,31 @@ mod utils;
|
|||
mod path_mapper;
|
||||
pub use path_mapper::{PathResolution, RootResolver, WorkspaceResolution, WorkspaceResolver};
|
||||
|
||||
use rpds::RedBlackTreeMapSync;
|
||||
pub use typst::foundations::Bytes;
|
||||
pub use typst::syntax::FileId as TypstFileId;
|
||||
|
||||
pub use tinymist_std::time::Time;
|
||||
pub use tinymist_std::ImmutPath;
|
||||
use typst::syntax::Source;
|
||||
|
||||
use core::fmt;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::sync::OnceLock;
|
||||
use std::{path::Path, sync::Arc};
|
||||
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use rpds::RedBlackTreeMapSync;
|
||||
use typst::diag::{FileError, FileResult};
|
||||
use typst::foundations::Dict;
|
||||
use typst::syntax::Source;
|
||||
use typst::utils::LazyHash;
|
||||
|
||||
use crate::notify::NotifyAccessModel;
|
||||
use crate::overlay::OverlayAccessModel;
|
||||
use crate::resolve::ResolveAccessModel;
|
||||
|
||||
pub use tinymist_std::time::Time;
|
||||
pub use tinymist_std::ImmutPath;
|
||||
pub use typst::foundations::Bytes;
|
||||
pub use typst::syntax::FileId as TypstFileId;
|
||||
|
||||
/// Handle to a file in [`Vfs`]
|
||||
pub type FileId = TypstFileId;
|
||||
/// Immutable prehashed reference to dictionary.
|
||||
pub type ImmutDict = Arc<LazyHash<Dict>>;
|
||||
|
||||
/// A trait for accessing underlying file system.
|
||||
///
|
||||
|
|
|
@ -26,7 +26,6 @@ hex.workspace = true
|
|||
js-sys = { workspace = true, optional = true }
|
||||
log.workspace = true
|
||||
parking_lot.workspace = true
|
||||
reflexo-typst = { workspace = true, default-features = false }
|
||||
reqwest = { workspace = true, optional = true }
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
@ -54,7 +53,6 @@ web = [
|
|||
"serde-wasm-bindgen",
|
||||
"tinymist-std/web",
|
||||
"tinymist-vfs/web",
|
||||
"reflexo-typst/web",
|
||||
]
|
||||
browser = ["tinymist-vfs/browser", "web"]
|
||||
system = [
|
||||
|
@ -63,23 +61,10 @@ system = [
|
|||
"http-registry",
|
||||
"tinymist-std/system",
|
||||
"tinymist-vfs/system",
|
||||
"reflexo-typst/system",
|
||||
]
|
||||
|
||||
# todo: remove me
|
||||
no-content-hint = ["reflexo-typst/no-content-hint"]
|
||||
[dev-dependencies]
|
||||
tinymist-world = { path = ".", features = ["system"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# ====
|
||||
|
||||
# [dependencies]
|
||||
|
||||
|
||||
# [features]
|
||||
# fonts = ["typst-assets/fonts"]
|
||||
# no-content-hint = ["reflexo-typst/no-content-hint"]
|
||||
|
||||
# [lints]
|
||||
# workspace = true
|
||||
|
|
|
@ -1,3 +1,24 @@
|
|||
# tinymist-world
|
||||
|
||||
Typst's World implementation for [tinymist.](https://github.com/Myriad-Dreamin/tinymist)
|
||||
|
||||
### Example: Resolves a system universe from system arguments
|
||||
|
||||
```rust
|
||||
let args = CompileOnceArgs::parse();
|
||||
let universe = args
|
||||
.resolve_system()
|
||||
.expect("failed to resolve system universe");
|
||||
```
|
||||
|
||||
### Example: Runs a typst compilation
|
||||
|
||||
```rust
|
||||
let world = verse.snapshot();
|
||||
// in current thread
|
||||
let doc = typst::compile(&world)?;
|
||||
// the snapshot is Send + Sync
|
||||
std::thread::spawn(move || {
|
||||
let doc = typst::compile(&world)?;
|
||||
});
|
||||
```
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
use std::path::PathBuf;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use clap::{builder::ValueParser, ArgAction, Parser};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tinymist_std::{bail, error::prelude::*};
|
||||
use tinymist_vfs::ImmutDict;
|
||||
use typst::{foundations::IntoValue, utils::LazyHash};
|
||||
|
||||
use crate::EntryOpts;
|
||||
|
||||
const ENV_PATH_SEP: char = if cfg!(windows) { ';' } else { ':' };
|
||||
|
||||
|
@ -88,6 +96,84 @@ pub struct CompileOnceArgs {
|
|||
pub cert: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl CompileOnceArgs {
|
||||
pub fn resolve_inputs(&self) -> Option<ImmutDict> {
|
||||
if self.inputs.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let pairs = self.inputs.iter();
|
||||
let pairs = pairs.map(|(k, v)| (k.as_str().into(), v.as_str().into_value()));
|
||||
Some(Arc::new(LazyHash::new(pairs.collect())))
|
||||
}
|
||||
|
||||
/// Resolves the entry options.
|
||||
pub fn resolve_sys_entry_opts(&self) -> Result<EntryOpts> {
|
||||
let mut cwd = None;
|
||||
let mut cwd = move || {
|
||||
cwd.get_or_insert_with(|| {
|
||||
std::env::current_dir().context("failed to get current directory")
|
||||
})
|
||||
.clone()
|
||||
};
|
||||
|
||||
let main = {
|
||||
let input = self.input.as_ref().context("entry file must be provided")?;
|
||||
let input = Path::new(&input);
|
||||
if input.is_absolute() {
|
||||
input.to_owned()
|
||||
} else {
|
||||
cwd()?.join(input)
|
||||
}
|
||||
};
|
||||
|
||||
let root = if let Some(root) = &self.root {
|
||||
if root.is_absolute() {
|
||||
root.clone()
|
||||
} else {
|
||||
cwd()?.join(root)
|
||||
}
|
||||
} else {
|
||||
main.parent()
|
||||
.context("entry file don't have a valid parent as root")?
|
||||
.to_owned()
|
||||
};
|
||||
|
||||
let relative_main = match main.strip_prefix(&root) {
|
||||
Ok(relative_main) => relative_main,
|
||||
Err(_) => {
|
||||
log::error!("entry file must be inside the root, file: {main:?}, root: {root:?}");
|
||||
bail!("entry file must be inside the root, file: {main:?}, root: {root:?}");
|
||||
}
|
||||
};
|
||||
|
||||
Ok(EntryOpts::new_rooted(
|
||||
root.clone(),
|
||||
Some(relative_main.to_owned()),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "system")]
|
||||
impl CompileOnceArgs {
|
||||
/// Resolves the arguments into a system universe. This is also a sample
|
||||
/// implementation of how to resolve the arguments (user inputs) into a
|
||||
/// universe.
|
||||
pub fn resolve_system(&self) -> Result<crate::TypstSystemUniverse> {
|
||||
use crate::system::SystemUniverseBuilder;
|
||||
|
||||
let entry = self.resolve_sys_entry_opts()?.try_into()?;
|
||||
let inputs = self.resolve_inputs().unwrap_or_default();
|
||||
let fonts = Arc::new(SystemUniverseBuilder::resolve_fonts(self.font.clone())?);
|
||||
let package = SystemUniverseBuilder::resolve_package(
|
||||
self.cert.as_deref().map(From::from),
|
||||
Some(&self.package),
|
||||
);
|
||||
|
||||
Ok(SystemUniverseBuilder::build(entry, inputs, fonts, package))
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses key/value pairs split by the first equal sign.
|
||||
///
|
||||
/// This function will return an error if the argument contains no equals sign
|
||||
|
|
|
@ -146,3 +146,6 @@ pub mod build_info {
|
|||
/// The version of the reflexo-world crate.
|
||||
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
use std::sync::Arc;
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
use tinymist_std::error::prelude::*;
|
||||
use tinymist_vfs::{system::SystemAccessModel, Vfs};
|
||||
use tinymist_std::{error::prelude::*, ImmutPath};
|
||||
use tinymist_vfs::{system::SystemAccessModel, ImmutDict, Vfs};
|
||||
use typst::utils::LazyHash;
|
||||
|
||||
use crate::{
|
||||
config::CompileOpts,
|
||||
args::{CompileFontArgs, CompilePackageArgs},
|
||||
config::{CompileFontOpts, CompileOpts},
|
||||
font::{system::SystemFontSearcher, FontResolverImpl},
|
||||
package::{http::HttpRegistry, RegistryPathMapper},
|
||||
EntryState,
|
||||
};
|
||||
|
||||
/// type trait of [`TypstSystemWorld`].
|
||||
|
@ -52,3 +54,52 @@ impl TypstSystemUniverse {
|
|||
Ok(searcher.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Builders for Typst universe.
|
||||
pub struct SystemUniverseBuilder;
|
||||
|
||||
impl SystemUniverseBuilder {
|
||||
/// Create [`TypstSystemUniverse`] with the given options.
|
||||
/// See [`LspCompilerFeat`] for instantiation details.
|
||||
pub fn build(
|
||||
entry: EntryState,
|
||||
inputs: ImmutDict,
|
||||
font_resolver: Arc<FontResolverImpl>,
|
||||
package_registry: HttpRegistry,
|
||||
) -> TypstSystemUniverse {
|
||||
let registry = Arc::new(package_registry);
|
||||
let resolver = Arc::new(RegistryPathMapper::new(registry.clone()));
|
||||
|
||||
TypstSystemUniverse::new_raw(
|
||||
entry,
|
||||
Some(inputs),
|
||||
Vfs::new(resolver, SystemAccessModel {}),
|
||||
registry,
|
||||
font_resolver,
|
||||
)
|
||||
}
|
||||
|
||||
/// Resolve fonts from given options.
|
||||
pub fn resolve_fonts(args: CompileFontArgs) -> Result<FontResolverImpl> {
|
||||
let mut searcher = SystemFontSearcher::new();
|
||||
searcher.resolve_opts(CompileFontOpts {
|
||||
font_profile_cache_path: Default::default(),
|
||||
font_paths: args.font_paths,
|
||||
no_system_fonts: args.ignore_system_fonts,
|
||||
with_embedded_fonts: typst_assets::fonts().map(Cow::Borrowed).collect(),
|
||||
})?;
|
||||
Ok(searcher.into())
|
||||
}
|
||||
|
||||
/// Resolve package registry from given options.
|
||||
pub fn resolve_package(
|
||||
cert_path: Option<ImmutPath>,
|
||||
args: Option<&CompilePackageArgs>,
|
||||
) -> HttpRegistry {
|
||||
HttpRegistry::new(
|
||||
cert_path,
|
||||
args.and_then(|args| Some(args.package_path.clone()?.into())),
|
||||
args.and_then(|args| Some(args.package_cache_path.clone()?.into())),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
15
crates/tinymist-world/src/tests.rs
Normal file
15
crates/tinymist-world/src/tests.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use clap::Parser;
|
||||
|
||||
use crate::args::CompileOnceArgs;
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "system")]
|
||||
fn test_args() {
|
||||
let args = CompileOnceArgs::parse_from(["tinymist", "main.typ"]);
|
||||
let verse = args
|
||||
.resolve_system()
|
||||
.expect("failed to resolve system universe");
|
||||
|
||||
let world = verse.snapshot();
|
||||
let _res = typst::compile(&world);
|
||||
}
|
|
@ -100,6 +100,7 @@ embed-fonts = ["tinymist-project/fonts"]
|
|||
# Disable the default content hint.
|
||||
# This requires modifying typst.
|
||||
no-content-hint = [
|
||||
"tinymist-query/no-content-hint",
|
||||
"tinymist-project/no-content-hint",
|
||||
"reflexo-typst/no-content-hint",
|
||||
"reflexo-vec2svg/no-content-hint",
|
||||
|
|
|
@ -596,20 +596,11 @@ impl CompileConfig {
|
|||
Err(e) => bail!("failed to parse typstExtraArgs: {e}"),
|
||||
};
|
||||
|
||||
// Convert the input pairs to a dictionary.
|
||||
let inputs: TypstDict = if command.inputs.is_empty() {
|
||||
TypstDict::default()
|
||||
} else {
|
||||
let pairs = command.inputs.iter();
|
||||
let pairs = pairs.map(|(k, v)| (k.as_str().into(), v.as_str().into_value()));
|
||||
pairs.collect()
|
||||
};
|
||||
|
||||
// todo: the command.root may be not absolute
|
||||
self.typst_extra_args = Some(CompileExtraOpts {
|
||||
inputs: command.resolve_inputs().unwrap_or_default(),
|
||||
entry: command.input.map(|e| Path::new(&e).into()),
|
||||
root_dir: command.root.as_ref().map(|r| r.as_path().into()),
|
||||
inputs: Arc::new(LazyHash::new(inputs)),
|
||||
font: command.font,
|
||||
package: command.package,
|
||||
creation_timestamp: command.creation_timestamp,
|
||||
|
|
|
@ -6,7 +6,6 @@ use std::{
|
|||
io,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
|
@ -16,7 +15,7 @@ use futures::future::MaybeDone;
|
|||
use lsp_server::RequestId;
|
||||
use once_cell::sync::Lazy;
|
||||
use reflexo::ImmutPath;
|
||||
use reflexo_typst::{package::PackageSpec, TypstDict};
|
||||
use reflexo_typst::package::PackageSpec;
|
||||
use serde_json::Value as JsonValue;
|
||||
use sync_lsp::{
|
||||
internal_error,
|
||||
|
@ -32,8 +31,6 @@ 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;
|
||||
|
||||
use crate::args::*;
|
||||
|
||||
|
@ -147,11 +144,11 @@ pub fn lsp_main(args: LspArgs) -> Result<()> {
|
|||
|
||||
/// The main entry point for the compiler.
|
||||
pub fn trace_lsp_main(args: TraceLspArgs) -> Result<()> {
|
||||
let inputs = args.compile.resolve_inputs();
|
||||
let mut input = PathBuf::from(match args.compile.input {
|
||||
Some(value) => value,
|
||||
None => Err(anyhow::anyhow!("provide a valid path"))?,
|
||||
});
|
||||
|
||||
let mut root_path = args.compile.root.unwrap_or(PathBuf::from("."));
|
||||
|
||||
if root_path.is_relative() {
|
||||
|
@ -164,14 +161,6 @@ pub fn trace_lsp_main(args: TraceLspArgs) -> Result<()> {
|
|||
bail!("input file is not within the root path: {input:?} not in {root_path:?}");
|
||||
}
|
||||
|
||||
let inputs = Arc::new(LazyHash::new(if args.compile.inputs.is_empty() {
|
||||
TypstDict::default()
|
||||
} else {
|
||||
let pairs = args.compile.inputs.iter();
|
||||
let pairs = pairs.map(|(k, v)| (k.as_str().into(), v.as_str().into_value()));
|
||||
pairs.collect()
|
||||
}));
|
||||
|
||||
with_stdio_transport(args.mirror.clone(), |conn| {
|
||||
let client_root = LspClientRoot::new(RUNTIMES.tokio_runtime.handle().clone(), conn.sender);
|
||||
let client = client_root.weak();
|
||||
|
@ -227,7 +216,7 @@ pub fn trace_lsp_main(args: TraceLspArgs) -> Result<()> {
|
|||
RUNTIMES.tokio_runtime.block_on(async {
|
||||
let w = snap.world.task(TaskInputs {
|
||||
entry: Some(entry),
|
||||
inputs: Some(inputs),
|
||||
inputs,
|
||||
});
|
||||
|
||||
UserActionTask::trace_main(client, state, &w, args.rpc_kind, req_id).await
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue