From b9d696dd8a0fdf312c2d42bee8e0306d87c4150e Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Thu, 8 Dec 2022 14:42:11 +0300 Subject: [PATCH 1/5] feat: interrupt shortcut --- .../controller/engine-protocol/src/language_server.rs | 4 ++++ app/gui/src/controller/graph/executed.rs | 10 +++++----- app/gui/src/model/execution_context.rs | 4 ++++ app/gui/src/model/execution_context/plain.rs | 4 ++++ app/gui/src/model/execution_context/synchronized.rs | 8 ++++++++ app/gui/src/presenter/project.rs | 11 +++++++++++ app/gui/view/src/project.rs | 3 +++ 7 files changed, 39 insertions(+), 5 deletions(-) diff --git a/app/gui/controller/engine-protocol/src/language_server.rs b/app/gui/controller/engine-protocol/src/language_server.rs index c2df69c858b0..6857d0321a8c 100644 --- a/app/gui/controller/engine-protocol/src/language_server.rs +++ b/app/gui/controller/engine-protocol/src/language_server.rs @@ -151,6 +151,10 @@ trait API { fn modify_visualisation (&self, visualisation_id: Uuid, visualisation_config: VisualisationConfiguration) -> (); + /// Interrupt execution of running program. + #[MethodInput=InterruptInput, rpc_name="executionContext/interrupt"] + fn interrupt(&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/src/controller/graph/executed.rs b/app/gui/src/controller/graph/executed.rs index 194352776787..dd46b566ee24 100644 --- a/app/gui/src/controller/graph/executed.rs +++ b/app/gui/src/controller/graph/executed.rs @@ -75,17 +75,17 @@ pub enum Notification { #[derive(Clone, CloneRef, Debug)] pub struct Handle { #[allow(missing_docs)] - pub logger: Logger, + pub logger: Logger, /// A handle to basic graph operations. - graph: Rc>, + graph: Rc>, /// Execution Context handle, its call stack top contains `graph`'s definition. - execution_ctx: model::ExecutionContext, + pub execution_ctx: model::ExecutionContext, /// The handle to project controller is necessary, as entering nodes might need to switch /// modules, and only the project can provide their controllers. - project: model::Project, + project: model::Project, /// The publisher allowing sending notification to subscribed entities. Note that its outputs /// is merged with publishers from the stored graph and execution controllers. - notifier: crate::notification::Publisher, + notifier: crate::notification::Publisher, } impl Handle { diff --git a/app/gui/src/model/execution_context.rs b/app/gui/src/model/execution_context.rs index 8ec2cefae3ce..ec1ff2943b46 100644 --- a/app/gui/src/model/execution_context.rs +++ b/app/gui/src/model/execution_context.rs @@ -493,6 +493,10 @@ 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>; } // 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..6d53f08871f0 100644 --- a/app/gui/src/model/execution_context/plain.rs +++ b/app/gui/src/model/execution_context/plain.rs @@ -250,6 +250,10 @@ impl model::execution_context::API for ExecutionContext { Err(InvalidVisualizationId(visualization_id).into()) } } + + fn interrupt(&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..b92b18094140 100644 --- a/app/gui/src/model/execution_context/synchronized.rs +++ b/app/gui/src/model/execution_context/synchronized.rs @@ -289,6 +289,14 @@ 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() + } } impl Drop for ExecutionContext { diff --git a/app/gui/src/presenter/project.rs b/app/gui/src/presenter/project.rs index f2208f9f840c..41101f7591e6 100644 --- a/app/gui/src/presenter/project.rs +++ b/app/gui/src/presenter/project.rs @@ -177,6 +177,15 @@ impl Model { } }) } + + fn execution_context_interrupt(&self) { + let controller = self.graph_controller.clone_ref(); + executor::global::spawn(async move { + if let Err(err) = controller.execution_ctx.interrupt().await { + error!("Error interrupting execution context: {err}"); + } + }) + } } @@ -246,6 +255,8 @@ 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()); } 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..dffff82dcb94 100644 --- a/app/gui/view/src/project.rs +++ b/app/gui/view/src/project.rs @@ -84,6 +84,8 @@ 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(), } Output { @@ -834,6 +836,7 @@ 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 p", "execution_context_interrupt"), ] .iter() .map(|(a, b, c, d)| Self::self_shortcut_when(*a, *c, *d, *b)) From d5c97efc142d591d8fd2183f4f03881ae07ac6dc Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Thu, 8 Dec 2022 16:00:14 +0300 Subject: [PATCH 2/5] feat: restart shortcut --- .../controller/engine-protocol/src/language_server.rs | 6 +++++- app/gui/src/model/execution_context.rs | 4 ++++ app/gui/src/model/execution_context/plain.rs | 4 ++++ app/gui/src/model/execution_context/synchronized.rs | 8 ++++++++ app/gui/src/presenter/project.rs | 11 +++++++++++ app/gui/view/src/project.rs | 3 +++ 6 files changed, 35 insertions(+), 1 deletion(-) diff --git a/app/gui/controller/engine-protocol/src/language_server.rs b/app/gui/controller/engine-protocol/src/language_server.rs index 6857d0321a8c..0536c2d63c10 100644 --- a/app/gui/controller/engine-protocol/src/language_server.rs +++ b/app/gui/controller/engine-protocol/src/language_server.rs @@ -151,10 +151,14 @@ trait API { fn modify_visualisation (&self, visualisation_id: Uuid, visualisation_config: VisualisationConfiguration) -> (); - /// Interrupt execution of running program. + /// 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/src/model/execution_context.rs b/app/gui/src/model/execution_context.rs index ec1ff2943b46..c37926853c99 100644 --- a/app/gui/src/model/execution_context.rs +++ b/app/gui/src/model/execution_context.rs @@ -497,6 +497,10 @@ pub trait API: Debug { /// 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 6d53f08871f0..8d888d775444 100644 --- a/app/gui/src/model/execution_context/plain.rs +++ b/app/gui/src/model/execution_context/plain.rs @@ -254,6 +254,10 @@ impl model::execution_context::API for ExecutionContext { 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 b92b18094140..7f5b768bffd9 100644 --- a/app/gui/src/model/execution_context/synchronized.rs +++ b/app/gui/src/model/execution_context/synchronized.rs @@ -297,6 +297,14 @@ impl model::execution_context::API for ExecutionContext { } .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 41101f7591e6..fddad7c0e418 100644 --- a/app/gui/src/presenter/project.rs +++ b/app/gui/src/presenter/project.rs @@ -186,6 +186,15 @@ impl Model { } }) } + + fn execution_context_restart(&self) { + let controller = self.graph_controller.clone_ref(); + executor::global::spawn(async move { + if let Err(err) = controller.execution_ctx.restart().await { + error!("Error restarting execution context: {err}"); + } + }) + } } @@ -257,6 +266,8 @@ impl Project { 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 dffff82dcb94..1328f5417447 100644 --- a/app/gui/view/src/project.rs +++ b/app/gui/view/src/project.rs @@ -86,6 +86,8 @@ ensogl::define_endpoints! { values_updated(), /// Interrupt the running program. execution_context_interrupt(), + /// Restart the program execution. + execution_context_restart(), } Output { @@ -837,6 +839,7 @@ impl application::View for View { (Press, "!debug_mode", DEBUG_MODE_SHORTCUT, "enable_debug_mode"), (Press, "debug_mode", DEBUG_MODE_SHORTCUT, "disable_debug_mode"), (Press, "", "cmd shift p", "execution_context_interrupt"), + (Press, "", "cmd shift l", "execution_context_restart"), ] .iter() .map(|(a, b, c, d)| Self::self_shortcut_when(*a, *c, *d, *b)) From 0f6ac89c867b1ff8373db3c188ed07aa101bfcf1 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Fri, 9 Dec 2022 18:27:53 +0300 Subject: [PATCH 3/5] doc: update shortcuts --- app/gui/docs/product/shortcuts.md | 2 ++ app/gui/view/src/project.rs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) 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/view/src/project.rs b/app/gui/view/src/project.rs index 1328f5417447..08caae4682b3 100644 --- a/app/gui/view/src/project.rs +++ b/app/gui/view/src/project.rs @@ -838,8 +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 p", "execution_context_interrupt"), - (Press, "", "cmd shift l", "execution_context_restart"), + (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)) From 18b9e6d50d614549696fa023d8e87f53c9a1fd84 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Mon, 12 Dec 2022 19:33:40 +0300 Subject: [PATCH 4/5] misc: changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a2bc8408a5d..16dc81bd6fe9 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) @@ -399,6 +400,7 @@ [3841]: https://github.com/enso-org/enso/pull/3841 [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 From 7a350738377b75ecba1f336cef781c27699c3cb8 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Mon, 12 Dec 2022 20:13:06 +0300 Subject: [PATCH 5/5] fix: hide execution_ctx --- app/gui/src/controller/graph/executed.rs | 22 +++++++++++++++++----- app/gui/src/presenter/project.rs | 4 ++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/app/gui/src/controller/graph/executed.rs b/app/gui/src/controller/graph/executed.rs index dd46b566ee24..f509eb593762 100644 --- a/app/gui/src/controller/graph/executed.rs +++ b/app/gui/src/controller/graph/executed.rs @@ -75,17 +75,17 @@ pub enum Notification { #[derive(Clone, CloneRef, Debug)] pub struct Handle { #[allow(missing_docs)] - pub logger: Logger, + pub logger: Logger, /// A handle to basic graph operations. - graph: Rc>, + graph: Rc>, /// Execution Context handle, its call stack top contains `graph`'s definition. - pub execution_ctx: model::ExecutionContext, + execution_ctx: model::ExecutionContext, /// The handle to project controller is necessary, as entering nodes might need to switch /// modules, and only the project can provide their controllers. - project: model::Project, + project: model::Project, /// The publisher allowing sending notification to subscribed entities. Note that its outputs /// is merged with publishers from the stored graph and execution controllers. - notifier: crate::notification::Publisher, + notifier: crate::notification::Publisher, } impl Handle { @@ -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/presenter/project.rs b/app/gui/src/presenter/project.rs index fddad7c0e418..0cc9befb5b77 100644 --- a/app/gui/src/presenter/project.rs +++ b/app/gui/src/presenter/project.rs @@ -181,7 +181,7 @@ impl Model { fn execution_context_interrupt(&self) { let controller = self.graph_controller.clone_ref(); executor::global::spawn(async move { - if let Err(err) = controller.execution_ctx.interrupt().await { + if let Err(err) = controller.interrupt().await { error!("Error interrupting execution context: {err}"); } }) @@ -190,7 +190,7 @@ impl Model { fn execution_context_restart(&self) { let controller = self.graph_controller.clone_ref(); executor::global::spawn(async move { - if let Err(err) = controller.execution_ctx.restart().await { + if let Err(err) = controller.restart().await { error!("Error restarting execution context: {err}"); } })