diff --git a/crates/tinymist/src/actor/editor.rs b/crates/tinymist/src/actor/editor.rs index b9a45337..649b3f44 100644 --- a/crates/tinymist/src/actor/editor.rs +++ b/crates/tinymist/src/actor/editor.rs @@ -50,6 +50,9 @@ pub struct EditorActor { diagnostics: HashMap>>, /// The map from project ID to the affected files. affect_map: HashMap>, + + /// The local state. + status: StatusAll, } impl EditorActor { @@ -65,68 +68,76 @@ impl EditorActor { diagnostics: HashMap::new(), affect_map: HashMap::new(), config: EditorActorConfig { notify_status }, + + status: StatusAll { + status: CompileStatusEnum::Compiling, + path: "".to_owned(), + page_count: 0, + words_count: None, + }, } } /// Runs the editor actor in background. It exits when the editor channel /// is closed. pub async fn run(mut self) { - // The local state. - let mut status = StatusAll { - status: CompileStatusEnum::Compiling, - path: "".to_owned(), - page_count: 0, - words_count: None, - }; - while let Some(req) = self.editor_rx.recv().await { - match req { - EditorRequest::Config(config) => { - log::info!("received config request: {config:?}"); - self.config = config; - } - EditorRequest::Diag(version, diagnostics) => { - log::debug!( - "received diagnostics from {version:?}: diag({:?})", - diagnostics.as_ref().map(|files| files.len()) - ); - - self.publish(version.id, diagnostics).await; - } - EditorRequest::Status(compile_status) => { - log::trace!("received status request: {compile_status:?}"); - if self.config.notify_status && compile_status.id == ProjectInsId::PRIMARY { - use tinymist_project::CompileStatusEnum::*; - - status.path = compile_status - .compiling_id - .map_or_default(|fid| unix_slash(fid.vpath().as_rooted_path())); - status.page_count = compile_status.page_count; - status.status = match &compile_status.status { - Compiling => CompileStatusEnum::Compiling, - Suspend | CompileSuccess { .. } => CompileStatusEnum::CompileSuccess, - ExportError { .. } | CompileError { .. } => { - CompileStatusEnum::CompileError - } - }; - self.client.send_notification::(&status); - } - } - EditorRequest::WordCount(id, count) => { - log::trace!("received word count request"); - if self.config.notify_status && id == ProjectInsId::PRIMARY { - status.words_count = Some(count); - self.client.send_notification::(&status); - } - } - } + self.handle(req); } log::info!("editor actor is stopped"); } + #[cfg(not(feature = "system"))] + pub fn step(&mut self) { + while let Ok(req) = self.editor_rx.try_recv() { + self.handle(req); + } + } + + fn handle(&mut self, req: EditorRequest) { + match req { + EditorRequest::Config(config) => { + log::info!("received config request: {config:?}"); + self.config = config; + } + EditorRequest::Diag(version, diagnostics) => { + log::debug!( + "received diagnostics from {version:?}: diag({:?})", + diagnostics.as_ref().map(|files| files.len()) + ); + + self.publish(version.id, diagnostics); + } + EditorRequest::Status(compile_status) => { + log::trace!("received status request: {compile_status:?}"); + if self.config.notify_status && compile_status.id == ProjectInsId::PRIMARY { + use tinymist_project::CompileStatusEnum::*; + + self.status.path = compile_status + .compiling_id + .map_or_default(|fid| unix_slash(fid.vpath().as_rooted_path())); + self.status.page_count = compile_status.page_count; + self.status.status = match &compile_status.status { + Compiling => CompileStatusEnum::Compiling, + Suspend | CompileSuccess { .. } => CompileStatusEnum::CompileSuccess, + ExportError { .. } | CompileError { .. } => CompileStatusEnum::CompileError, + }; + self.client.send_notification::(&self.status); + } + } + EditorRequest::WordCount(id, count) => { + log::trace!("received word count request"); + if self.config.notify_status && id == ProjectInsId::PRIMARY { + self.status.words_count = Some(count); + self.client.send_notification::(&self.status); + } + } + } + } + /// Publishes diagnostics of a project to the editor. - pub async fn publish(&mut self, id: ProjectInsId, next_diag: Option) { + pub fn publish(&mut self, id: ProjectInsId, next_diag: Option) { let affected = match next_diag.as_ref() { Some(next_diag) => self .affect_map diff --git a/crates/tinymist/src/lsp.rs b/crates/tinymist/src/lsp.rs index 6f12fce0..32596f13 100644 --- a/crates/tinymist/src/lsp.rs +++ b/crates/tinymist/src/lsp.rs @@ -60,6 +60,7 @@ impl ServerState { .log_error("could not register to watch config changes"); } + self.schedule_async(); log::info!("server initialized"); Ok(()) } @@ -94,6 +95,8 @@ impl ServerState { // Focus after opening self.implicit_focus_entry(|| Some(path), 'o'); + + self.schedule_async(); Ok(()) } @@ -101,6 +104,7 @@ impl ServerState { let path = as_path(params.text_document).as_path().into(); self.remove_source(path).map_err(invalid_params)?; + self.schedule_async(); Ok(()) } @@ -110,6 +114,7 @@ impl ServerState { self.edit_source(path, changes, self.const_config().position_encoding) .map_err(invalid_params)?; + self.schedule_async(); Ok(()) } @@ -117,6 +122,7 @@ impl ServerState { let path = as_path(params.text_document).as_path().into(); self.save_source(path).map_err(invalid_params)?; + self.schedule_async(); Ok(()) } } @@ -176,6 +182,7 @@ impl ServerState { } log::info!("new settings applied"); + self.schedule_async(); Ok(()) } diff --git a/crates/tinymist/src/server.rs b/crates/tinymist/src/server.rs index a14e7e48..8c501f9f 100644 --- a/crates/tinymist/src/server.rs +++ b/crates/tinymist/src/server.rs @@ -92,8 +92,11 @@ pub struct ServerState { pub config: Config, /// Source synchronized with client pub memory_changes: HashMap, Source>, + /// The diagnostics sender to send diagnostics to `crate::actor::cluster`. pub editor_tx: mpsc::UnboundedSender, + /// The editor actor state + editor_actor: Option, } /// Getters and the main loop. @@ -147,6 +150,7 @@ impl ServerState { focusing: None, implicit_position: None, formatter, + editor_actor: None, } } @@ -196,8 +200,14 @@ impl ServerState { #[cfg(feature = "preview")] server.background_preview(); - // Run the cluster in the background after we referencing it - client.handle.spawn(editor_actor.run()); + // Runs the editor actor. If the server is not running in the system, we do not + // spawn the editor actor and run it in the background, but steps it + // in the main thread using `Self::schedule_async`. + if cfg!(feature = "system") { + client.handle.spawn(editor_actor.run()); + } else { + server.editor_actor = Some(editor_actor); + } } server @@ -341,6 +351,20 @@ impl ServerState { .with_request::(Self::debug_threads) } + #[cfg(not(feature = "system"))] + /// Schedules the async tasks of the server on some paths. This is used to + /// run the server in passive context, for example, in the web + /// environment where the server is not run in background. + pub(crate) fn schedule_async(&mut self) { + if let Some(editor_actor) = self.editor_actor.as_mut() { + editor_actor.step(); + } + } + + #[cfg(feature = "system")] + #[inline(always)] + pub(crate) fn schedule_async(&mut self) {} + /// Handles the project interrupts. fn compile_interrupt>( mut state: ServiceState, @@ -354,6 +378,9 @@ impl ServerState { }; ready.project.interrupt(params); + + ready.schedule_async(); + // log::info!("interrupted in {:?}", _start.elapsed()); Ok(()) }