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

View file

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

View file

@ -36,11 +36,11 @@ impl SemanticRequest for SymbolRequest {
let mut symbols = vec![];
ctx.resources.iter_dependencies(&mut |path, _| {
let Ok(source) = ctx.source_by_path(path) else {
ctx.resources.iter_dependencies(&mut |path| {
let Ok(source) = ctx.source_by_path(&path) else {
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(
|symbols| {
self.pattern.as_ref().map(|pattern| {

View file

@ -8,17 +8,13 @@ use std::{
use once_cell::sync::Lazy;
pub use serde::Serialize;
use serde_json::{ser::PrettyFormatter, Serializer, Value};
use typst::{
diag::FileResult,
syntax::{
use typst::syntax::{
ast::{self, AstNode},
FileId as TypstFileId, LinkedNode, Source, SyntaxKind, VirtualPath,
},
};
use typst::{diag::PackageError, foundations::Bytes};
use typst_ts_compiler::{
service::{CompileDriver, Compiler, EntryManager},
NotifyApi, ShadowApi,
service::CompileDriver, EntryManager, EntryReader, ShadowApi, TypstSystemUniverse, WorldDeps,
};
use typst_ts_core::{
config::compiler::{EntryOpts, EntryState},
@ -46,10 +42,7 @@ impl<'a> AnalysisResources for WrapWorld<'a> {
self.0.registry.resolve(spec)
}
fn iter_dependencies<'b>(
&'b self,
f: &mut dyn FnMut(&'b reflexo::ImmutPath, FileResult<&typst_ts_compiler::Time>),
) {
fn iter_dependencies(&self, f: &mut dyn FnMut(reflexo::ImmutPath)) {
self.0.iter_dependencies(f)
}
}
@ -65,7 +58,7 @@ pub fn snapshot_testing(name: &str, f: &impl Fn(&mut AnalysisContext, PathBuf))
#[cfg(windows)]
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 paths = w
.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()))
})
.collect::<Vec<_>>();
let w = WrapWorld(w);
let mut w = w.spawn();
let w = WrapWorld(&mut w);
let mut ctx = AnalysisContext::new(
&w,
Analysis {
@ -103,13 +97,16 @@ pub fn get_test_properties(s: &str) -> HashMap<&'_ str, &'_ str> {
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) {
PathBuf::from("C:\\")
} else {
PathBuf::from("/")
};
let mut world = TypstSystemWorld::new(CompileOpts {
let mut world = TypstSystemUniverse::new(CompileOpts {
entry: EntryOpts::new_rooted(root.as_path().into(), None),
..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();
let mut driver = CompileDriver::new(world);
let mut driver = CompileDriver::new(std::marker::PhantomData, world);
let _ = driver.compile(&mut Default::default());
let pw = last_pw.unwrap();
driver
.world_mut()
.universe_mut()
.mutate_entry(EntryState::new_rooted(
root.as_path().into(),
Some(TypstFileId::new(
@ -159,7 +156,7 @@ pub fn run_with_sources<T>(source: &str, f: impl FnOnce(&mut TypstSystemWorld, P
)),
))
.unwrap();
f(driver.world_mut(), pw)
f(driver.universe_mut(), pw)
}
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_render::PeriscopeRenderer;
use tokio::sync::{mpsc, watch};
use typst_ts_compiler::{
service::CompileDriverImpl,
vfs::notify::{FileChangeSet, MemoryEvent},
};
use typst_ts_compiler::vfs::notify::{FileChangeSet, MemoryEvent};
use typst_ts_core::config::compiler::EntryState;
use self::{
@ -28,12 +25,10 @@ use self::{
};
use crate::{
compiler::CompileServer,
world::{ImmutDict, LspWorld, LspWorldBuilder},
world::{ImmutDict, LspWorldBuilder},
TypstLanguageServer,
};
type CompileDriverInner = CompileDriverImpl<LspWorld>;
impl CompileServer {
pub fn restart_server(&mut self, group: &str) {
let server = self.server(
@ -102,13 +97,12 @@ impl CompileServer {
self.handle.spawn_blocking(move || {
// Create the world
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");
// Create the compiler
let driver = CompileDriverInner::new(world);
let driver = CompileDriver {
inner: driver,
inner: std::marker::PhantomData,
handler,
analysis: Analysis {
position_encoding,
@ -121,7 +115,7 @@ impl CompileServer {
// Create the actor
tokio::spawn(
CompileServerActor::new(driver, entry_, intr_tx, intr_rx)
CompileServerActor::new(driver, verse, entry_, intr_tx, intr_rx)
.with_watch(true)
.spawn(),
);

View file

@ -42,16 +42,16 @@ use tinymist_query::{
use tinymist_render::PeriscopeRenderer;
use tokio::sync::{mpsc, oneshot, watch};
use typst::{
diag::{FileResult, PackageError, SourceDiagnostic, SourceResult},
diag::{PackageError, SourceDiagnostic, SourceResult},
layout::Position,
model::Document as TypstDocument,
syntax::package::PackageSpec,
World as TypstWorld,
};
use typst_ts_compiler::{
service::{CompileDriverImpl, CompileEnv, CompileMiddleware, Compiler, EntryManager, EnvWorld},
service::{CompileEnv, CompileMiddleware, Compiler, PureCompiler},
vfs::notify::MemoryEvent,
Time,
EntryManager, EntryReader,
};
use typst_ts_core::{
config::compiler::EntryState, debug_loc::DataSource, error::prelude::*, typst::prelude::EcoVec,
@ -61,18 +61,18 @@ use typst_ts_core::{
use super::{
editor::{EditorRequest, TinymistCompileStatusEnum},
export::ExportConfig,
typ_server::{is_inactive, CompileServerActor, Interrupt},
typ_server::{CompileServerActor, Interrupt},
};
use crate::{
actor::export::ExportRequest,
compiler_init::CompileConfig,
tools::preview::{CompilationHandle, CompileStatus},
utils,
world::LspWorld,
world::{LspCompilerFeat, LspWorld},
};
type CompileDriverInner = CompileDriverImpl<LspWorld>;
type CompileService = CompileServerActor<CompileDriver>;
type CompileService<C> = CompileServerActor<C, LspCompilerFeat>;
pub type CompileClientActor = CompileClientActorImpl<CompileDriver>;
type EditorSender = mpsc::UnboundedSender<EditorRequest>;
@ -131,7 +131,7 @@ impl CompileHandler {
}
pub struct CompileDriver {
pub(super) inner: CompileDriverInner,
pub(super) inner: PureCompiler<LspWorld>,
#[allow(unused)]
pub(super) handler: CompileHandler,
pub(super) analysis: Analysis,
@ -139,7 +139,7 @@ pub struct CompileDriver {
}
impl CompileMiddleware for CompileDriver {
type Compiler = CompileDriverInner;
type Compiler = PureCompiler<LspWorld>;
fn inner(&self) -> &Self::Compiler {
&self.inner
@ -149,7 +149,11 @@ impl CompileMiddleware for CompileDriver {
&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
.editor_tx
.send(EditorRequest::Status(
@ -158,10 +162,14 @@ impl CompileMiddleware for CompileDriver {
))
.unwrap();
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) => {
self.handler.notify_compile(Ok(doc.clone()));
self.notify_diagnostics(
world,
EcoVec::new(),
env.tracer.as_ref().map(|e| e.clone().warnings()),
);
@ -170,7 +178,11 @@ impl CompileMiddleware for CompileDriver {
Err(err) => {
self.handler
.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())
}
}
@ -180,20 +192,22 @@ impl CompileMiddleware for CompileDriver {
impl CompileDriver {
fn notify_diagnostics(
&mut self,
world: &LspWorld,
errors: EcoVec<SourceDiagnostic>,
warnings: Option<EcoVec<SourceDiagnostic>>,
) {
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()))
});
match diagnostics {
Ok(diagnostics) => {
let entry = world.entry_state();
// todo: better way to remove diagnostics
// todo: check all errors in this file
let detached = is_inactive(&self.inner.world().entry);
let detached = entry.is_inactive();
let valid = !detached;
self.handler.push_diagnostics(valid.then_some(diagnostics));
}
@ -206,15 +220,14 @@ impl CompileDriver {
pub fn run_analysis<T>(
&mut self,
w: &LspWorld,
f: impl FnOnce(&mut AnalysisContext<'_>) -> T,
) -> anyhow::Result<T> {
let w = self.inner.world_mut();
let Some(main) = w.main_id() else {
error!("TypstActor: 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");
bail!("root is not set");
};
@ -222,12 +235,8 @@ impl CompileDriver {
info!("TypstActor: failed to prepare main file: {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> {
fn world(&self) -> &dyn typst::World {
@ -239,17 +248,14 @@ impl CompileDriver {
self.0.registry.resolve(spec)
}
fn iter_dependencies<'b>(
&'b self,
f: &mut dyn FnMut(&'b ImmutPath, FileResult<&Time>),
) {
use typst_ts_compiler::NotifyApi;
fn iter_dependencies(&self, f: &mut dyn FnMut(ImmutPath)) {
use typst_ts_compiler::WorldDeps;
self.0.iter_dependencies(f)
}
/// Resolve extra font information.
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.
@ -270,20 +276,20 @@ impl CompileDriver {
}
}
pub struct CompileClientActor {
pub struct CompileClientActorImpl<C: Compiler> {
pub diag_group: String,
pub config: CompileConfig,
entry: EntryState,
intr_tx: mpsc::UnboundedSender<Interrupt<CompileService>>,
intr_tx: mpsc::UnboundedSender<Interrupt<CompileService<C>>>,
export_tx: mpsc::UnboundedSender<ExportRequest>,
}
impl CompileClientActor {
impl<C: Compiler<W = LspWorld> + Send> CompileClientActorImpl<C> {
pub(crate) fn new(
diag_group: String,
config: CompileConfig,
entry: EntryState,
intr_tx: mpsc::UnboundedSender<Interrupt<CompileService>>,
intr_tx: mpsc::UnboundedSender<Interrupt<CompileService<C>>>,
export_tx: mpsc::UnboundedSender<ExportRequest>,
) -> Self {
Self {
@ -297,11 +303,11 @@ impl CompileClientActor {
fn steal_inner<Ret: Send + 'static>(
&self,
f: impl FnOnce(&mut CompileService) -> Ret + Send + 'static,
f: impl FnOnce(&mut CompileService<C>) -> Ret + Send + 'static,
) -> ZResult<oneshot::Receiver<Ret>> {
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() {
// Receiver was dropped. The main thread may have exited, or the request may
// have been cancelled.
@ -319,7 +325,7 @@ impl CompileClientActor {
/// Steal the compiler thread and run the given function.
pub fn steal<Ret: Send + 'static>(
&self,
f: impl FnOnce(&mut CompileService) -> Ret + Send + 'static,
f: impl FnOnce(&mut CompileService<C>) -> Ret + Send + 'static,
) -> ZResult<Ret> {
utils::threaded_receive(self.steal_inner(f)?)
}
@ -327,31 +333,46 @@ impl CompileClientActor {
/// Steal the compiler thread and run the given function.
pub async fn steal_async<Ret: Send + 'static>(
&self,
f: impl FnOnce(&mut CompileService) -> Ret + Send + 'static,
f: impl FnOnce(&mut CompileService<C>) -> Ret + Send + 'static,
) -> ZResult<Ret> {
self.steal_inner(f)?
.await
.map_err(map_string_err("failed to call steal_async"))
}
pub fn steal_state<T: Send + Sync + 'static>(
&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 c = &mut compiler.compiler.compiler;
c.run_analysis(move |ctx| f(ctx, doc))
})?
pub fn sync_config(&mut self, config: CompileConfig) {
self.config = config;
}
pub fn steal_world<T: Send + Sync + 'static>(
&self,
f: impl FnOnce(&mut AnalysisContext) -> T + Send + Sync + 'static,
) -> anyhow::Result<T> {
self.steal(move |compiler| compiler.compiler.compiler.run_analysis(f))?
pub fn add_memory_changes(&self, event: MemoryEvent) {
let _ = self.intr_tx.send(Interrupt::Memory(event));
}
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) {
let _ = self.change_entry(None);
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> {
if path
.as_deref()
@ -388,8 +405,8 @@ impl CompileClientActor {
self.steal(move |compiler| {
compiler.change_entry(next.clone());
let next_is_inactive = is_inactive(&next);
let res = compiler.compiler.world_mut().mutate_entry(next);
let next_is_inactive = next.is_inactive();
let res = compiler.verse.mutate_entry(next);
if next_is_inactive {
info!("TypstActor: removing diag");
@ -408,12 +425,26 @@ impl CompileClientActor {
Ok(true)
}
pub fn add_memory_changes(&self, event: MemoryEvent) {
let _ = self.intr_tx.send(Interrupt::Memory(event));
pub fn steal_state<T: Send + Sync + 'static>(
&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) {
let _ = self.export_tx.send(ExportRequest::ChangeConfig(config));
pub fn steal_world<T: Send + Sync + 'static>(
&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) {
@ -426,13 +457,15 @@ impl CompileClientActor {
let dg = self.diag_group.clone();
self.steal(move |c| {
let cc = &c.compiler.compiler;
let w = c.verse.spawn();
let info = ServerInfoResponse {
root: cc.world().entry.root().map(|e| e.as_ref().to_owned()),
font_paths: cc.world().font_resolver.font_paths().to_owned(),
inputs: cc.world().inputs.as_ref().deref().clone(),
root: w.entry_state().root().map(|e| e.as_ref().to_owned()),
font_paths: w.font_resolver.font_paths().to_owned(),
inputs: c.verse.inputs().as_ref().deref().clone(),
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()),
]),
};
@ -441,23 +474,4 @@ impl CompileClientActor {
})
.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 tokio::sync::{mpsc, oneshot};
use typst_ts_compiler::service::{
use typst_ts_compiler::{
service::{
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_compiler::ShadowApi;
use typst_ts_core::{config::compiler::EntryState, TypstDocument, TypstFileId};
use typst_ts_core::{config::compiler::EntryState, TypstDocument};
/// 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> {
/// Compile anyway.
Compile,
/// 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>),
/// Memory file changes.
Memory(MemoryEvent),
@ -30,11 +38,6 @@ pub enum Interrupt<Ctx> {
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.
enum CompilerResponse {
/// Response to the file watcher
@ -55,9 +58,11 @@ struct SuspendState {
}
/// 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.
pub compiler: CompileReporter<C>,
pub compiler: CompileReporter<C, CompilerWorld<F>>,
/// Whether to enable file system watching.
pub enable_watch: bool,
@ -85,12 +90,12 @@ pub struct CompileServerActor<C: Compiler> {
suspend_state: SuspendState,
}
impl<C: Compiler + ShadowApi + Send + 'static> CompileServerActor<C>
where
C::World: for<'files> codespan_reporting::files::Files<'files, FileId = TypstFileId>,
impl<F: CompilerFeat + Send + 'static, C: Compiler<W = CompilerWorld<F>> + Send + 'static>
CompileServerActor<C, F>
{
pub fn new_with_features(
compiler: C,
verse: CompilerUniverse<F>,
entry: EntryState,
feature_set: FeatureSet,
intr_tx: mpsc::UnboundedSender<Interrupt<Self>>,
@ -99,6 +104,7 @@ where
Self {
compiler: CompileReporter::new(compiler)
.with_generic_reporter(ConsoleDiagReporter::default()),
verse,
logical_tick: 1,
enable_watch: false,
@ -116,7 +122,7 @@ where
intr_rx,
suspend_state: SuspendState {
suspended: is_inactive(&entry),
suspended: entry.is_inactive(),
dirty: false,
},
}
@ -125,18 +131,29 @@ where
/// Create a new compiler thread.
pub fn new(
compiler: C,
world: CompilerUniverse<F>,
entry: EntryState,
intr_tx: mpsc::UnboundedSender<Interrupt<Self>>,
intr_rx: mpsc::UnboundedReceiver<Interrupt<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 {
self.enable_watch = enable_watch;
self
}
pub fn intr_tx(&self) -> mpsc::UnboundedSender<Interrupt<Self>> {
self.intr_tx.clone()
}
pub fn success_doc(&self) -> Option<VersionedDocument> {
self.latest_success_doc
.clone()
@ -174,7 +191,8 @@ where
async fn block_run_inner(mut self) -> bool {
if !self.enable_watch {
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();
}
@ -191,7 +209,8 @@ where
pub async fn spawn(mut self) -> Option<JoinHandle<()>> {
if !self.enable_watch {
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;
}
@ -268,8 +287,8 @@ where
Some(compile_thread.unwrap())
}
pub(crate) fn change_entry(&mut self, entry: EntryState) {
self.suspend_state.suspended = is_inactive(&entry);
pub fn change_entry(&mut self, entry: EntryState) {
self.suspend_state.suspended = entry.is_inactive();
if !self.suspend_state.suspended && self.suspend_state.dirty {
self.intr_tx.send(Interrupt::Compile).ok();
}
@ -288,9 +307,11 @@ where
return;
}
let w = self.verse.spawn();
// Compile the document.
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() {
self.latest_success_doc.clone_from(&self.latest_doc);
}
@ -303,8 +324,7 @@ where
// Notify the new file dependencies.
let mut deps = vec![];
self.compiler
.iter_dependencies(&mut |dep, _| deps.push(dep.clone()));
w.iter_dependencies(&mut |dep| deps.push(dep.clone()));
send(Notify(NotifyMessage::SyncDependency(deps)));
}
@ -340,7 +360,8 @@ where
// If there is no invalidation happening, apply memory changes directly.
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;
}
@ -362,13 +383,15 @@ where
Interrupt::Fs(mut event) => {
log::debug!("CompileServerActor: fs event incoming {event:?}");
// Apply file system changes.
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(&mut event).is_none() {
if Self::apply_delayed_memory_changes(verse, dirty_tick, &mut event).is_none() {
log::warn!("CompileServerActor: unknown upstream update event");
}
// Apply file system changes.
self.compiler.notify_fs_event(event);
verse.notify_fs_event(event)
});
true
}
@ -377,7 +400,11 @@ where
}
/// 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
if let FilesystemEvent::UpstreamUpdate { upstream_event, .. } = event {
let event = upstream_event.take()?.opaque;
@ -387,25 +414,25 @@ where
} = *event.downcast().ok()?;
// Recovery from dirty shadow state.
if logical_tick == self.dirty_shadow_logical_tick {
self.dirty_shadow_logical_tick = 0;
if logical_tick == *dirty_shadow_logical_tick {
*dirty_shadow_logical_tick = 0;
}
self.apply_memory_changes(event);
Self::apply_memory_changes(verse, event);
}
Some(())
}
/// 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(..)) {
self.compiler.reset_shadow();
verse.reset_shadow();
}
match event {
MemoryEvent::Update(event) | MemoryEvent::Sync(event) => {
for removes in event.removes {
let _ = self.compiler.unmap_shadow(&removes);
let _ = verse.unmap_shadow(&removes);
}
for (p, t) in event.inserts {
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]
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}"))

View file

@ -42,7 +42,9 @@ pub use server::compiler_init;
pub use server::lsp::*;
pub use server::lsp_init::*;
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;

View file

@ -10,12 +10,6 @@ use comemo::Prehashed;
use lsp_types::{InitializeParams, InitializedParams};
use once_cell::sync::Lazy;
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::{
compiler_init::{CompileInit, CompileInitializeParams},
harness::{lsp_harness, InitializedLspDriver, LspDriver, LspHost},
@ -23,6 +17,13 @@ use tinymist::{
transport::with_stdio_transport,
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")]
#[global_allocator]
@ -183,8 +184,12 @@ pub fn compiler_main(args: CompileArgs) -> anyhow::Result<()> {
let (timings, _doc, diagnostics) = service
.compiler()
.steal(|c| {
c.compiler.world_mut().mutate_entry(entry).unwrap();
c.compiler.world_mut().inputs = inputs;
c.verse.increment_revision(|verse| {
verse.mutate_entry(entry).unwrap();
verse.set_inputs(inputs);
});
let w = c.verse.spawn();
let mut env = CompileEnv {
tracer: Some(Tracer::default()),
@ -192,24 +197,23 @@ pub fn compiler_main(args: CompileArgs) -> anyhow::Result<()> {
};
typst_timing::enable();
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),
Err(e) => {
errors = e;
None
}
};
let world = c.compiler.world();
let mut writer = std::io::BufWriter::new(Vec::new());
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 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(
ctx,
warnings.iter().flatten().chain(errors.iter()),
@ -261,7 +265,6 @@ impl<T> Drop for ForceDrop<T> {
/// Turns a span into a (file, line) pair.
fn resolve_span(world: &LspWorld, span: Span) -> Option<(String, u32)> {
use typst::World;
let id = span.id()?;
let source = world.source(id).ok()?;
let range = source.range(span)?;

View file

@ -1,6 +1,5 @@
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};
pub use super::prelude::*;
@ -188,16 +187,23 @@ impl TypstLanguageServer {
let font = self
.primary()
.steal(move |e| {
let verse = &mut e.verse;
let entry_path: Arc<Path> = Path::new("/._sym_.typ").into();
let new_entry = EntryState::new_rootless(entry_path.clone())?;
let old_entry = e.compiler.world_mut().mutate_entry(new_entry).ok()?;
let prepared = e
.compiler
let (old_entry, prepared) = verse.increment_revision(|verse| {
let old_entry = verse.mutate_entry(new_entry).ok()?;
let prepared = verse
.map_shadow(&entry_path, math_shaping_text.into_bytes().into())
.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!(
"sym doc: {doc:?}",

View file

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

View file

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

View file

@ -1,32 +1,31 @@
use std::path::Path;
use std::sync::Arc;
use await_tree::InstrumentAwait;
use log::error;
use tokio::sync::mpsc;
use typst::diag::SourceResult;
use typst::layout::Position;
use typst::model::Document;
use typst::syntax::Span;
use typst_ts_compiler::service::{
CompileActor, CompileClient as TsCompileClient, CompileExporter, Compiler, WorldExporter,
};
use typst::World;
use typst_ts_compiler::service::{CompileDriver, CompileMiddleware};
use typst_ts_compiler::vfs::notify::{FileChangeSet, MemoryEvent};
use typst_ts_core::debug_loc::SourceSpanOffset;
use typst_ts_compiler::service::{CompileExporter, Compiler, PureCompiler, WorldExporter};
use typst_ts_compiler::{EntryReader, TypstSystemWorld};
use typst_ts_core::Error;
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>>;
pub type CompileClient<H> = TsCompileClient<CompileService<H>>;
use crate::actor::typ_client::CompileClientActorImpl;
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> {
inner: CompileService<H>,
client: TypstClient<H>,
}
pub struct Reporter<C, H> {
@ -47,10 +46,11 @@ impl<C: Compiler, H: CompilationHandle> CompileMiddleware for Reporter<C, H> {
fn wrap_compile(
&mut self,
world: &<C as typst_ts_compiler::service::Compiler>::W,
env: &mut typst_ts_compiler::service::CompileEnv,
) -> SourceResult<Arc<Document>> {
self.cb.status(CompileStatus::Compiling);
match self.inner_mut().compile(env) {
match self.inner_mut().compile(world, env) {
Ok(doc) => {
self.cb.notify_compile(Ok(doc.clone()));
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> {
fn export(&mut self, output: Arc<typst::model::Document>) -> SourceResult<()> {
self.inner.export(output)
impl<W: World, C: Compiler<W = W> + WorldExporter<W>, H> WorldExporter<W> for Reporter<C, H> {
fn export(&mut self, world: &W, output: Arc<typst::model::Document>) -> SourceResult<()> {
self.inner.export(world, output)
}
}
impl<H: CompilationHandle> CompileServer<H> {
pub fn new(
compiler_driver: CompileDriver,
compiler_driver: CompileDriver<PureCompiler<TypstSystemWorld>>,
cb: H,
// renderer_sender: broadcast::Sender<RenderActorRequest>,
// editor_conn_sender: mpsc::UnboundedSender<EditorActorRequest>,
) -> Self {
let (intr_tx, intr_rx) = mpsc::unbounded_channel();
let CompileDriver { compiler, universe } = compiler_driver;
let entry = universe.entry_state();
// CompileExporter + DynamicLayoutCompiler + WatchDriver
let driver = CompileExporter::new(compiler_driver);
let driver = CompileExporter::new(compiler);
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 {
inner,
client: TypstClient {
inner: once_cell::sync::OnceCell::new(),
},
}
Self { inner }
}
pub fn spawn(self) -> Result<TypstClient<H>, Error> {
let (server, client) = self.inner.split();
tokio::spawn(server.spawn().instrument_await("spawn typst server"));
self.client.inner.set(client).ok().unwrap();
Ok(self.client)
pub fn spawn(self) -> Result<CompileClient<H>, Error> {
let (export_tx, mut export_rx) = mpsc::unbounded_channel();
let intr_tx = self.inner.intr_tx();
let entry = self.inner.verse.entry_state();
tokio::spawn(self.inner.spawn().instrument_await("spawn typst server"));
// drop all export events
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_ts_core::{Bytes, ImmutPath, TypstFileId};
use crate::world::LspWorld;
use crate::world::{LspUniverse, LspWorld};
#[derive(Debug, Clone)]
pub enum TemplateSource {
@ -20,13 +20,15 @@ pub struct InitTask {
}
/// 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 toml_id = TypstFileId::new(Some(spec.clone()), VirtualPath::new("typst.toml"));
let world = verse.spawn();
// Parse the manifest.
let manifest = parse_manifest(world, toml_id)?;
let manifest = parse_manifest(&world, toml_id)?;
manifest.validate(&spec)?;
// 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_ts_compiler::package::Registry;
use crate::world::LspWorld;
use crate::world::LspUniverse;
mod init;
pub use init::*;
/// Try to determine the latest version of a package.
pub fn determine_latest_version(
world: &LspWorld,
world: &LspUniverse,
spec: &VersionlessPackageSpec,
) -> StrResult<PackageVersion> {
if spec.namespace == "preview" {

View file

@ -9,14 +9,15 @@ use typst_preview::{
CompileHost, DocToSrcJumpInfo, EditorServer, Location, MemoryFiles, MemoryFilesShort,
SourceFileServer,
};
use typst_ts_compiler::service::{Compiler, EntryManager};
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::{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.
/// We treat it as UTF-8 now.
async fn resolve_source_span(
@ -25,12 +26,10 @@ impl SourceFileServer for CompileClientActor {
) -> Result<Option<SourceSpanOffset>, Error> {
let Location::Src(loc) = loc;
self.steal_async(move |this| {
let world = this.compiler.world();
let world = this.verse.spawn();
let filepath = Path::new(&loc.filepath);
let relative_path = filepath
.strip_prefix(&this.compiler.world().workspace_root()?)
.ok()?;
let relative_path = filepath.strip_prefix(&world.workspace_root()?).ok()?;
let source_id = TypstFileId::new(None, VirtualPath::new(relative_path));
let source = world.source(source_id).ok()?;
@ -64,11 +63,9 @@ impl SourceFileServer for CompileClientActor {
self.steal_async(move |this| {
let doc = this.latest_doc.as_deref()?;
let world = this.compiler.world();
let world = this.verse.spawn();
let relative_path = path
.strip_prefix(&this.compiler.world().workspace_root()?)
.ok()?;
let relative_path = path.strip_prefix(&world.workspace_root()?).ok()?;
let source_id = TypstFileId::new(None, VirtualPath::new(relative_path));
let source = world.source(source_id).ok()?;
@ -89,7 +86,7 @@ impl SourceFileServer for CompileClientActor {
let ret = self
.steal_async(move |this| {
let world = this.compiler.world();
let world = this.verse.spawn();
let src_id = span.id()?;
let source = world.source(src_id).ok()?;
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
}
impl EditorServer for CompileClientActor {
impl<C: Compiler<W = LspWorld> + Send> EditorServer for CompileClientActorImpl<C> {
async fn update_memory_files(
&mut self,
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,
package::http::HttpRegistry,
vfs::{system::SystemAccessModel, Vfs},
world::CompilerWorld,
SystemCompilerFeat, TypstSystemUniverse, TypstSystemWorld,
};
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
@ -49,7 +49,6 @@ pub struct CompileFontOpts {
#[derive(Debug, Clone)]
pub struct SharedFontResolver {
font_paths: Vec<PathBuf>,
pub inner: Arc<FontResolverImpl>,
}
@ -63,58 +62,24 @@ impl FontResolver for SharedFontResolver {
}
impl SharedFontResolver {
pub fn new(mut 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)?;
pub fn new(opts: CompileFontOpts) -> ZResult<Self> {
Ok(Self {
font_paths,
inner: Arc::new(res),
inner: Arc::new(crate::world::LspWorldBuilder::resolve_fonts(opts)?),
})
}
pub fn font_paths(&self) -> &[PathBuf] {
&self.font_paths
self.inner.font_paths()
}
}
/// type trait of [`LspWorld`].
#[derive(Debug, Clone, Copy)]
pub struct SystemCompilerFeat;
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 LspCompilerFeat = SystemCompilerFeat;
pub type LspUniverse = TypstSystemUniverse;
pub type LspWorld = TypstSystemWorld;
pub type ImmutDict = Arc<Prehashed<TypstDict>>;
pub struct LspWorldBuilder;
// Self::resolve_fonts(opts)?,
impl LspWorldBuilder {
/// Create [`LspWorld`] with the given options.
@ -124,15 +89,14 @@ impl LspWorldBuilder {
entry: EntryState,
font_resolver: SharedFontResolver,
inputs: ImmutDict,
) -> ZResult<LspWorld> {
let mut res = CompilerWorld::new_raw(
) -> ZResult<LspUniverse> {
Ok(LspUniverse::new_raw(
entry,
Some(inputs),
Vfs::new(SystemAccessModel {}),
HttpRegistry::default(),
font_resolver,
);
res.inputs = inputs;
Ok(res)
font_resolver.inner,
))
}
/// Resolve fonts from given options.

View file

@ -13,7 +13,7 @@ use ::await_tree::InstrumentAwait;
use debug_loc::SpanInterner;
use futures::SinkExt;
use log::info;
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use tokio::net::{TcpListener, TcpStream};
use tokio_tungstenite::tungstenite::Message;
use tokio_tungstenite::WebSocketStream;
@ -22,7 +22,12 @@ use typst_ts_core::debug_loc::SourceSpanOffset;
use typst_ts_core::Error;
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::typst::TypstActor;