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))