diff --git a/CHANGELOG.md b/CHANGELOG.md index 02f82bca0c6c..8ef91b0cb8e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,7 @@ - [Added scroll bounce animation][3836] which activates when scrolling past the end of scrollable content. - [Added project snapshot saving on shortcut][3923] +- [Added shortcut to interrupt the program][3967] #### EnsoGL (rendering engine) @@ -403,6 +404,7 @@ [3885]: https://github.com/enso-org/enso/pull/3885 [3919]: https://github.com/enso-org/enso/pull/3919 [3923]: https://github.com/enso-org/enso/pull/3923 +[3967]: https://github.com/enso-org/enso/pull/3967 #### Enso Compiler diff --git a/app/gui/controller/engine-protocol/src/language_server.rs b/app/gui/controller/engine-protocol/src/language_server.rs index c2df69c858b0..0536c2d63c10 100644 --- a/app/gui/controller/engine-protocol/src/language_server.rs +++ b/app/gui/controller/engine-protocol/src/language_server.rs @@ -151,6 +151,14 @@ trait API { fn modify_visualisation (&self, visualisation_id: Uuid, visualisation_config: VisualisationConfiguration) -> (); + /// Interrupt the program execution. + #[MethodInput=InterruptInput, rpc_name="executionContext/interrupt"] + fn interrupt(&self, context_id: ContextId) -> (); + + /// Restart the program execution. + #[MethodInput=RecomputeInput, rpc_name="executionContext/recompute"] + fn recompute(&self, context_id: ContextId) -> (); + /// Obtain the full suggestions database. #[MethodInput=GetSuggestionsDatabaseInput, rpc_name="search/getSuggestionsDatabase"] fn get_suggestions_database(&self) -> response::GetSuggestionDatabase; diff --git a/app/gui/docs/product/shortcuts.md b/app/gui/docs/product/shortcuts.md index e5319f81020b..69a34644d04b 100644 --- a/app/gui/docs/product/shortcuts.md +++ b/app/gui/docs/product/shortcuts.md @@ -48,6 +48,8 @@ broken and require further investigation. | ctrl+w | Close the application (Windows, Linux) | | :warning: ctrl+p | Toggle profiling mode | | escape | Cancel current action. For example, drop currently dragged connection. | +| cmd+shift+t | Terminate the program execution | +| cmd+shift+r | Re-execute the program | #### Navigation diff --git a/app/gui/src/controller/graph/executed.rs b/app/gui/src/controller/graph/executed.rs index 194352776787..f509eb593762 100644 --- a/app/gui/src/controller/graph/executed.rs +++ b/app/gui/src/controller/graph/executed.rs @@ -279,6 +279,18 @@ impl Handle { Ok(()) } + /// Interrupt the program execution. + pub async fn interrupt(&self) -> FallibleResult { + self.execution_ctx.interrupt().await?; + Ok(()) + } + + /// Restart the program execution. + pub async fn restart(&self) -> FallibleResult { + self.execution_ctx.restart().await?; + Ok(()) + } + /// Get the current call stack frames. pub fn call_stack(&self) -> Vec { self.execution_ctx.stack_items().collect() diff --git a/app/gui/src/model/execution_context.rs b/app/gui/src/model/execution_context.rs index 8ec2cefae3ce..c37926853c99 100644 --- a/app/gui/src/model/execution_context.rs +++ b/app/gui/src/model/execution_context.rs @@ -493,6 +493,14 @@ pub trait API: Debug { let detach_actions = visualizations.into_iter().map(move |v| self.detach_visualization(v)); futures::future::join_all(detach_actions).boxed_local() } + + /// Interrupt the program execution. + #[allow(clippy::needless_lifetimes)] // Note: Needless lifetimes + fn interrupt<'a>(&'a self) -> BoxFuture<'a, FallibleResult>; + + /// Restart the program execution. + #[allow(clippy::needless_lifetimes)] // Note: Needless lifetimes + fn restart<'a>(&'a self) -> BoxFuture<'a, FallibleResult>; } // Note: Needless lifetimes diff --git a/app/gui/src/model/execution_context/plain.rs b/app/gui/src/model/execution_context/plain.rs index 50b486ad9a2f..8d888d775444 100644 --- a/app/gui/src/model/execution_context/plain.rs +++ b/app/gui/src/model/execution_context/plain.rs @@ -250,6 +250,14 @@ impl model::execution_context::API for ExecutionContext { Err(InvalidVisualizationId(visualization_id).into()) } } + + fn interrupt(&self) -> BoxFuture { + futures::future::ready(Ok(())).boxed_local() + } + + fn restart(&self) -> BoxFuture { + futures::future::ready(Ok(())).boxed_local() + } } diff --git a/app/gui/src/model/execution_context/synchronized.rs b/app/gui/src/model/execution_context/synchronized.rs index f5802b7b7da4..7f5b768bffd9 100644 --- a/app/gui/src/model/execution_context/synchronized.rs +++ b/app/gui/src/model/execution_context/synchronized.rs @@ -289,6 +289,22 @@ impl model::execution_context::API for ExecutionContext { debug!("Dispatching visualization update through the context {}", self.id()); self.model.dispatch_visualization_update(visualization_id, data) } + + fn interrupt(&self) -> BoxFuture { + async move { + self.language_server.client.interrupt(&self.id).await?; + Ok(()) + } + .boxed_local() + } + + fn restart(&self) -> BoxFuture { + async move { + self.language_server.client.recompute(&self.id).await?; + Ok(()) + } + .boxed_local() + } } impl Drop for ExecutionContext { diff --git a/app/gui/src/presenter/project.rs b/app/gui/src/presenter/project.rs index f2208f9f840c..0cc9befb5b77 100644 --- a/app/gui/src/presenter/project.rs +++ b/app/gui/src/presenter/project.rs @@ -177,6 +177,24 @@ impl Model { } }) } + + fn execution_context_interrupt(&self) { + let controller = self.graph_controller.clone_ref(); + executor::global::spawn(async move { + if let Err(err) = controller.interrupt().await { + error!("Error interrupting execution context: {err}"); + } + }) + } + + fn execution_context_restart(&self) { + let controller = self.graph_controller.clone_ref(); + executor::global::spawn(async move { + if let Err(err) = controller.restart().await { + error!("Error restarting execution context: {err}"); + } + }) + } } @@ -246,6 +264,10 @@ impl Project { view.values_updated <+ values_computed; eval_ view.save_project_snapshot(model.save_project_snapshot()); + + eval_ view.execution_context_interrupt(model.execution_context_interrupt()); + + eval_ view.execution_context_restart(model.execution_context_restart()); } let graph_controller = self.model.graph_controller.clone_ref(); diff --git a/app/gui/view/src/project.rs b/app/gui/view/src/project.rs index d35b1cd15e44..08caae4682b3 100644 --- a/app/gui/view/src/project.rs +++ b/app/gui/view/src/project.rs @@ -84,6 +84,10 @@ ensogl::define_endpoints! { disable_debug_mode(), /// A set of value updates has been processed and rendered. values_updated(), + /// Interrupt the running program. + execution_context_interrupt(), + /// Restart the program execution. + execution_context_restart(), } Output { @@ -834,6 +838,8 @@ impl application::View for View { (Press, "", "cmd y", "redo"), (Press, "!debug_mode", DEBUG_MODE_SHORTCUT, "enable_debug_mode"), (Press, "debug_mode", DEBUG_MODE_SHORTCUT, "disable_debug_mode"), + (Press, "", "cmd shift t", "execution_context_interrupt"), + (Press, "", "cmd shift r", "execution_context_restart"), ] .iter() .map(|(a, b, c, d)| Self::self_shortcut_when(*a, *c, *d, *b))