refactor: combine typst-preview and tinymist compiler (#337)

* refactor: combine typst-preview and tinymist compiler

* dev: update link

* fix: bad changes

* dev: sync snapshot compiler
This commit is contained in:
Myriad-Dreamin 2024-06-22 15:06:42 +08:00 committed by GitHub
parent b265dd49d6
commit 7d65829ed7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 397 additions and 418 deletions

87
Cargo.lock generated
View file

@ -140,6 +140,16 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "archery"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8967cd1cc9e9e1954f644e14fbd6042fe9a37da96c52a67e44a2ac18261f8561"
dependencies = [
"static_assertions",
"triomphe",
]
[[package]] [[package]]
name = "arraydeque" name = "arraydeque"
version = "0.5.1" version = "0.5.1"
@ -2889,8 +2899,7 @@ dependencies = [
[[package]] [[package]]
name = "reflexo" name = "reflexo"
version = "0.5.0-rc4" version = "0.5.0-rc4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/Myriad-Dreamin/typst.ts/?rev=36b969e2d5743544dca8679c53c89129b989ec80#36b969e2d5743544dca8679c53c89129b989ec80"
checksum = "803cbdda7d5beefc048966f342ba9a13dab586f136a8fe7f6d915b2f073a9151"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"bitvec", "bitvec",
@ -2898,10 +2907,12 @@ dependencies = [
"dashmap", "dashmap",
"ecow 0.2.2", "ecow 0.2.2",
"fxhash", "fxhash",
"instant",
"once_cell", "once_cell",
"parking_lot", "parking_lot",
"path-clean", "path-clean",
"rkyv", "rkyv",
"rustc-hash",
"serde", "serde",
"serde_json", "serde_json",
"serde_repr", "serde_repr",
@ -2910,6 +2921,55 @@ dependencies = [
"tiny-skia-path", "tiny-skia-path",
] ]
[[package]]
name = "reflexo-vfs"
version = "0.5.0-rc4"
source = "git+https://github.com/Myriad-Dreamin/typst.ts/?rev=36b969e2d5743544dca8679c53c89129b989ec80#36b969e2d5743544dca8679c53c89129b989ec80"
dependencies = [
"append-only-vec",
"indexmap 2.2.6",
"log",
"nohash-hasher",
"once_cell",
"parking_lot",
"reflexo",
"rpds",
"rustc-hash",
"typst",
]
[[package]]
name = "reflexo-world"
version = "0.5.0-rc4"
source = "git+https://github.com/Myriad-Dreamin/typst.ts/?rev=36b969e2d5743544dca8679c53c89129b989ec80#36b969e2d5743544dca8679c53c89129b989ec80"
dependencies = [
"append-only-vec",
"chrono",
"codespan-reporting",
"comemo 0.4.0",
"dashmap",
"dirs",
"flate2",
"fontdb",
"hex",
"indexmap 2.2.6",
"log",
"nohash-hasher",
"once_cell",
"parking_lot",
"reflexo",
"reflexo-vfs",
"reqwest",
"rustc-hash",
"serde",
"serde_json",
"sha2",
"strum 0.25.0",
"tar",
"typst",
"typst-ts-core",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.10.4" version = "1.10.4"
@ -3075,6 +3135,15 @@ version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
[[package]]
name = "rpds"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0e15515d3ce3313324d842629ea4905c25a13f81953eadb88f85516f59290a4"
dependencies = [
"archery",
]
[[package]] [[package]]
name = "rust_iso3166" name = "rust_iso3166"
version = "0.1.13" version = "0.1.13"
@ -4425,12 +4494,10 @@ dependencies = [
[[package]] [[package]]
name = "typst-ts-compiler" name = "typst-ts-compiler"
version = "0.5.0-rc4" version = "0.5.0-rc4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/Myriad-Dreamin/typst.ts/?rev=36b969e2d5743544dca8679c53c89129b989ec80#36b969e2d5743544dca8679c53c89129b989ec80"
checksum = "1b5d68fc30324347a294155cc4691defa4e01aaff23d7e3c1c40a3c8e5d22e3f"
dependencies = [ dependencies = [
"append-only-vec", "append-only-vec",
"base64 0.22.1", "base64 0.22.1",
"chrono",
"codespan-reporting", "codespan-reporting",
"comemo 0.4.0", "comemo 0.4.0",
"dirs", "dirs",
@ -4446,7 +4513,8 @@ dependencies = [
"once_cell", "once_cell",
"parking_lot", "parking_lot",
"pathdiff", "pathdiff",
"reqwest", "reflexo-vfs",
"reflexo-world",
"rustc-hash", "rustc-hash",
"serde", "serde",
"serde_json", "serde_json",
@ -4463,8 +4531,7 @@ dependencies = [
[[package]] [[package]]
name = "typst-ts-core" name = "typst-ts-core"
version = "0.5.0-rc4" version = "0.5.0-rc4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/Myriad-Dreamin/typst.ts/?rev=36b969e2d5743544dca8679c53c89129b989ec80#36b969e2d5743544dca8679c53c89129b989ec80"
checksum = "5b9355a7f3b2f687088524fa1d5c0d0cbe45878e9672d7a321aec491ae10b1dd"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"base64-serde", "base64-serde",
@ -4491,6 +4558,7 @@ dependencies = [
"serde_with", "serde_with",
"sha2", "sha2",
"siphasher 1.0.1", "siphasher 1.0.1",
"svgtypes",
"tiny-skia", "tiny-skia",
"tiny-skia-path", "tiny-skia-path",
"ttf-parser", "ttf-parser",
@ -4501,8 +4569,7 @@ dependencies = [
[[package]] [[package]]
name = "typst-ts-svg-exporter" name = "typst-ts-svg-exporter"
version = "0.5.0-rc4" version = "0.5.0-rc4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/Myriad-Dreamin/typst.ts/?rev=36b969e2d5743544dca8679c53c89129b989ec80#36b969e2d5743544dca8679c53c89129b989ec80"
checksum = "13b1e7baba2453d072cbc1e97a43d5a8ecfe36299fc103e9156bb9470abd35e0"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"comemo 0.4.0", "comemo 0.4.0",

View file

@ -51,6 +51,7 @@ typst-assets = "0.11.1"
reflexo = { version = "0.5.0-rc4", default-features = false, features = [ reflexo = { version = "0.5.0-rc4", default-features = false, features = [
"flat-vector", "flat-vector",
] } ] }
reflexo-world = { version = "0.5.0-rc4", features = ["system"] }
typst-ts-core = { version = "0.5.0-rc4", default-features = false } typst-ts-core = { version = "0.5.0-rc4", default-features = false }
typst-ts-compiler = { version = "0.5.0-rc4" } typst-ts-compiler = { version = "0.5.0-rc4" }
typst-ts-svg-exporter = { version = "0.5.0-rc4" } typst-ts-svg-exporter = { version = "0.5.0-rc4" }
@ -143,13 +144,15 @@ typst-syntax = { git = "https://github.com/Myriad-Dreamin/typst.git", branch = "
# typst-render = { path = "../typst/crates/typst-render" } # typst-render = { path = "../typst/crates/typst-render" }
# typst-syntax = { path = "../typst/crates/typst-syntax" } # typst-syntax = { path = "../typst/crates/typst-syntax" }
# typst-ts-svg-exporter = { git = "https://github.com/Myriad-Dreamin/typst.ts/", rev = "9cced415e29e5e341ad4bdcc32ab3e67ffad74db" } typst-ts-svg-exporter = { git = "https://github.com/Myriad-Dreamin/typst.ts/", rev = "36b969e2d5743544dca8679c53c89129b989ec80" }
# reflexo = { git = "https://github.com/Myriad-Dreamin/typst.ts/", rev = "9cced415e29e5e341ad4bdcc32ab3e67ffad74db" } reflexo = { git = "https://github.com/Myriad-Dreamin/typst.ts/", rev = "36b969e2d5743544dca8679c53c89129b989ec80" }
# typst-ts-core = { git = "https://github.com/Myriad-Dreamin/typst.ts/", rev = "9cced415e29e5e341ad4bdcc32ab3e67ffad74db" } reflexo-world = { git = "https://github.com/Myriad-Dreamin/typst.ts/", rev = "36b969e2d5743544dca8679c53c89129b989ec80" }
# typst-ts-compiler = { git = "https://github.com/Myriad-Dreamin/typst.ts/", rev = "9cced415e29e5e341ad4bdcc32ab3e67ffad74db" } typst-ts-core = { git = "https://github.com/Myriad-Dreamin/typst.ts/", rev = "36b969e2d5743544dca8679c53c89129b989ec80" }
typst-ts-compiler = { git = "https://github.com/Myriad-Dreamin/typst.ts/", rev = "36b969e2d5743544dca8679c53c89129b989ec80" }
# typst-ts-svg-exporter = { path = "../typst.ts/exporter/svg" } # typst-ts-svg-exporter = { path = "../typst.ts/exporter/svg" }
# reflexo = { path = "../typst.ts/crates/reflexo/" } # reflexo = { path = "../typst.ts/crates/reflexo/" }
# reflexo-world = { path = "../typst.ts/crates/reflexo-world/" }
# typst-ts-core = { path = "../typst.ts/core" } # typst-ts-core = { path = "../typst.ts/core" }
# typst-ts-compiler = { path = "../typst.ts/compiler" } # typst-ts-compiler = { path = "../typst.ts/compiler" }
# typstyle = { path = "../typstyle" } # typstyle = { path = "../typstyle" }

View file

@ -420,10 +420,7 @@ pub trait AnalysisResources {
fn resolve(&self, spec: &PackageSpec) -> Result<Arc<Path>, PackageError>; fn resolve(&self, spec: &PackageSpec) -> Result<Arc<Path>, PackageError>;
/// Get all the files in the workspace. /// Get all the files in the workspace.
fn iter_dependencies<'a>( fn iter_dependencies(&self, f: &mut dyn FnMut(ImmutPath));
&'a self,
f: &mut dyn FnMut(&'a ImmutPath, FileResult<&std::time::SystemTime>),
);
/// Resolve extra font information. /// Resolve extra font information.
fn font_info(&self, _font: Font) -> Option<Arc<DataSource>> { fn font_info(&self, _font: Font) -> Option<Arc<DataSource>> {

View file

@ -36,11 +36,11 @@ impl SemanticRequest for SymbolRequest {
let mut symbols = vec![]; let mut symbols = vec![];
ctx.resources.iter_dependencies(&mut |path, _| { ctx.resources.iter_dependencies(&mut |path| {
let Ok(source) = ctx.source_by_path(path) else { let Ok(source) = ctx.source_by_path(&path) else {
return; return;
}; };
let uri = path_to_url(path).unwrap(); let uri = path_to_url(&path).unwrap();
let res = get_lexical_hierarchy(source.clone(), LexicalScopeKind::Symbol).and_then( let res = get_lexical_hierarchy(source.clone(), LexicalScopeKind::Symbol).and_then(
|symbols| { |symbols| {
self.pattern.as_ref().map(|pattern| { self.pattern.as_ref().map(|pattern| {

View file

@ -8,17 +8,13 @@ use std::{
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
pub use serde::Serialize; pub use serde::Serialize;
use serde_json::{ser::PrettyFormatter, Serializer, Value}; use serde_json::{ser::PrettyFormatter, Serializer, Value};
use typst::{ use typst::syntax::{
diag::FileResult, ast::{self, AstNode},
syntax::{ FileId as TypstFileId, LinkedNode, Source, SyntaxKind, VirtualPath,
ast::{self, AstNode},
FileId as TypstFileId, LinkedNode, Source, SyntaxKind, VirtualPath,
},
}; };
use typst::{diag::PackageError, foundations::Bytes}; use typst::{diag::PackageError, foundations::Bytes};
use typst_ts_compiler::{ use typst_ts_compiler::{
service::{CompileDriver, Compiler, EntryManager}, service::CompileDriver, EntryManager, EntryReader, ShadowApi, TypstSystemUniverse, WorldDeps,
NotifyApi, ShadowApi,
}; };
use typst_ts_core::{ use typst_ts_core::{
config::compiler::{EntryOpts, EntryState}, config::compiler::{EntryOpts, EntryState},
@ -46,10 +42,7 @@ impl<'a> AnalysisResources for WrapWorld<'a> {
self.0.registry.resolve(spec) self.0.registry.resolve(spec)
} }
fn iter_dependencies<'b>( fn iter_dependencies(&self, f: &mut dyn FnMut(reflexo::ImmutPath)) {
&'b self,
f: &mut dyn FnMut(&'b reflexo::ImmutPath, FileResult<&typst_ts_compiler::Time>),
) {
self.0.iter_dependencies(f) self.0.iter_dependencies(f)
} }
} }
@ -65,7 +58,7 @@ pub fn snapshot_testing(name: &str, f: &impl Fn(&mut AnalysisContext, PathBuf))
#[cfg(windows)] #[cfg(windows)]
let contents = contents.replace("\r\n", "\n"); let contents = contents.replace("\r\n", "\n");
run_with_sources(&contents, |w: &mut TypstSystemWorld, p| { run_with_sources(&contents, |w: &mut TypstSystemUniverse, p| {
let root = w.workspace_root().unwrap(); let root = w.workspace_root().unwrap();
let paths = w let paths = w
.shadow_paths() .shadow_paths()
@ -74,7 +67,8 @@ pub fn snapshot_testing(name: &str, f: &impl Fn(&mut AnalysisContext, PathBuf))
TypstFileId::new(None, VirtualPath::new(p.strip_prefix(&root).unwrap())) TypstFileId::new(None, VirtualPath::new(p.strip_prefix(&root).unwrap()))
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let w = WrapWorld(w); let mut w = w.spawn();
let w = WrapWorld(&mut w);
let mut ctx = AnalysisContext::new( let mut ctx = AnalysisContext::new(
&w, &w,
Analysis { Analysis {
@ -103,13 +97,16 @@ pub fn get_test_properties(s: &str) -> HashMap<&'_ str, &'_ str> {
props props
} }
pub fn run_with_sources<T>(source: &str, f: impl FnOnce(&mut TypstSystemWorld, PathBuf) -> T) -> T { pub fn run_with_sources<T>(
source: &str,
f: impl FnOnce(&mut TypstSystemUniverse, PathBuf) -> T,
) -> T {
let root = if cfg!(windows) { let root = if cfg!(windows) {
PathBuf::from("C:\\") PathBuf::from("C:\\")
} else { } else {
PathBuf::from("/") PathBuf::from("/")
}; };
let mut world = TypstSystemWorld::new(CompileOpts { let mut world = TypstSystemUniverse::new(CompileOpts {
entry: EntryOpts::new_rooted(root.as_path().into(), None), entry: EntryOpts::new_rooted(root.as_path().into(), None),
..Default::default() ..Default::default()
}) })
@ -145,12 +142,12 @@ pub fn run_with_sources<T>(source: &str, f: impl FnOnce(&mut TypstSystemWorld, P
} }
world.mutate_entry(EntryState::new_detached()).unwrap(); world.mutate_entry(EntryState::new_detached()).unwrap();
let mut driver = CompileDriver::new(world); let mut driver = CompileDriver::new(std::marker::PhantomData, world);
let _ = driver.compile(&mut Default::default()); let _ = driver.compile(&mut Default::default());
let pw = last_pw.unwrap(); let pw = last_pw.unwrap();
driver driver
.world_mut() .universe_mut()
.mutate_entry(EntryState::new_rooted( .mutate_entry(EntryState::new_rooted(
root.as_path().into(), root.as_path().into(),
Some(TypstFileId::new( Some(TypstFileId::new(
@ -159,7 +156,7 @@ pub fn run_with_sources<T>(source: &str, f: impl FnOnce(&mut TypstSystemWorld, P
)), )),
)) ))
.unwrap(); .unwrap();
f(driver.world_mut(), pw) f(driver.universe_mut(), pw)
} }
pub fn find_test_range(s: &Source) -> Range<usize> { pub fn find_test_range(s: &Source) -> Range<usize> {

View file

@ -13,10 +13,7 @@ use tinymist_query::analysis::Analysis;
use tinymist_query::ExportKind; use tinymist_query::ExportKind;
use tinymist_render::PeriscopeRenderer; use tinymist_render::PeriscopeRenderer;
use tokio::sync::{mpsc, watch}; use tokio::sync::{mpsc, watch};
use typst_ts_compiler::{ use typst_ts_compiler::vfs::notify::{FileChangeSet, MemoryEvent};
service::CompileDriverImpl,
vfs::notify::{FileChangeSet, MemoryEvent},
};
use typst_ts_core::config::compiler::EntryState; use typst_ts_core::config::compiler::EntryState;
use self::{ use self::{
@ -28,12 +25,10 @@ use self::{
}; };
use crate::{ use crate::{
compiler::CompileServer, compiler::CompileServer,
world::{ImmutDict, LspWorld, LspWorldBuilder}, world::{ImmutDict, LspWorldBuilder},
TypstLanguageServer, TypstLanguageServer,
}; };
type CompileDriverInner = CompileDriverImpl<LspWorld>;
impl CompileServer { impl CompileServer {
pub fn restart_server(&mut self, group: &str) { pub fn restart_server(&mut self, group: &str) {
let server = self.server( let server = self.server(
@ -102,13 +97,12 @@ impl CompileServer {
self.handle.spawn_blocking(move || { self.handle.spawn_blocking(move || {
// Create the world // Create the world
let font_resolver = font_resolver.wait().clone(); let font_resolver = font_resolver.wait().clone();
let world = LspWorldBuilder::build(entry_.clone(), font_resolver, inputs) let verse = LspWorldBuilder::build(entry_.clone(), font_resolver, inputs)
.expect("incorrect options"); .expect("incorrect options");
// Create the compiler // Create the compiler
let driver = CompileDriverInner::new(world);
let driver = CompileDriver { let driver = CompileDriver {
inner: driver, inner: std::marker::PhantomData,
handler, handler,
analysis: Analysis { analysis: Analysis {
position_encoding, position_encoding,
@ -121,7 +115,7 @@ impl CompileServer {
// Create the actor // Create the actor
tokio::spawn( tokio::spawn(
CompileServerActor::new(driver, entry_, intr_tx, intr_rx) CompileServerActor::new(driver, verse, entry_, intr_tx, intr_rx)
.with_watch(true) .with_watch(true)
.spawn(), .spawn(),
); );

View file

@ -42,16 +42,16 @@ use tinymist_query::{
use tinymist_render::PeriscopeRenderer; use tinymist_render::PeriscopeRenderer;
use tokio::sync::{mpsc, oneshot, watch}; use tokio::sync::{mpsc, oneshot, watch};
use typst::{ use typst::{
diag::{FileResult, PackageError, SourceDiagnostic, SourceResult}, diag::{PackageError, SourceDiagnostic, SourceResult},
layout::Position, layout::Position,
model::Document as TypstDocument, model::Document as TypstDocument,
syntax::package::PackageSpec, syntax::package::PackageSpec,
World as TypstWorld, World as TypstWorld,
}; };
use typst_ts_compiler::{ use typst_ts_compiler::{
service::{CompileDriverImpl, CompileEnv, CompileMiddleware, Compiler, EntryManager, EnvWorld}, service::{CompileEnv, CompileMiddleware, Compiler, PureCompiler},
vfs::notify::MemoryEvent, vfs::notify::MemoryEvent,
Time, EntryManager, EntryReader,
}; };
use typst_ts_core::{ use typst_ts_core::{
config::compiler::EntryState, debug_loc::DataSource, error::prelude::*, typst::prelude::EcoVec, config::compiler::EntryState, debug_loc::DataSource, error::prelude::*, typst::prelude::EcoVec,
@ -61,18 +61,18 @@ use typst_ts_core::{
use super::{ use super::{
editor::{EditorRequest, TinymistCompileStatusEnum}, editor::{EditorRequest, TinymistCompileStatusEnum},
export::ExportConfig, export::ExportConfig,
typ_server::{is_inactive, CompileServerActor, Interrupt}, typ_server::{CompileServerActor, Interrupt},
}; };
use crate::{ use crate::{
actor::export::ExportRequest, actor::export::ExportRequest,
compiler_init::CompileConfig, compiler_init::CompileConfig,
tools::preview::{CompilationHandle, CompileStatus}, tools::preview::{CompilationHandle, CompileStatus},
utils, utils,
world::LspWorld, world::{LspCompilerFeat, LspWorld},
}; };
type CompileDriverInner = CompileDriverImpl<LspWorld>; type CompileService<C> = CompileServerActor<C, LspCompilerFeat>;
type CompileService = CompileServerActor<CompileDriver>; pub type CompileClientActor = CompileClientActorImpl<CompileDriver>;
type EditorSender = mpsc::UnboundedSender<EditorRequest>; type EditorSender = mpsc::UnboundedSender<EditorRequest>;
@ -131,7 +131,7 @@ impl CompileHandler {
} }
pub struct CompileDriver { pub struct CompileDriver {
pub(super) inner: CompileDriverInner, pub(super) inner: PureCompiler<LspWorld>,
#[allow(unused)] #[allow(unused)]
pub(super) handler: CompileHandler, pub(super) handler: CompileHandler,
pub(super) analysis: Analysis, pub(super) analysis: Analysis,
@ -139,7 +139,7 @@ pub struct CompileDriver {
} }
impl CompileMiddleware for CompileDriver { impl CompileMiddleware for CompileDriver {
type Compiler = CompileDriverInner; type Compiler = PureCompiler<LspWorld>;
fn inner(&self) -> &Self::Compiler { fn inner(&self) -> &Self::Compiler {
&self.inner &self.inner
@ -149,7 +149,11 @@ impl CompileMiddleware for CompileDriver {
&mut self.inner &mut self.inner
} }
fn wrap_compile(&mut self, env: &mut CompileEnv) -> SourceResult<Arc<typst::model::Document>> { fn wrap_compile(
&mut self,
world: &LspWorld,
env: &mut CompileEnv,
) -> SourceResult<Arc<typst::model::Document>> {
self.handler self.handler
.editor_tx .editor_tx
.send(EditorRequest::Status( .send(EditorRequest::Status(
@ -158,10 +162,14 @@ impl CompileMiddleware for CompileDriver {
)) ))
.unwrap(); .unwrap();
self.handler.status(CompileStatus::Compiling); self.handler.status(CompileStatus::Compiling);
match self.inner_mut().compile(env) { match self
.ensure_main(world)
.and_then(|_| self.inner_mut().compile(world, env))
{
Ok(doc) => { Ok(doc) => {
self.handler.notify_compile(Ok(doc.clone())); self.handler.notify_compile(Ok(doc.clone()));
self.notify_diagnostics( self.notify_diagnostics(
world,
EcoVec::new(), EcoVec::new(),
env.tracer.as_ref().map(|e| e.clone().warnings()), env.tracer.as_ref().map(|e| e.clone().warnings()),
); );
@ -170,7 +178,11 @@ impl CompileMiddleware for CompileDriver {
Err(err) => { Err(err) => {
self.handler self.handler
.notify_compile(Err(CompileStatus::CompileError)); .notify_compile(Err(CompileStatus::CompileError));
self.notify_diagnostics(err, env.tracer.as_ref().map(|e| e.clone().warnings())); self.notify_diagnostics(
world,
err,
env.tracer.as_ref().map(|e| e.clone().warnings()),
);
Err(EcoVec::new()) Err(EcoVec::new())
} }
} }
@ -180,20 +192,22 @@ impl CompileMiddleware for CompileDriver {
impl CompileDriver { impl CompileDriver {
fn notify_diagnostics( fn notify_diagnostics(
&mut self, &mut self,
world: &LspWorld,
errors: EcoVec<SourceDiagnostic>, errors: EcoVec<SourceDiagnostic>,
warnings: Option<EcoVec<SourceDiagnostic>>, warnings: Option<EcoVec<SourceDiagnostic>>,
) { ) {
trace!("notify diagnostics: {errors:#?} {warnings:#?}"); trace!("notify diagnostics: {errors:#?} {warnings:#?}");
let diagnostics = self.run_analysis(|ctx| { let diagnostics = self.run_analysis(world, |ctx| {
tinymist_query::convert_diagnostics(ctx, errors.iter().chain(warnings.iter().flatten())) tinymist_query::convert_diagnostics(ctx, errors.iter().chain(warnings.iter().flatten()))
}); });
match diagnostics { match diagnostics {
Ok(diagnostics) => { Ok(diagnostics) => {
let entry = world.entry_state();
// todo: better way to remove diagnostics // todo: better way to remove diagnostics
// todo: check all errors in this file // todo: check all errors in this file
let detached = is_inactive(&self.inner.world().entry); let detached = entry.is_inactive();
let valid = !detached; let valid = !detached;
self.handler.push_diagnostics(valid.then_some(diagnostics)); self.handler.push_diagnostics(valid.then_some(diagnostics));
} }
@ -206,15 +220,14 @@ impl CompileDriver {
pub fn run_analysis<T>( pub fn run_analysis<T>(
&mut self, &mut self,
w: &LspWorld,
f: impl FnOnce(&mut AnalysisContext<'_>) -> T, f: impl FnOnce(&mut AnalysisContext<'_>) -> T,
) -> anyhow::Result<T> { ) -> anyhow::Result<T> {
let w = self.inner.world_mut();
let Some(main) = w.main_id() else { let Some(main) = w.main_id() else {
error!("TypstActor: main file is not set"); error!("TypstActor: main file is not set");
bail!("main file is not set"); bail!("main file is not set");
}; };
let Some(root) = w.entry.root() else { let Some(root) = w.entry_state().root() else {
error!("TypstActor: root is not set"); error!("TypstActor: root is not set");
bail!("root is not set"); bail!("root is not set");
}; };
@ -222,12 +235,8 @@ impl CompileDriver {
info!("TypstActor: failed to prepare main file: {err:?}"); info!("TypstActor: failed to prepare main file: {err:?}");
anyhow!("failed to get source: {err}") anyhow!("failed to get source: {err}")
})?; })?;
w.prepare_env(&mut Default::default()).map_err(|err| {
error!("TypstActor: failed to prepare env: {err:?}");
anyhow!("failed to prepare env")
})?;
struct WrapWorld<'a>(&'a mut LspWorld, &'a PeriscopeRenderer); struct WrapWorld<'a>(&'a LspWorld, &'a PeriscopeRenderer);
impl<'a> AnalysisResources for WrapWorld<'a> { impl<'a> AnalysisResources for WrapWorld<'a> {
fn world(&self) -> &dyn typst::World { fn world(&self) -> &dyn typst::World {
@ -239,17 +248,14 @@ impl CompileDriver {
self.0.registry.resolve(spec) self.0.registry.resolve(spec)
} }
fn iter_dependencies<'b>( fn iter_dependencies(&self, f: &mut dyn FnMut(ImmutPath)) {
&'b self, use typst_ts_compiler::WorldDeps;
f: &mut dyn FnMut(&'b ImmutPath, FileResult<&Time>),
) {
use typst_ts_compiler::NotifyApi;
self.0.iter_dependencies(f) self.0.iter_dependencies(f)
} }
/// Resolve extra font information. /// Resolve extra font information.
fn font_info(&self, font: TypstFont) -> Option<Arc<DataSource>> { fn font_info(&self, font: TypstFont) -> Option<Arc<DataSource>> {
self.0.font_resolver.inner.describe_font(&font) self.0.font_resolver.describe_font(&font)
} }
/// Resolve periscope image at the given position. /// Resolve periscope image at the given position.
@ -270,20 +276,20 @@ impl CompileDriver {
} }
} }
pub struct CompileClientActor { pub struct CompileClientActorImpl<C: Compiler> {
pub diag_group: String, pub diag_group: String,
pub config: CompileConfig, pub config: CompileConfig,
entry: EntryState, entry: EntryState,
intr_tx: mpsc::UnboundedSender<Interrupt<CompileService>>, intr_tx: mpsc::UnboundedSender<Interrupt<CompileService<C>>>,
export_tx: mpsc::UnboundedSender<ExportRequest>, export_tx: mpsc::UnboundedSender<ExportRequest>,
} }
impl CompileClientActor { impl<C: Compiler<W = LspWorld> + Send> CompileClientActorImpl<C> {
pub(crate) fn new( pub(crate) fn new(
diag_group: String, diag_group: String,
config: CompileConfig, config: CompileConfig,
entry: EntryState, entry: EntryState,
intr_tx: mpsc::UnboundedSender<Interrupt<CompileService>>, intr_tx: mpsc::UnboundedSender<Interrupt<CompileService<C>>>,
export_tx: mpsc::UnboundedSender<ExportRequest>, export_tx: mpsc::UnboundedSender<ExportRequest>,
) -> Self { ) -> Self {
Self { Self {
@ -297,11 +303,11 @@ impl CompileClientActor {
fn steal_inner<Ret: Send + 'static>( fn steal_inner<Ret: Send + 'static>(
&self, &self,
f: impl FnOnce(&mut CompileService) -> Ret + Send + 'static, f: impl FnOnce(&mut CompileService<C>) -> Ret + Send + 'static,
) -> ZResult<oneshot::Receiver<Ret>> { ) -> ZResult<oneshot::Receiver<Ret>> {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
let task = Box::new(move |this: &mut CompileService| { let task = Box::new(move |this: &mut CompileService<C>| {
if tx.send(f(this)).is_err() { if tx.send(f(this)).is_err() {
// Receiver was dropped. The main thread may have exited, or the request may // Receiver was dropped. The main thread may have exited, or the request may
// have been cancelled. // have been cancelled.
@ -319,7 +325,7 @@ impl CompileClientActor {
/// Steal the compiler thread and run the given function. /// Steal the compiler thread and run the given function.
pub fn steal<Ret: Send + 'static>( pub fn steal<Ret: Send + 'static>(
&self, &self,
f: impl FnOnce(&mut CompileService) -> Ret + Send + 'static, f: impl FnOnce(&mut CompileService<C>) -> Ret + Send + 'static,
) -> ZResult<Ret> { ) -> ZResult<Ret> {
utils::threaded_receive(self.steal_inner(f)?) utils::threaded_receive(self.steal_inner(f)?)
} }
@ -327,31 +333,46 @@ impl CompileClientActor {
/// Steal the compiler thread and run the given function. /// Steal the compiler thread and run the given function.
pub async fn steal_async<Ret: Send + 'static>( pub async fn steal_async<Ret: Send + 'static>(
&self, &self,
f: impl FnOnce(&mut CompileService) -> Ret + Send + 'static, f: impl FnOnce(&mut CompileService<C>) -> Ret + Send + 'static,
) -> ZResult<Ret> { ) -> ZResult<Ret> {
self.steal_inner(f)? self.steal_inner(f)?
.await .await
.map_err(map_string_err("failed to call steal_async")) .map_err(map_string_err("failed to call steal_async"))
} }
pub fn steal_state<T: Send + Sync + 'static>( pub fn sync_config(&mut self, config: CompileConfig) {
&self, self.config = config;
f: impl FnOnce(&mut AnalysisContext, Option<VersionedDocument>) -> T + Send + Sync + 'static,
) -> anyhow::Result<T> {
self.steal(move |compiler| {
let doc = compiler.success_doc();
let c = &mut compiler.compiler.compiler;
c.run_analysis(move |ctx| f(ctx, doc))
})?
} }
pub fn steal_world<T: Send + Sync + 'static>( pub fn add_memory_changes(&self, event: MemoryEvent) {
&self, let _ = self.intr_tx.send(Interrupt::Memory(event));
f: impl FnOnce(&mut AnalysisContext) -> T + Send + Sync + 'static,
) -> anyhow::Result<T> {
self.steal(move |compiler| compiler.compiler.compiler.run_analysis(f))?
} }
pub(crate) fn change_export_pdf(&mut self, config: ExportConfig) {
let _ = self.export_tx.send(ExportRequest::ChangeConfig(config));
}
pub fn on_export(&self, kind: ExportKind, path: PathBuf) -> anyhow::Result<Option<PathBuf>> {
// todo: we currently doesn't respect the path argument...
info!("CompileActor: on export: {}", path.display());
let (tx, rx) = oneshot::channel();
let _ = self.export_tx.send(ExportRequest::Oneshot(Some(kind), tx));
let res: Option<PathBuf> = utils::threaded_receive(rx)?;
info!("CompileActor: on export end: {path:?} as {res:?}");
Ok(res)
}
pub fn on_save_export(&self, path: PathBuf) -> anyhow::Result<()> {
info!("CompileActor: on save export: {}", path.display());
let _ = self.export_tx.send(ExportRequest::OnSaved);
Ok(())
}
}
impl CompileClientActorImpl<CompileDriver> {
pub fn settle(&mut self) { pub fn settle(&mut self) {
let _ = self.change_entry(None); let _ = self.change_entry(None);
info!("TypstActor({}): settle requested", self.diag_group); info!("TypstActor({}): settle requested", self.diag_group);
@ -363,10 +384,6 @@ impl CompileClientActor {
} }
} }
pub fn sync_config(&mut self, config: CompileConfig) {
self.config = config;
}
pub fn change_entry(&mut self, path: Option<ImmutPath>) -> Result<bool, Error> { pub fn change_entry(&mut self, path: Option<ImmutPath>) -> Result<bool, Error> {
if path if path
.as_deref() .as_deref()
@ -388,8 +405,8 @@ impl CompileClientActor {
self.steal(move |compiler| { self.steal(move |compiler| {
compiler.change_entry(next.clone()); compiler.change_entry(next.clone());
let next_is_inactive = is_inactive(&next); let next_is_inactive = next.is_inactive();
let res = compiler.compiler.world_mut().mutate_entry(next); let res = compiler.verse.mutate_entry(next);
if next_is_inactive { if next_is_inactive {
info!("TypstActor: removing diag"); info!("TypstActor: removing diag");
@ -408,12 +425,26 @@ impl CompileClientActor {
Ok(true) Ok(true)
} }
pub fn add_memory_changes(&self, event: MemoryEvent) { pub fn steal_state<T: Send + Sync + 'static>(
let _ = self.intr_tx.send(Interrupt::Memory(event)); &self,
f: impl FnOnce(&mut AnalysisContext, Option<VersionedDocument>) -> T + Send + Sync + 'static,
) -> anyhow::Result<T> {
self.steal(move |compiler| {
let doc = compiler.success_doc();
let w = compiler.verse.spawn();
let c = &mut compiler.compiler.compiler;
c.run_analysis(&w, move |ctx| f(ctx, doc))
})?
} }
pub(crate) fn change_export_pdf(&mut self, config: ExportConfig) { pub fn steal_world<T: Send + Sync + 'static>(
let _ = self.export_tx.send(ExportRequest::ChangeConfig(config)); &self,
f: impl FnOnce(&mut AnalysisContext) -> T + Send + Sync + 'static,
) -> anyhow::Result<T> {
self.steal(move |compiler| {
let w = compiler.verse.spawn();
compiler.compiler.compiler.run_analysis(&w, f)
})?
} }
pub fn clear_cache(&self) { pub fn clear_cache(&self) {
@ -426,13 +457,15 @@ impl CompileClientActor {
let dg = self.diag_group.clone(); let dg = self.diag_group.clone();
self.steal(move |c| { self.steal(move |c| {
let cc = &c.compiler.compiler; let cc = &c.compiler.compiler;
let w = c.verse.spawn();
let info = ServerInfoResponse { let info = ServerInfoResponse {
root: cc.world().entry.root().map(|e| e.as_ref().to_owned()), root: w.entry_state().root().map(|e| e.as_ref().to_owned()),
font_paths: cc.world().font_resolver.font_paths().to_owned(), font_paths: w.font_resolver.font_paths().to_owned(),
inputs: cc.world().inputs.as_ref().deref().clone(), inputs: c.verse.inputs().as_ref().deref().clone(),
estimated_memory_usage: HashMap::from_iter([ estimated_memory_usage: HashMap::from_iter([
("vfs".to_owned(), cc.world().vfs.memory_usage()), // todo: vfs memory usage
// ("vfs".to_owned(), w.vfs.read().memory_usage()),
("analysis".to_owned(), cc.analysis.estimated_memory()), ("analysis".to_owned(), cc.analysis.estimated_memory()),
]), ]),
}; };
@ -441,23 +474,4 @@ impl CompileClientActor {
}) })
.map_err(|e| e.into()) .map_err(|e| e.into())
} }
pub fn on_export(&self, kind: ExportKind, path: PathBuf) -> anyhow::Result<Option<PathBuf>> {
// todo: we currently doesn't respect the path argument...
info!("CompileActor: on export: {}", path.display());
let (tx, rx) = oneshot::channel();
let _ = self.export_tx.send(ExportRequest::Oneshot(Some(kind), tx));
let res: Option<PathBuf> = utils::threaded_receive(rx)?;
info!("CompileActor: on export end: {path:?} as {res:?}");
Ok(res)
}
pub fn on_save_export(&self, path: PathBuf) -> anyhow::Result<()> {
info!("CompileActor: on save export: {}", path.display());
let _ = self.export_tx.send(ExportRequest::OnSaved);
Ok(())
}
} }

View file

@ -7,20 +7,28 @@ use std::{collections::HashSet, path::Path, sync::Arc, thread::JoinHandle};
use tinymist_query::VersionedDocument; use tinymist_query::VersionedDocument;
use tokio::sync::{mpsc, oneshot}; use tokio::sync::{mpsc, oneshot};
use typst_ts_compiler::service::{ use typst_ts_compiler::{
features::{FeatureSet, WITH_COMPILING_STATUS_FEATURE}, service::{
watch_deps, CompileEnv, CompileReporter, Compiler, ConsoleDiagReporter, features::{FeatureSet, WITH_COMPILING_STATUS_FEATURE},
watch_deps, CompileEnv, CompileReporter, Compiler, ConsoleDiagReporter,
},
vfs::notify::{FilesystemEvent, MemoryEvent, NotifyMessage},
world::{CompilerFeat, CompilerUniverse, CompilerWorld},
Revising, WorldDeps,
}; };
use typst_ts_compiler::vfs::notify::{FilesystemEvent, MemoryEvent, NotifyMessage}; use typst_ts_core::{config::compiler::EntryState, TypstDocument};
use typst_ts_compiler::ShadowApi;
use typst_ts_core::{config::compiler::EntryState, TypstDocument, TypstFileId}; /// A task that can be sent to the context (compiler thread)
///
/// The internal function will be dereferenced and called on the context.
type BorrowTask<Ctx> = Box<dyn FnOnce(&mut Ctx) + Send + 'static>;
pub enum Interrupt<Ctx> { pub enum Interrupt<Ctx> {
/// Compile anyway. /// Compile anyway.
Compile, Compile,
/// Borrow the compiler thread and run the task. /// Borrow the compiler thread and run the task.
/// ///
/// See [`CompileClient<Ctx>::steal`] for more information. /// See [`CompileClient<Ctx>::steal_async`] for more information.
Task(BorrowTask<Ctx>), Task(BorrowTask<Ctx>),
/// Memory file changes. /// Memory file changes.
Memory(MemoryEvent), Memory(MemoryEvent),
@ -30,11 +38,6 @@ pub enum Interrupt<Ctx> {
Settle(oneshot::Sender<()>), Settle(oneshot::Sender<()>),
} }
/// A task that can be sent to the context (compiler/render thread)
///
/// The internal function will be dereferenced and called on the context.
pub type BorrowTask<Ctx> = Box<dyn FnOnce(&mut Ctx) + Send + 'static>;
/// Responses from the compiler thread. /// Responses from the compiler thread.
enum CompilerResponse { enum CompilerResponse {
/// Response to the file watcher /// Response to the file watcher
@ -55,9 +58,11 @@ struct SuspendState {
} }
/// The compiler thread. /// The compiler thread.
pub struct CompileServerActor<C: Compiler> { pub struct CompileServerActor<C: Compiler, F: CompilerFeat> {
/// The underlying universe.
pub verse: CompilerUniverse<F>,
/// The underlying compiler. /// The underlying compiler.
pub compiler: CompileReporter<C>, pub compiler: CompileReporter<C, CompilerWorld<F>>,
/// Whether to enable file system watching. /// Whether to enable file system watching.
pub enable_watch: bool, pub enable_watch: bool,
@ -85,12 +90,12 @@ pub struct CompileServerActor<C: Compiler> {
suspend_state: SuspendState, suspend_state: SuspendState,
} }
impl<C: Compiler + ShadowApi + Send + 'static> CompileServerActor<C> impl<F: CompilerFeat + Send + 'static, C: Compiler<W = CompilerWorld<F>> + Send + 'static>
where CompileServerActor<C, F>
C::World: for<'files> codespan_reporting::files::Files<'files, FileId = TypstFileId>,
{ {
pub fn new_with_features( pub fn new_with_features(
compiler: C, compiler: C,
verse: CompilerUniverse<F>,
entry: EntryState, entry: EntryState,
feature_set: FeatureSet, feature_set: FeatureSet,
intr_tx: mpsc::UnboundedSender<Interrupt<Self>>, intr_tx: mpsc::UnboundedSender<Interrupt<Self>>,
@ -99,6 +104,7 @@ where
Self { Self {
compiler: CompileReporter::new(compiler) compiler: CompileReporter::new(compiler)
.with_generic_reporter(ConsoleDiagReporter::default()), .with_generic_reporter(ConsoleDiagReporter::default()),
verse,
logical_tick: 1, logical_tick: 1,
enable_watch: false, enable_watch: false,
@ -116,7 +122,7 @@ where
intr_rx, intr_rx,
suspend_state: SuspendState { suspend_state: SuspendState {
suspended: is_inactive(&entry), suspended: entry.is_inactive(),
dirty: false, dirty: false,
}, },
} }
@ -125,18 +131,29 @@ where
/// Create a new compiler thread. /// Create a new compiler thread.
pub fn new( pub fn new(
compiler: C, compiler: C,
world: CompilerUniverse<F>,
entry: EntryState, entry: EntryState,
intr_tx: mpsc::UnboundedSender<Interrupt<Self>>, intr_tx: mpsc::UnboundedSender<Interrupt<Self>>,
intr_rx: mpsc::UnboundedReceiver<Interrupt<Self>>, intr_rx: mpsc::UnboundedReceiver<Interrupt<Self>>,
) -> Self { ) -> Self {
Self::new_with_features(compiler, entry, FeatureSet::default(), intr_tx, intr_rx) Self::new_with_features(
compiler,
world,
entry,
FeatureSet::default(),
intr_tx,
intr_rx,
)
} }
pub fn with_watch(mut self, enable_watch: bool) -> Self { pub fn with_watch(mut self, enable_watch: bool) -> Self {
self.enable_watch = enable_watch; self.enable_watch = enable_watch;
self self
} }
pub fn intr_tx(&self) -> mpsc::UnboundedSender<Interrupt<Self>> {
self.intr_tx.clone()
}
pub fn success_doc(&self) -> Option<VersionedDocument> { pub fn success_doc(&self) -> Option<VersionedDocument> {
self.latest_success_doc self.latest_success_doc
.clone() .clone()
@ -174,7 +191,8 @@ where
async fn block_run_inner(mut self) -> bool { async fn block_run_inner(mut self) -> bool {
if !self.enable_watch { if !self.enable_watch {
let mut env = self.make_env(self.once_feature_set.clone()); let mut env = self.make_env(self.once_feature_set.clone());
let compiled = self.compiler.compile(&mut env); let w = self.verse.spawn();
let compiled = self.compiler.compile(&w, &mut env);
return compiled.is_ok(); return compiled.is_ok();
} }
@ -191,7 +209,8 @@ where
pub async fn spawn(mut self) -> Option<JoinHandle<()>> { pub async fn spawn(mut self) -> Option<JoinHandle<()>> {
if !self.enable_watch { if !self.enable_watch {
let mut env = self.make_env(self.once_feature_set.clone()); let mut env = self.make_env(self.once_feature_set.clone());
self.compiler.compile(&mut env).ok(); let w = self.verse.spawn();
self.compiler.compile(&w, &mut env).ok();
return None; return None;
} }
@ -268,8 +287,8 @@ where
Some(compile_thread.unwrap()) Some(compile_thread.unwrap())
} }
pub(crate) fn change_entry(&mut self, entry: EntryState) { pub fn change_entry(&mut self, entry: EntryState) {
self.suspend_state.suspended = is_inactive(&entry); self.suspend_state.suspended = entry.is_inactive();
if !self.suspend_state.suspended && self.suspend_state.dirty { if !self.suspend_state.suspended && self.suspend_state.dirty {
self.intr_tx.send(Interrupt::Compile).ok(); self.intr_tx.send(Interrupt::Compile).ok();
} }
@ -288,9 +307,11 @@ where
return; return;
} }
let w = self.verse.spawn();
// Compile the document. // Compile the document.
let mut env = self.make_env(self.watch_feature_set.clone()); let mut env = self.make_env(self.watch_feature_set.clone());
self.latest_doc = self.compiler.compile(&mut env).ok(); self.latest_doc = self.compiler.compile(&w, &mut env).ok();
if self.latest_doc.is_some() { if self.latest_doc.is_some() {
self.latest_success_doc.clone_from(&self.latest_doc); self.latest_success_doc.clone_from(&self.latest_doc);
} }
@ -303,8 +324,7 @@ where
// Notify the new file dependencies. // Notify the new file dependencies.
let mut deps = vec![]; let mut deps = vec![];
self.compiler w.iter_dependencies(&mut |dep| deps.push(dep.clone()));
.iter_dependencies(&mut |dep, _| deps.push(dep.clone()));
send(Notify(NotifyMessage::SyncDependency(deps))); send(Notify(NotifyMessage::SyncDependency(deps)));
} }
@ -340,7 +360,8 @@ where
// If there is no invalidation happening, apply memory changes directly. // If there is no invalidation happening, apply memory changes directly.
if files.is_empty() && self.dirty_shadow_logical_tick == 0 { if files.is_empty() && self.dirty_shadow_logical_tick == 0 {
self.apply_memory_changes(event); self.verse
.increment_revision(|verse| Self::apply_memory_changes(verse, event));
return true; return true;
} }
@ -362,13 +383,15 @@ where
Interrupt::Fs(mut event) => { Interrupt::Fs(mut event) => {
log::debug!("CompileServerActor: fs event incoming {event:?}"); log::debug!("CompileServerActor: fs event incoming {event:?}");
// Handle delayed upstream update event before applying file system changes
if self.apply_delayed_memory_changes(&mut event).is_none() {
log::warn!("CompileServerActor: unknown upstream update event");
}
// Apply file system changes. // Apply file system changes.
self.compiler.notify_fs_event(event); let dirty_tick = &mut self.dirty_shadow_logical_tick;
self.verse.increment_revision(|verse| {
// Handle delayed upstream update event before applying file system changes
if Self::apply_delayed_memory_changes(verse, dirty_tick, &mut event).is_none() {
log::warn!("CompileServerActor: unknown upstream update event");
}
verse.notify_fs_event(event)
});
true true
} }
@ -377,7 +400,11 @@ where
} }
/// Apply delayed memory changes to underlying compiler. /// Apply delayed memory changes to underlying compiler.
fn apply_delayed_memory_changes(&mut self, event: &mut FilesystemEvent) -> Option<()> { fn apply_delayed_memory_changes(
verse: &mut Revising<CompilerUniverse<F>>,
dirty_shadow_logical_tick: &mut usize,
event: &mut FilesystemEvent,
) -> Option<()> {
// Handle delayed upstream update event before applying file system changes // Handle delayed upstream update event before applying file system changes
if let FilesystemEvent::UpstreamUpdate { upstream_event, .. } = event { if let FilesystemEvent::UpstreamUpdate { upstream_event, .. } = event {
let event = upstream_event.take()?.opaque; let event = upstream_event.take()?.opaque;
@ -387,25 +414,25 @@ where
} = *event.downcast().ok()?; } = *event.downcast().ok()?;
// Recovery from dirty shadow state. // Recovery from dirty shadow state.
if logical_tick == self.dirty_shadow_logical_tick { if logical_tick == *dirty_shadow_logical_tick {
self.dirty_shadow_logical_tick = 0; *dirty_shadow_logical_tick = 0;
} }
self.apply_memory_changes(event); Self::apply_memory_changes(verse, event);
} }
Some(()) Some(())
} }
/// Apply memory changes to underlying compiler. /// Apply memory changes to underlying compiler.
fn apply_memory_changes(&mut self, event: MemoryEvent) { fn apply_memory_changes(verse: &mut Revising<CompilerUniverse<F>>, event: MemoryEvent) {
if matches!(event, MemoryEvent::Sync(..)) { if matches!(event, MemoryEvent::Sync(..)) {
self.compiler.reset_shadow(); verse.reset_shadow();
} }
match event { match event {
MemoryEvent::Update(event) | MemoryEvent::Sync(event) => { MemoryEvent::Update(event) | MemoryEvent::Sync(event) => {
for removes in event.removes { for removes in event.removes {
let _ = self.compiler.unmap_shadow(&removes); let _ = verse.unmap_shadow(&removes);
} }
for (p, t) in event.inserts { for (p, t) in event.inserts {
let insert_file = match t.content().cloned() { let insert_file = match t.content().cloned() {
@ -420,18 +447,13 @@ where
} }
}; };
let _ = self.compiler.map_shadow(&p, insert_file); let _ = verse.map_shadow(&p, insert_file);
} }
} }
} }
} }
} }
pub fn is_inactive(entry: &EntryState) -> bool {
use EntryState::*;
matches!(entry, Detached | Workspace { main: None, .. })
}
#[inline] #[inline]
fn log_send_error<T>(chan: &'static str, res: Result<(), mpsc::error::SendError<T>>) -> bool { fn log_send_error<T>(chan: &'static str, res: Result<(), mpsc::error::SendError<T>>) -> bool {
res.map_err(|err| log::warn!("CompileServerActor: send to {chan} error: {err}")) res.map_err(|err| log::warn!("CompileServerActor: send to {chan} error: {err}"))

View file

@ -42,7 +42,9 @@ pub use server::compiler_init;
pub use server::lsp::*; pub use server::lsp::*;
pub use server::lsp_init::*; pub use server::lsp_init::*;
pub use server::preview; pub use server::preview;
pub use world::{CompileFontOpts, CompileOnceOpts, CompileOpts, LspWorld, LspWorldBuilder}; pub use world::{
CompileFontOpts, CompileOnceOpts, CompileOpts, LspUniverse, LspWorld, LspWorldBuilder,
};
use lsp_server::ResponseError; use lsp_server::ResponseError;

View file

@ -10,12 +10,6 @@ use comemo::Prehashed;
use lsp_types::{InitializeParams, InitializedParams}; use lsp_types::{InitializeParams, InitializedParams};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use parking_lot::RwLock; use parking_lot::RwLock;
use tokio::sync::mpsc;
use typst::{eval::Tracer, foundations::IntoValue, syntax::Span};
use typst_ts_compiler::service::{CompileEnv, Compiler, EntryManager};
use typst_ts_core::{typst::prelude::EcoVec, TypstDict};
use crate::args::{CliArguments, Commands, CompileArgs, LspArgs};
use tinymist::{ use tinymist::{
compiler_init::{CompileInit, CompileInitializeParams}, compiler_init::{CompileInit, CompileInitializeParams},
harness::{lsp_harness, InitializedLspDriver, LspDriver, LspHost}, harness::{lsp_harness, InitializedLspDriver, LspDriver, LspHost},
@ -23,6 +17,13 @@ use tinymist::{
transport::with_stdio_transport, transport::with_stdio_transport,
CompileFontOpts, Init, LspWorld, TypstLanguageServer, CompileFontOpts, Init, LspWorld, TypstLanguageServer,
}; };
use tokio::sync::mpsc;
use typst::World;
use typst::{eval::Tracer, foundations::IntoValue, syntax::Span};
use typst_ts_compiler::service::{CompileEnv, Compiler};
use typst_ts_core::{typst::prelude::EcoVec, TypstDict};
use crate::args::{CliArguments, Commands, CompileArgs, LspArgs};
#[cfg(feature = "dhat-heap")] #[cfg(feature = "dhat-heap")]
#[global_allocator] #[global_allocator]
@ -183,8 +184,12 @@ pub fn compiler_main(args: CompileArgs) -> anyhow::Result<()> {
let (timings, _doc, diagnostics) = service let (timings, _doc, diagnostics) = service
.compiler() .compiler()
.steal(|c| { .steal(|c| {
c.compiler.world_mut().mutate_entry(entry).unwrap(); c.verse.increment_revision(|verse| {
c.compiler.world_mut().inputs = inputs; verse.mutate_entry(entry).unwrap();
verse.set_inputs(inputs);
});
let w = c.verse.spawn();
let mut env = CompileEnv { let mut env = CompileEnv {
tracer: Some(Tracer::default()), tracer: Some(Tracer::default()),
@ -192,24 +197,23 @@ pub fn compiler_main(args: CompileArgs) -> anyhow::Result<()> {
}; };
typst_timing::enable(); typst_timing::enable();
let mut errors = EcoVec::new(); let mut errors = EcoVec::new();
let res = match c.compiler.pure_compile(&mut env) { let res = match c.compiler.pure_compile(&w, &mut env) {
Ok(doc) => Some(doc), Ok(doc) => Some(doc),
Err(e) => { Err(e) => {
errors = e; errors = e;
None None
} }
}; };
let world = c.compiler.world();
let mut writer = std::io::BufWriter::new(Vec::new()); let mut writer = std::io::BufWriter::new(Vec::new());
let _ = typst_timing::export_json(&mut writer, |span| { let _ = typst_timing::export_json(&mut writer, |span| {
resolve_span(world, span).unwrap_or_else(|| ("unknown".to_string(), 0)) resolve_span(&w, span).unwrap_or_else(|| ("unknown".to_string(), 0))
}); });
let s = String::from_utf8(writer.into_inner().unwrap()).unwrap(); let s = String::from_utf8(writer.into_inner().unwrap()).unwrap();
let warnings = env.tracer.map(|e| e.warnings()); let warnings = env.tracer.map(|e| e.warnings());
let diagnostics = c.compiler.compiler.run_analysis(|ctx| { let diagnostics = c.compiler.compiler.run_analysis(&w, |ctx| {
tinymist_query::convert_diagnostics( tinymist_query::convert_diagnostics(
ctx, ctx,
warnings.iter().flatten().chain(errors.iter()), warnings.iter().flatten().chain(errors.iter()),
@ -261,7 +265,6 @@ impl<T> Drop for ForceDrop<T> {
/// Turns a span into a (file, line) pair. /// Turns a span into a (file, line) pair.
fn resolve_span(world: &LspWorld, span: Span) -> Option<(String, u32)> { fn resolve_span(world: &LspWorld, span: Span) -> Option<(String, u32)> {
use typst::World;
let id = span.id()?; let id = span.id()?;
let source = world.source(id).ok()?; let source = world.source(id).ok()?;
let range = source.range(span)?; let range = source.range(span)?;

View file

@ -1,6 +1,5 @@
use std::{collections::BTreeMap, path::Path, sync::Arc}; use std::{collections::BTreeMap, path::Path, sync::Arc};
use typst_ts_compiler::{service::EntryManager, ShadowApi};
use typst_ts_core::{config::compiler::EntryState, font::GlyphId, TypstDocument, TypstFont}; use typst_ts_core::{config::compiler::EntryState, font::GlyphId, TypstDocument, TypstFont};
pub use super::prelude::*; pub use super::prelude::*;
@ -188,16 +187,23 @@ impl TypstLanguageServer {
let font = self let font = self
.primary() .primary()
.steal(move |e| { .steal(move |e| {
let verse = &mut e.verse;
let entry_path: Arc<Path> = Path::new("/._sym_.typ").into(); let entry_path: Arc<Path> = Path::new("/._sym_.typ").into();
let new_entry = EntryState::new_rootless(entry_path.clone())?; let new_entry = EntryState::new_rootless(entry_path.clone())?;
let old_entry = e.compiler.world_mut().mutate_entry(new_entry).ok()?; let (old_entry, prepared) = verse.increment_revision(|verse| {
let prepared = e let old_entry = verse.mutate_entry(new_entry).ok()?;
.compiler let prepared = verse
.map_shadow(&entry_path, math_shaping_text.into_bytes().into()) .map_shadow(&entry_path, math_shaping_text.into_bytes().into())
.is_ok(); .is_ok();
let sym_doc = prepared.then(|| e.compiler.pure_compile(&mut Default::default()));
e.compiler.world_mut().mutate_entry(old_entry).ok()?; Some((old_entry, prepared))
})?;
let w = verse.spawn();
let sym_doc =
prepared.then(|| e.compiler.pure_compile(&w, &mut Default::default()));
verse.increment_revision(|verse| verse.mutate_entry(old_entry).ok())?;
log::debug!( log::debug!(
"sym doc: {doc:?}", "sym doc: {doc:?}",

View file

@ -23,7 +23,6 @@ use tinymist_query::{
use tokio::sync::mpsc; use tokio::sync::mpsc;
use typst::diag::StrResult; use typst::diag::StrResult;
use typst::syntax::package::{PackageSpec, VersionlessPackageSpec}; use typst::syntax::package::{PackageSpec, VersionlessPackageSpec};
use typst_ts_compiler::service::Compiler;
use typst_ts_core::path::PathClean; use typst_ts_core::path::PathClean;
use typst_ts_core::{error::prelude::*, ImmutPath}; use typst_ts_core::{error::prelude::*, ImmutPath};
@ -646,7 +645,7 @@ impl TypstLanguageServer {
let res = self let res = self
.primary() .primary()
.steal(move |c| { .steal(move |c| {
let cc = &c.compiler; let verse = &c.verse;
// todo: rootless file // todo: rootless file
// todo: memory dirty file // todo: memory dirty file
@ -665,8 +664,8 @@ impl TypstLanguageServer {
compiler_program: self_path, compiler_program: self_path,
root: root.as_ref().to_owned(), root: root.as_ref().to_owned(),
main, main,
inputs: cc.world().inputs.as_ref().deref().clone(), inputs: verse.inputs().as_ref().deref().clone(),
font_paths: cc.world().font_resolver.font_paths().to_owned(), font_paths: verse.font_resolver.font_paths().to_owned(),
}, },
)) ))
.context("cannot send trace request")?; .context("cannot send trace request")?;
@ -770,6 +769,7 @@ impl TypstLanguageServer {
let res = self let res = self
.primary() .primary()
.steal(move |c| { .steal(move |c| {
let world = c.verse.spawn();
// Parse the package specification. If the user didn't specify the version, // Parse the package specification. If the user didn't specify the version,
// we try to figure it out automatically by downloading the package index // we try to figure it out automatically by downloading the package index
// or searching the disk. // or searching the disk.
@ -779,7 +779,7 @@ impl TypstLanguageServer {
// Try to parse without version, but prefer the error message of the // Try to parse without version, but prefer the error message of the
// normal package spec parsing if it fails. // normal package spec parsing if it fails.
let spec: VersionlessPackageSpec = from_source.parse().map_err(|_| err)?; let spec: VersionlessPackageSpec = from_source.parse().map_err(|_| err)?;
let version = determine_latest_version(c.compiler.world(), &spec)?; let version = determine_latest_version(&c.verse, &spec)?;
StrResult::Ok(spec.at(version)) StrResult::Ok(spec.at(version))
}) })
.map_err(map_string_err("failed to parse package spec"))?; .map_err(map_string_err("failed to parse package spec"))?;
@ -787,7 +787,7 @@ impl TypstLanguageServer {
let from_source = TemplateSource::Package(spec); let from_source = TemplateSource::Package(spec);
let entry_path = package::init( let entry_path = package::init(
c.compiler.world(), &world,
InitTask { InitTask {
tmpl: from_source.clone(), tmpl: from_source.clone(),
dir: to_path.clone(), dir: to_path.clone(),
@ -827,14 +827,14 @@ impl TypstLanguageServer {
// Try to parse without version, but prefer the error message of the // Try to parse without version, but prefer the error message of the
// normal package spec parsing if it fails. // normal package spec parsing if it fails.
let spec: VersionlessPackageSpec = from_source.parse().map_err(|_| err)?; let spec: VersionlessPackageSpec = from_source.parse().map_err(|_| err)?;
let version = determine_latest_version(c.compiler.world(), &spec)?; let version = determine_latest_version(&c.verse, &spec)?;
StrResult::Ok(spec.at(version)) StrResult::Ok(spec.at(version))
}) })
.map_err(map_string_err("failed to parse package spec"))?; .map_err(map_string_err("failed to parse package spec"))?;
let from_source = TemplateSource::Package(spec); let from_source = TemplateSource::Package(spec);
let entry = package::get_entry(c.compiler.world(), from_source) let entry = package::get_entry(&c.verse, from_source)
.map_err(map_string_err("failed to get template entry"))?; .map_err(map_string_err("failed to get template entry"))?;
ZResult::Ok(entry) ZResult::Ok(entry)

View file

@ -5,8 +5,7 @@ use await_tree::InstrumentAwait;
use log::{error, info}; use log::{error, info};
use typst::foundations::{Str, Value}; use typst::foundations::{Str, Value};
use typst_ts_compiler::service::CompileDriver; use typst_ts_compiler::{service::CompileDriver, TypstSystemUniverse};
use typst_ts_compiler::TypstSystemWorld;
use typst_ts_core::config::{compiler::EntryOpts, CompileOpts}; use typst_ts_core::config::{compiler::EntryOpts, CompileOpts};
use hyper::{ use hyper::{
@ -139,7 +138,7 @@ pub async fn preview_main(args: PreviewCliArgs) -> anyhow::Result<()> {
} }
let compiler_driver = { let compiler_driver = {
let world = TypstSystemWorld::new(CompileOpts { let world = TypstSystemUniverse::new(CompileOpts {
entry: EntryOpts::new_rooted(root.clone(), Some(entry.clone())), entry: EntryOpts::new_rooted(root.clone(), Some(entry.clone())),
inputs, inputs,
no_system_fonts: args.compile.font.ignore_system_fonts, no_system_fonts: args.compile.font.ignore_system_fonts,
@ -149,7 +148,7 @@ pub async fn preview_main(args: PreviewCliArgs) -> anyhow::Result<()> {
}) })
.expect("incorrect options"); .expect("incorrect options");
CompileDriver::new(world).with_entry_file(entry) CompileDriver::new(std::marker::PhantomData, world.with_entry_file(entry))
}; };
tokio::spawn(async move { tokio::spawn(async move {

View file

@ -1,32 +1,31 @@
use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use await_tree::InstrumentAwait; use await_tree::InstrumentAwait;
use log::error;
use tokio::sync::mpsc;
use typst::diag::SourceResult; use typst::diag::SourceResult;
use typst::layout::Position;
use typst::model::Document; use typst::model::Document;
use typst::syntax::Span;
use typst_ts_compiler::service::{ use typst::World;
CompileActor, CompileClient as TsCompileClient, CompileExporter, Compiler, WorldExporter,
};
use typst_ts_compiler::service::{CompileDriver, CompileMiddleware}; use typst_ts_compiler::service::{CompileDriver, CompileMiddleware};
use typst_ts_compiler::vfs::notify::{FileChangeSet, MemoryEvent}; use typst_ts_compiler::service::{CompileExporter, Compiler, PureCompiler, WorldExporter};
use typst_ts_core::debug_loc::SourceSpanOffset; use typst_ts_compiler::{EntryReader, TypstSystemWorld};
use typst_ts_core::Error; use typst_ts_core::Error;
use typst_preview::{CompilationHandle, CompileStatus}; use typst_preview::{CompilationHandle, CompileStatus};
use typst_preview::{CompileHost, EditorServer, MemoryFiles, MemoryFilesShort, SourceFileServer};
use typst_preview::{DocToSrcJumpInfo, Location};
pub type CompileService<H> = CompileActor<Reporter<CompileExporter<CompileDriver>, H>>; use crate::actor::typ_client::CompileClientActorImpl;
pub type CompileClient<H> = TsCompileClient<CompileService<H>>; use crate::actor::typ_server::CompileServerActor;
use crate::compiler_init::CompileConfig;
use crate::world::{LspCompilerFeat, LspWorld};
pub type CompileService<H> =
CompileServerActor<Reporter<CompileExporter<PureCompiler<LspWorld>>, H>, LspCompilerFeat>;
pub type CompileClient<H> =
CompileClientActorImpl<Reporter<CompileExporter<PureCompiler<LspWorld>>, H>>;
pub struct CompileServer<H: CompilationHandle> { pub struct CompileServer<H: CompilationHandle> {
inner: CompileService<H>, inner: CompileService<H>,
client: TypstClient<H>,
} }
pub struct Reporter<C, H> { pub struct Reporter<C, H> {
@ -47,10 +46,11 @@ impl<C: Compiler, H: CompilationHandle> CompileMiddleware for Reporter<C, H> {
fn wrap_compile( fn wrap_compile(
&mut self, &mut self,
world: &<C as typst_ts_compiler::service::Compiler>::W,
env: &mut typst_ts_compiler::service::CompileEnv, env: &mut typst_ts_compiler::service::CompileEnv,
) -> SourceResult<Arc<Document>> { ) -> SourceResult<Arc<Document>> {
self.cb.status(CompileStatus::Compiling); self.cb.status(CompileStatus::Compiling);
match self.inner_mut().compile(env) { match self.inner_mut().compile(world, env) {
Ok(doc) => { Ok(doc) => {
self.cb.notify_compile(Ok(doc.clone())); self.cb.notify_compile(Ok(doc.clone()));
Ok(doc) Ok(doc)
@ -63,138 +63,45 @@ impl<C: Compiler, H: CompilationHandle> CompileMiddleware for Reporter<C, H> {
} }
} }
impl<C: Compiler + WorldExporter, H> WorldExporter for Reporter<C, H> { impl<W: World, C: Compiler<W = W> + WorldExporter<W>, H> WorldExporter<W> for Reporter<C, H> {
fn export(&mut self, output: Arc<typst::model::Document>) -> SourceResult<()> { fn export(&mut self, world: &W, output: Arc<typst::model::Document>) -> SourceResult<()> {
self.inner.export(output) self.inner.export(world, output)
} }
} }
impl<H: CompilationHandle> CompileServer<H> { impl<H: CompilationHandle> CompileServer<H> {
pub fn new( pub fn new(
compiler_driver: CompileDriver, compiler_driver: CompileDriver<PureCompiler<TypstSystemWorld>>,
cb: H, cb: H,
// renderer_sender: broadcast::Sender<RenderActorRequest>, // renderer_sender: broadcast::Sender<RenderActorRequest>,
// editor_conn_sender: mpsc::UnboundedSender<EditorActorRequest>, // editor_conn_sender: mpsc::UnboundedSender<EditorActorRequest>,
) -> Self { ) -> Self {
let (intr_tx, intr_rx) = mpsc::unbounded_channel();
let CompileDriver { compiler, universe } = compiler_driver;
let entry = universe.entry_state();
// CompileExporter + DynamicLayoutCompiler + WatchDriver // CompileExporter + DynamicLayoutCompiler + WatchDriver
let driver = CompileExporter::new(compiler_driver); let driver = CompileExporter::new(compiler);
let driver = Reporter { inner: driver, cb }; let driver = Reporter { inner: driver, cb };
let inner = CompileActor::new(driver).with_watch(true); let inner =
CompileServerActor::new(driver, universe, entry, intr_tx, intr_rx).with_watch(true);
Self { Self { inner }
inner,
client: TypstClient {
inner: once_cell::sync::OnceCell::new(),
},
}
} }
pub fn spawn(self) -> Result<TypstClient<H>, Error> { pub fn spawn(self) -> Result<CompileClient<H>, Error> {
let (server, client) = self.inner.split(); let (export_tx, mut export_rx) = mpsc::unbounded_channel();
tokio::spawn(server.spawn().instrument_await("spawn typst server")); let intr_tx = self.inner.intr_tx();
let entry = self.inner.verse.entry_state();
self.client.inner.set(client).ok().unwrap(); tokio::spawn(self.inner.spawn().instrument_await("spawn typst server"));
// drop all export events
Ok(self.client) tokio::spawn(async move { while export_rx.recv().await.is_some() {} });
Ok(CompileClient::new(
"main".to_owned(),
CompileConfig::default(),
entry,
intr_tx,
export_tx,
))
} }
} }
pub struct TypstClient<H: CompilationHandle> {
inner: once_cell::sync::OnceCell<CompileClient<H>>,
}
impl<H: CompilationHandle> TypstClient<H> {
fn inner(&mut self) -> &mut CompileClient<H> {
self.inner.get_mut().unwrap()
}
}
impl<H: CompilationHandle> SourceFileServer for TypstClient<H> {
async fn resolve_source_span(
&mut self,
loc: Location,
) -> Result<Option<SourceSpanOffset>, Error> {
let Location::Src(src_loc) = loc;
self.inner()
.resolve_src_location(src_loc)
.instrument_await("resolve src location")
.await
}
async fn resolve_document_position(
&mut self,
loc: Location,
) -> Result<Option<Position>, Error> {
let Location::Src(src_loc) = loc;
let path = Path::new(&src_loc.filepath).to_owned();
let line = src_loc.pos.line;
let column = src_loc.pos.column;
self.inner()
.resolve_src_to_doc_jump(path, line, column)
.instrument_await("resolve src to doc jump")
.await
}
async fn resolve_source_location(
&mut self,
s: Span,
offset: Option<usize>,
) -> Result<Option<DocToSrcJumpInfo>, Error> {
Ok(self
.inner()
.resolve_span_and_offset(s, offset)
.instrument_await("resolve span offset")
.await
.map_err(|err| {
error!("TypstActor: failed to resolve doc to src jump: {:#}", err);
})
.ok()
.flatten()
.map(|e| DocToSrcJumpInfo {
filepath: e.filepath,
start: e.start,
end: e.end,
}))
}
}
impl<H: CompilationHandle> EditorServer for TypstClient<H> {
async fn update_memory_files(
&mut self,
files: MemoryFiles,
reset_shadow: bool,
) -> Result<(), Error> {
// todo: is it safe to believe that the path is normalized?
let now = std::time::SystemTime::now();
let files = FileChangeSet::new_inserts(
files
.files
.into_iter()
.map(|(path, content)| {
let content = content.as_bytes().into();
// todo: cloning PathBuf -> Arc<Path>
(path.into(), Ok((now, content)).into())
})
.collect(),
);
self.inner().add_memory_changes(if reset_shadow {
MemoryEvent::Sync(files)
} else {
MemoryEvent::Update(files)
});
Ok(())
}
async fn remove_shadow_files(&mut self, files: MemoryFilesShort) -> Result<(), Error> {
// todo: is it safe to believe that the path is normalized?
let files = FileChangeSet::new_removes(files.files.into_iter().map(From::from).collect());
self.inner().add_memory_changes(MemoryEvent::Update(files));
Ok(())
}
}
impl<H: CompilationHandle> CompileHost for TypstClient<H> {}

View file

@ -7,7 +7,7 @@ use typst::syntax::VirtualPath;
use typst::World; use typst::World;
use typst_ts_core::{Bytes, ImmutPath, TypstFileId}; use typst_ts_core::{Bytes, ImmutPath, TypstFileId};
use crate::world::LspWorld; use crate::world::{LspUniverse, LspWorld};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum TemplateSource { pub enum TemplateSource {
@ -20,13 +20,15 @@ pub struct InitTask {
} }
/// Execute an initialization command. /// Execute an initialization command.
pub fn get_entry(world: &LspWorld, tmpl: TemplateSource) -> StrResult<Bytes> { pub fn get_entry(verse: &LspUniverse, tmpl: TemplateSource) -> StrResult<Bytes> {
let TemplateSource::Package(spec) = tmpl; let TemplateSource::Package(spec) = tmpl;
let toml_id = TypstFileId::new(Some(spec.clone()), VirtualPath::new("typst.toml")); let toml_id = TypstFileId::new(Some(spec.clone()), VirtualPath::new("typst.toml"));
let world = verse.spawn();
// Parse the manifest. // Parse the manifest.
let manifest = parse_manifest(world, toml_id)?; let manifest = parse_manifest(&world, toml_id)?;
manifest.validate(&spec)?; manifest.validate(&spec)?;
// Ensure that it is indeed a template. // Ensure that it is indeed a template.

View file

@ -2,14 +2,14 @@ use typst::diag::{eco_format, StrResult};
use typst::syntax::package::{PackageVersion, VersionlessPackageSpec}; use typst::syntax::package::{PackageVersion, VersionlessPackageSpec};
use typst_ts_compiler::package::Registry; use typst_ts_compiler::package::Registry;
use crate::world::LspWorld; use crate::world::LspUniverse;
mod init; mod init;
pub use init::*; pub use init::*;
/// Try to determine the latest version of a package. /// Try to determine the latest version of a package.
pub fn determine_latest_version( pub fn determine_latest_version(
world: &LspWorld, world: &LspUniverse,
spec: &VersionlessPackageSpec, spec: &VersionlessPackageSpec,
) -> StrResult<PackageVersion> { ) -> StrResult<PackageVersion> {
if spec.namespace == "preview" { if spec.namespace == "preview" {

View file

@ -9,14 +9,15 @@ use typst_preview::{
CompileHost, DocToSrcJumpInfo, EditorServer, Location, MemoryFiles, MemoryFilesShort, CompileHost, DocToSrcJumpInfo, EditorServer, Location, MemoryFiles, MemoryFilesShort,
SourceFileServer, SourceFileServer,
}; };
use typst_ts_compiler::service::{Compiler, EntryManager};
use typst_ts_compiler::vfs::notify::{FileChangeSet, MemoryEvent}; use typst_ts_compiler::vfs::notify::{FileChangeSet, MemoryEvent};
use typst_ts_compiler::{service::Compiler, EntryReader};
use typst_ts_core::debug_loc::SourceSpanOffset; use typst_ts_core::debug_loc::SourceSpanOffset;
use typst_ts_core::{Error, TypstDocument, TypstFileId}; use typst_ts_core::{Error, TypstDocument, TypstFileId};
use crate::actor::typ_client::CompileClientActor; use crate::actor::typ_client::CompileClientActorImpl;
use crate::world::LspWorld;
impl SourceFileServer for CompileClientActor { impl<C: Compiler<W = LspWorld> + Send> SourceFileServer for CompileClientActorImpl<C> {
/// fixme: character is 0-based, UTF-16 code unit. /// fixme: character is 0-based, UTF-16 code unit.
/// We treat it as UTF-8 now. /// We treat it as UTF-8 now.
async fn resolve_source_span( async fn resolve_source_span(
@ -25,12 +26,10 @@ impl SourceFileServer for CompileClientActor {
) -> Result<Option<SourceSpanOffset>, Error> { ) -> Result<Option<SourceSpanOffset>, Error> {
let Location::Src(loc) = loc; let Location::Src(loc) = loc;
self.steal_async(move |this| { self.steal_async(move |this| {
let world = this.compiler.world(); let world = this.verse.spawn();
let filepath = Path::new(&loc.filepath); let filepath = Path::new(&loc.filepath);
let relative_path = filepath let relative_path = filepath.strip_prefix(&world.workspace_root()?).ok()?;
.strip_prefix(&this.compiler.world().workspace_root()?)
.ok()?;
let source_id = TypstFileId::new(None, VirtualPath::new(relative_path)); let source_id = TypstFileId::new(None, VirtualPath::new(relative_path));
let source = world.source(source_id).ok()?; let source = world.source(source_id).ok()?;
@ -64,11 +63,9 @@ impl SourceFileServer for CompileClientActor {
self.steal_async(move |this| { self.steal_async(move |this| {
let doc = this.latest_doc.as_deref()?; let doc = this.latest_doc.as_deref()?;
let world = this.compiler.world(); let world = this.verse.spawn();
let relative_path = path let relative_path = path.strip_prefix(&world.workspace_root()?).ok()?;
.strip_prefix(&this.compiler.world().workspace_root()?)
.ok()?;
let source_id = TypstFileId::new(None, VirtualPath::new(relative_path)); let source_id = TypstFileId::new(None, VirtualPath::new(relative_path));
let source = world.source(source_id).ok()?; let source = world.source(source_id).ok()?;
@ -89,7 +86,7 @@ impl SourceFileServer for CompileClientActor {
let ret = self let ret = self
.steal_async(move |this| { .steal_async(move |this| {
let world = this.compiler.world(); let world = this.verse.spawn();
let src_id = span.id()?; let src_id = span.id()?;
let source = world.source(src_id).ok()?; let source = world.source(src_id).ok()?;
let mut range = source.find(span)?.range(); let mut range = source.find(span)?.range();
@ -181,7 +178,7 @@ fn find_in_frame(frame: &Frame, span: Span, min_dis: &mut u64, p: &mut Point) ->
None None
} }
impl EditorServer for CompileClientActor { impl<C: Compiler<W = LspWorld> + Send> EditorServer for CompileClientActorImpl<C> {
async fn update_memory_files( async fn update_memory_files(
&mut self, &mut self,
files: MemoryFiles, files: MemoryFiles,
@ -218,4 +215,4 @@ impl EditorServer for CompileClientActor {
} }
} }
impl CompileHost for CompileClientActor {} impl<C: Compiler<W = LspWorld> + Send> CompileHost for CompileClientActorImpl<C> {}

View file

@ -13,7 +13,7 @@ use typst_ts_compiler::{
font::system::SystemFontSearcher, font::system::SystemFontSearcher,
package::http::HttpRegistry, package::http::HttpRegistry,
vfs::{system::SystemAccessModel, Vfs}, vfs::{system::SystemAccessModel, Vfs},
world::CompilerWorld, SystemCompilerFeat, TypstSystemUniverse, TypstSystemWorld,
}; };
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
@ -49,7 +49,6 @@ pub struct CompileFontOpts {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SharedFontResolver { pub struct SharedFontResolver {
font_paths: Vec<PathBuf>,
pub inner: Arc<FontResolverImpl>, pub inner: Arc<FontResolverImpl>,
} }
@ -63,58 +62,24 @@ impl FontResolver for SharedFontResolver {
} }
impl SharedFontResolver { impl SharedFontResolver {
pub fn new(mut opts: CompileFontOpts) -> ZResult<Self> { pub fn new(opts: CompileFontOpts) -> ZResult<Self> {
// todo: relative paths?
let mut has_relative_path = false;
for p in &opts.font_paths {
if p.is_relative() {
has_relative_path = true;
break;
}
}
if has_relative_path {
let current_dir = std::env::current_dir()
.context("failed to get current directory for relative font paths")?;
for p in &mut opts.font_paths {
if p.is_relative() {
*p = current_dir.join(&p);
}
}
}
let font_paths = opts.font_paths.clone();
let res = crate::world::LspWorldBuilder::resolve_fonts(opts)?;
Ok(Self { Ok(Self {
font_paths, inner: Arc::new(crate::world::LspWorldBuilder::resolve_fonts(opts)?),
inner: Arc::new(res),
}) })
} }
pub fn font_paths(&self) -> &[PathBuf] { pub fn font_paths(&self) -> &[PathBuf] {
&self.font_paths self.inner.font_paths()
} }
} }
/// type trait of [`LspWorld`]. pub type LspCompilerFeat = SystemCompilerFeat;
#[derive(Debug, Clone, Copy)] pub type LspUniverse = TypstSystemUniverse;
pub struct SystemCompilerFeat; pub type LspWorld = TypstSystemWorld;
impl typst_ts_compiler::world::CompilerFeat for SystemCompilerFeat {
/// Uses [`SharedFontResolver`] directly.
type FontResolver = SharedFontResolver;
/// It accesses a physical file system.
type AccessModel = SystemAccessModel;
/// It performs native HTTP requests for fetching package data.
type Registry = HttpRegistry;
}
/// The compiler world in system environment.
pub type LspWorld = CompilerWorld<SystemCompilerFeat>;
pub type ImmutDict = Arc<Prehashed<TypstDict>>; pub type ImmutDict = Arc<Prehashed<TypstDict>>;
pub struct LspWorldBuilder; pub struct LspWorldBuilder;
// Self::resolve_fonts(opts)?,
impl LspWorldBuilder { impl LspWorldBuilder {
/// Create [`LspWorld`] with the given options. /// Create [`LspWorld`] with the given options.
@ -124,15 +89,14 @@ impl LspWorldBuilder {
entry: EntryState, entry: EntryState,
font_resolver: SharedFontResolver, font_resolver: SharedFontResolver,
inputs: ImmutDict, inputs: ImmutDict,
) -> ZResult<LspWorld> { ) -> ZResult<LspUniverse> {
let mut res = CompilerWorld::new_raw( Ok(LspUniverse::new_raw(
entry, entry,
Some(inputs),
Vfs::new(SystemAccessModel {}), Vfs::new(SystemAccessModel {}),
HttpRegistry::default(), HttpRegistry::default(),
font_resolver, font_resolver.inner,
); ))
res.inputs = inputs;
Ok(res)
} }
/// Resolve fonts from given options. /// Resolve fonts from given options.

View file

@ -13,7 +13,7 @@ use ::await_tree::InstrumentAwait;
use debug_loc::SpanInterner; use debug_loc::SpanInterner;
use futures::SinkExt; use futures::SinkExt;
use log::info; use log::info;
use serde::Deserialize; use serde::{Deserialize, Serialize};
use tokio::net::{TcpListener, TcpStream}; use tokio::net::{TcpListener, TcpStream};
use tokio_tungstenite::tungstenite::Message; use tokio_tungstenite::tungstenite::Message;
use tokio_tungstenite::WebSocketStream; use tokio_tungstenite::WebSocketStream;
@ -22,7 +22,12 @@ use typst_ts_core::debug_loc::SourceSpanOffset;
use typst_ts_core::Error; use typst_ts_core::Error;
use typst_ts_core::{ImmutStr, TypstDocument as Document}; use typst_ts_core::{ImmutStr, TypstDocument as Document};
pub use typst_ts_compiler::service::DocToSrcJumpInfo; #[derive(Debug, Serialize)]
pub struct DocToSrcJumpInfo {
pub filepath: String,
pub start: Option<(usize, usize)>, // row, column
pub end: Option<(usize, usize)>,
}
use actor::editor::EditorActor; use actor::editor::EditorActor;
use actor::typst::TypstActor; use actor::typst::TypstActor;