From de6809a59d2d85d9c29c874a3ea3345ce100bd8b Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Fri, 12 Aug 2022 11:06:18 +0300 Subject: [PATCH 01/25] feat: method pointer vis --- .../double-representation/src/module.rs | 8 ++ .../src/language_server/types.rs | 6 +- app/gui/src/controller/graph/executed.rs | 11 ++- app/gui/src/model/execution_context.rs | 85 ++++++++++++++++--- app/gui/src/model/execution_context/plain.rs | 20 ++--- .../model/execution_context/synchronized.rs | 7 +- .../presenter/graph/visualization/manager.rs | 20 +++-- .../src/builtin/visualization/native/error.rs | 9 +- .../src/component/visualization/instance.rs | 27 ++++-- app/gui/view/graph-editor/src/data.rs | 3 + 10 files changed, 145 insertions(+), 51 deletions(-) diff --git a/app/gui/controller/double-representation/src/module.rs b/app/gui/controller/double-representation/src/module.rs index da61ceb46974..9219bd892c02 100644 --- a/app/gui/controller/double-representation/src/module.rs +++ b/app/gui/controller/double-representation/src/module.rs @@ -325,6 +325,14 @@ impl TryFrom for QualifiedName { } } +impl TryFrom<&String> for QualifiedName { + type Error = failure::Error; + + fn try_from(text: &String) -> Result { + Self::from_text(text) + } +} + impl TryFrom for QualifiedName { type Error = failure::Error; diff --git a/app/gui/controller/engine-protocol/src/language_server/types.rs b/app/gui/controller/engine-protocol/src/language_server/types.rs index 7b622d66ede2..8d5ae2d69ecc 100644 --- a/app/gui/controller/engine-protocol/src/language_server/types.rs +++ b/app/gui/controller/engine-protocol/src/language_server/types.rs @@ -651,10 +651,8 @@ pub type ExpressionId = Uuid; pub struct VisualisationConfiguration { #[allow(missing_docs)] pub execution_context_id: ContextId, - /// A qualified name of the module containing the expression which creates visualisation. - pub visualisation_module: String, - /// An enso lambda that will transform the data into expected format, i.e. `a -> a.json`. - pub expression: String, + /// An enso function that will transform the data into expected format. + pub expression: MethodPointer, } /// Used to enter deeper in the execution context stack. In general, all consequent stack items diff --git a/app/gui/src/controller/graph/executed.rs b/app/gui/src/controller/graph/executed.rs index 8970845934b8..7b0f373ede1a 100644 --- a/app/gui/src/controller/graph/executed.rs +++ b/app/gui/src/controller/graph/executed.rs @@ -9,6 +9,7 @@ use crate::prelude::*; use crate::model::execution_context::ComponentGroup; use crate::model::execution_context::ComputedValueInfoRegistry; use crate::model::execution_context::LocalCall; +use crate::model::execution_context::QualifiedMethodPointer; use crate::model::execution_context::Visualization; use crate::model::execution_context::VisualizationId; use crate::model::execution_context::VisualizationUpdateData; @@ -142,10 +143,9 @@ impl Handle { pub fn modify_visualization( &self, id: VisualizationId, - expression: Option, - module: Option, + method_pointer: Option, ) -> BoxFuture { - self.execution_ctx.modify_visualization(id, expression, module) + self.execution_ctx.modify_visualization(id, method_pointer) } /// See [`model::ExecutionContext::detach_visualization`]. @@ -171,10 +171,9 @@ impl Handle { pub async fn set_visualization_preprocessor( &self, id: VisualizationId, - code: String, - module: model::module::QualifiedName, + method_pointer: QualifiedMethodPointer, ) -> FallibleResult { - self.execution_ctx.modify_visualization(id, Some(code), Some(module)).await + self.execution_ctx.modify_visualization(id, Some(method_pointer)).await } /// See [`model::ExecutionContext::component_groups`]. diff --git a/app/gui/src/model/execution_context.rs b/app/gui/src/model/execution_context.rs index e437bd21ee46..c569d1f306f2 100644 --- a/app/gui/src/model/execution_context.rs +++ b/app/gui/src/model/execution_context.rs @@ -2,7 +2,7 @@ use crate::prelude::*; -use crate::model::module::QualifiedName as ModuleQualifiedName; +use crate::model::module; use crate::model::suggestion_database::entry as suggestion; use crate::notification::Publisher; @@ -216,6 +216,73 @@ pub struct LocalCall { +// ===================== +// === Visualization === +// ===================== + +#[derive(Clone, Debug, PartialEq)] +pub struct QualifiedMethodPointer { + pub module: module::QualifiedName, + pub defined_on_type: module::QualifiedName, + pub name: String, +} + +impl QualifiedMethodPointer { + pub fn new( + module: module::QualifiedName, + defined_on_type: module::QualifiedName, + name: String, + ) -> QualifiedMethodPointer { + QualifiedMethodPointer { module, defined_on_type, name } + } + + pub fn from_unqualified( + module: &str, + defined_on_type: &str, + name: &str, + ) -> FallibleResult { + let resolved_module = module.try_into()?; + let resolved_type = defined_on_type.try_into()?; + Ok(QualifiedMethodPointer::new(resolved_module, resolved_type, name.to_owned())) + } +} + +impl TryFrom for QualifiedMethodPointer { + type Error = failure::Error; + + fn try_from(method_pointer: MethodPointer) -> Result { + Self::try_from(&method_pointer) + } +} + +impl TryFrom<&MethodPointer> for QualifiedMethodPointer { + type Error = failure::Error; + + fn try_from(method_pointer: &MethodPointer) -> Result { + let module = method_pointer.module.as_str().try_into()?; + let defined_on_type = method_pointer.defined_on_type.as_str().try_into()?; + let name = method_pointer.name.clone(); + Ok(QualifiedMethodPointer { module, defined_on_type, name }) + } +} + +impl From for MethodPointer { + fn from(qualified_method_pointer: QualifiedMethodPointer) -> Self { + Self::from(&qualified_method_pointer) + } +} + +impl From<&QualifiedMethodPointer> for MethodPointer { + fn from(qualified_method_pointer: &QualifiedMethodPointer) -> Self { + let module = qualified_method_pointer.module.clone().into(); + let defined_on_type = qualified_method_pointer.defined_on_type.clone().into(); + let name = qualified_method_pointer.name.clone(); + MethodPointer { module, defined_on_type, name } + } +} + + + // ===================== // === Visualization === // ===================== @@ -232,8 +299,8 @@ pub struct Visualization { pub expression_id: ExpressionId, /// An enso lambda that will transform the data into expected format, e.g. `a -> a.json`. pub preprocessor_code: String, - /// Visualization module -- the module in which context the preprocessor code is evaluated. - pub context_module: ModuleQualifiedName, + /// A pointer to the enso method that will transform the data into expected format. + pub method_pointer: QualifiedMethodPointer, } impl Visualization { @@ -242,17 +309,16 @@ impl Visualization { pub fn new( expression_id: ExpressionId, preprocessor_code: String, - context_module: ModuleQualifiedName, + method_pointer: QualifiedMethodPointer, ) -> Visualization { let id = VisualizationId::new_v4(); - Visualization { id, expression_id, preprocessor_code, context_module } + Visualization { id, expression_id, preprocessor_code, method_pointer } } /// Creates a `VisualisationConfiguration` that is used in communication with language server. pub fn config(&self, execution_context_id: Uuid) -> VisualisationConfiguration { - let expression = self.preprocessor_code.clone(); - let visualisation_module = self.context_module.to_string(); - VisualisationConfiguration { execution_context_id, visualisation_module, expression } + let expression = self.method_pointer.clone().into(); + VisualisationConfiguration { execution_context_id, expression } } } @@ -391,8 +457,7 @@ pub trait API: Debug { fn modify_visualization<'a>( &'a self, id: VisualizationId, - expression: Option, - module: Option, + method_pointer: Option, ) -> BoxFuture<'a, FallibleResult>; /// Dispatches the visualization update data (typically received from as LS binary notification) diff --git a/app/gui/src/model/execution_context/plain.rs b/app/gui/src/model/execution_context/plain.rs index a51e16cb693a..838681decfe8 100644 --- a/app/gui/src/model/execution_context/plain.rs +++ b/app/gui/src/model/execution_context/plain.rs @@ -6,17 +6,16 @@ use crate::model::execution_context::AttachedVisualization; use crate::model::execution_context::ComponentGroup; use crate::model::execution_context::ComputedValueInfoRegistry; use crate::model::execution_context::LocalCall; +use crate::model::execution_context::QualifiedMethodPointer; use crate::model::execution_context::Visualization; use crate::model::execution_context::VisualizationId; use crate::model::execution_context::VisualizationUpdateData; -use crate::model::module; use engine_protocol::language_server::MethodPointer; use engine_protocol::language_server::VisualisationConfiguration; use futures::future::LocalBoxFuture; - // ============== // === Errors === // ============== @@ -134,18 +133,16 @@ impl ExecutionContext { pub fn modify_visualization( &self, id: VisualizationId, - expression: Option, - module: Option, + method_pointer: Option, ) -> FallibleResult { let err = || InvalidVisualizationId(id); let mut visualizations = self.visualizations.borrow_mut(); let visualization = &mut visualizations.get_mut(&id).ok_or_else(err)?.visualization; - if let Some(expression) = expression { - visualization.preprocessor_code = expression; - } - if let Some(module) = module { - visualization.context_module = module; + + if let Some(method_pointer) = method_pointer { + visualization.method_pointer = method_pointer; } + Ok(()) } @@ -227,10 +224,9 @@ impl model::execution_context::API for ExecutionContext { fn modify_visualization( &self, id: VisualizationId, - expression: Option, - module: Option, + method_pointer: Option, ) -> BoxFuture { - futures::future::ready(self.modify_visualization(id, expression, module)).boxed_local() + futures::future::ready(self.modify_visualization(id, method_pointer)).boxed_local() } fn dispatch_visualization_update( diff --git a/app/gui/src/model/execution_context/synchronized.rs b/app/gui/src/model/execution_context/synchronized.rs index de4569843aa2..01b3c0cbfece 100644 --- a/app/gui/src/model/execution_context/synchronized.rs +++ b/app/gui/src/model/execution_context/synchronized.rs @@ -5,10 +5,10 @@ use crate::prelude::*; use crate::model::execution_context::ComponentGroup; use crate::model::execution_context::ComputedValueInfoRegistry; use crate::model::execution_context::LocalCall; +use crate::model::execution_context::QualifiedMethodPointer; use crate::model::execution_context::Visualization; use crate::model::execution_context::VisualizationId; use crate::model::execution_context::VisualizationUpdateData; -use crate::model::module; use engine_protocol::language_server; @@ -269,10 +269,9 @@ impl model::execution_context::API for ExecutionContext { fn modify_visualization( &self, id: VisualizationId, - expression: Option, - module: Option, + method_pointer: Option, ) -> BoxFuture { - let result = self.model.modify_visualization(id, expression, module); + let result = self.model.modify_visualization(id, method_pointer); let new_config = self.model.visualization_config(id, self.id); async move { result?; diff --git a/app/gui/src/presenter/graph/visualization/manager.rs b/app/gui/src/presenter/graph/visualization/manager.rs index 5839264887c0..e6e25f4d07ab 100644 --- a/app/gui/src/presenter/graph/visualization/manager.rs +++ b/app/gui/src/presenter/graph/visualization/manager.rs @@ -4,6 +4,7 @@ use crate::prelude::*; use crate::controller::ExecutedGraph; use crate::executor::global::spawn; +use crate::model::execution_context::QualifiedMethodPointer; use crate::model::execution_context::Visualization; use crate::model::execution_context::VisualizationId; use crate::model::execution_context::VisualizationUpdateData; @@ -361,12 +362,17 @@ impl Manager { fn prepare_visualization(&self, desired: Desired) -> FallibleResult { let context_module = desired.metadata.preprocessor.module; - let resolved_module = self.resolve_context_module(&context_module)?; + let preprocessor_method = desired.metadata.preprocessor.method; + let method_pointer = QualifiedMethodPointer::from_unqualified( + &context_module, + &context_module, + &preprocessor_method, + )?; Ok(Visualization { - id: desired.visualization_id, - expression_id: desired.expression_id, + id: desired.visualization_id, + expression_id: desired.expression_id, preprocessor_code: desired.metadata.preprocessor.code.to_string(), - context_module: resolved_module, + method_pointer, }) } @@ -502,10 +508,8 @@ impl Manager { Status::BeingModified { from: so_far.clone(), to: new_visualization.clone() }; self.update_status(target, status); let id = so_far.id; - let expression = new_visualization.preprocessor_code.clone(); - let module = new_visualization.context_module.clone(); - let modifying_result = - self.executed_graph.modify_visualization(id, Some(expression), Some(module)); + let method_pointer = new_visualization.method_pointer.clone(); + let modifying_result = self.executed_graph.modify_visualization(id, Some(method_pointer)); match modifying_result.await { Ok(_) => { let status = Status::Attached(new_visualization); diff --git a/app/gui/view/graph-editor/src/builtin/visualization/native/error.rs b/app/gui/view/graph-editor/src/builtin/visualization/native/error.rs index f6563d4496b7..fca8414ecdd8 100644 --- a/app/gui/view/graph-editor/src/builtin/visualization/native/error.rs +++ b/app/gui/view/graph-editor/src/builtin/visualization/native/error.rs @@ -42,9 +42,16 @@ pub const PREPROCESSOR_CODE: &str = include_str!("inc/error_preprocessor.enso"); // RuntimeVisualisationsTest.scala, used to verify the snippet's correctness pub const PREPROCESSOR_MODULE: &str = "Standard.Base.Main"; +/// The method name containing the `PREPROCESSOR_CODE`. +pub const PREPROCESSOR_METHOD: &str = "error_preprocessor"; + /// Get preprocessor configuration for error visualization. pub fn preprocessor() -> instance::PreprocessorConfiguration { - instance::PreprocessorConfiguration::new(PREPROCESSOR_CODE, PREPROCESSOR_MODULE) + instance::PreprocessorConfiguration::new( + PREPROCESSOR_CODE, + PREPROCESSOR_MODULE, + PREPROCESSOR_METHOD, + ) } /// Get metadata description for error visualization. diff --git a/app/gui/view/graph-editor/src/component/visualization/instance.rs b/app/gui/view/graph-editor/src/component/visualization/instance.rs index 908ff2b74dd0..e0e6e976c8dd 100644 --- a/app/gui/view/graph-editor/src/component/visualization/instance.rs +++ b/app/gui/view/graph-editor/src/component/visualization/instance.rs @@ -18,7 +18,10 @@ use ensogl::display::Scene; /// An invocable language expression that serialize given input into JSON. pub const DEFAULT_VISUALIZATION_EXPRESSION: &str = "x -> x.to_default_visualization_data"; - +/// A module containing the default visualization function. +pub const DEFAULT_VISUALIZATION_MODULE: &str = "Default_Visualization"; +/// A name of the default visualization function. +pub const DEFAULT_VISUALIZATION_FUNCTION: &str = "default_preprocessor"; // ==================== @@ -61,7 +64,9 @@ pub struct PreprocessorConfiguration { /// that visualizations expect. pub code: enso::Code, /// The module that provides context for `code` evaluation. - pub module: ContextModule, + pub module: enso::Module, + /// The method being invoked. + pub method: enso::Function, } impl PreprocessorConfiguration { @@ -69,13 +74,17 @@ impl PreprocessorConfiguration { pub fn from_options( code: Option>, module: Option>, + method: Option>, ) -> Self { let mut ret = Self::default(); if let Some(code) = code { - ret.code = code.into() + ret.code = code.into(); } if let Some(module) = module { - ret.module = ContextModule::Specific(module.into()) + ret.module = module.into(); + } + if let Some(method) = method { + ret.method = method.into(); } ret } @@ -84,17 +93,23 @@ impl PreprocessorConfiguration { pub fn new( code: impl Into, module: impl Into, + method: impl Into, ) -> PreprocessorConfiguration { PreprocessorConfiguration { - module: ContextModule::Specific(module.into()), code: code.into(), + module: module.into(), + method: method.into(), } } } impl Default for PreprocessorConfiguration { fn default() -> Self { - Self { code: DEFAULT_VISUALIZATION_EXPRESSION.into(), module: default() } + Self { + code: DEFAULT_VISUALIZATION_EXPRESSION.into(), + module: DEFAULT_VISUALIZATION_MODULE.into(), + method: DEFAULT_VISUALIZATION_FUNCTION.into(), + } } } diff --git a/app/gui/view/graph-editor/src/data.rs b/app/gui/view/graph-editor/src/data.rs index fd4c23dc889c..a5e4929871d1 100644 --- a/app/gui/view/graph-editor/src/data.rs +++ b/app/gui/view/graph-editor/src/data.rs @@ -20,6 +20,9 @@ pub mod enso { /// The Enso type representation. Can be a complex type, like `String|Int`. Type, + /// The Enso function name, like `foo` or `my_cool_method`. + Function, + /// The Enso module represented as qualified path, like `Project.Data.Vector`. Module, } From 396e3b49dcdf04fc4b19faafb3c3c90c2b251bc7 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Mon, 15 Aug 2022 09:30:46 +0300 Subject: [PATCH 02/25] feat: default method pointer visualization --- app/gui/src/model/execution_context.rs | 6 ++++ .../presenter/graph/visualization/manager.rs | 11 ++----- .../foreign/java_script/binding.rs | 9 ++++-- .../foreign/java_script/visualization.js | 4 ++- .../src/component/visualization/instance.rs | 30 +++++++++---------- .../Visualization/0.0.0-dev/src/Main.enso | 1 + .../0.0.0-dev/src/Preprocessor.enso | 3 ++ 7 files changed, 37 insertions(+), 27 deletions(-) create mode 100644 distribution/lib/Standard/Visualization/0.0.0-dev/src/Preprocessor.enso diff --git a/app/gui/src/model/execution_context.rs b/app/gui/src/model/execution_context.rs index c569d1f306f2..c812e68edfe7 100644 --- a/app/gui/src/model/execution_context.rs +++ b/app/gui/src/model/execution_context.rs @@ -220,14 +220,19 @@ pub struct LocalCall { // === Visualization === // ===================== +/// A method pointer containing the qualified module and type names. #[derive(Clone, Debug, PartialEq)] pub struct QualifiedMethodPointer { + /// A module name containing the method. pub module: module::QualifiedName, + /// A type on which the method is defined. pub defined_on_type: module::QualifiedName, + /// A method name. pub name: String, } impl QualifiedMethodPointer { + /// Creates a new method pointer from its components. pub fn new( module: module::QualifiedName, defined_on_type: module::QualifiedName, @@ -236,6 +241,7 @@ impl QualifiedMethodPointer { QualifiedMethodPointer { module, defined_on_type, name } } + /// Tries to create a new method pointer from string components. pub fn from_unqualified( module: &str, defined_on_type: &str, diff --git a/app/gui/src/presenter/graph/visualization/manager.rs b/app/gui/src/presenter/graph/visualization/manager.rs index e6e25f4d07ab..25c16e1106cb 100644 --- a/app/gui/src/presenter/graph/visualization/manager.rs +++ b/app/gui/src/presenter/graph/visualization/manager.rs @@ -353,20 +353,13 @@ impl Manager { } } - fn resolve_context_module( - &self, - context_module: &ContextModule, - ) -> FallibleResult { - resolve_context_module(context_module, || self.project.main_module()) - } - fn prepare_visualization(&self, desired: Desired) -> FallibleResult { let context_module = desired.metadata.preprocessor.module; - let preprocessor_method = desired.metadata.preprocessor.method; + let preprocessor_function = desired.metadata.preprocessor.function; let method_pointer = QualifiedMethodPointer::from_unqualified( &context_module, &context_module, - &preprocessor_method, + &preprocessor_function, )?; Ok(Visualization { id: desired.visualization_id, diff --git a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs index c6c64f0f3be2..41dc13d7a0fa 100644 --- a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs +++ b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs @@ -197,9 +197,14 @@ impl JsConsArgs { } /// Helper method to emit an preprocessor change event from the visualisation. - pub fn emit_preprocessor_change(&self, code: Option, module: Option) { + pub fn emit_preprocessor_change( + &self, + code: Option, + module: Option, + fun: Option, + ) { let closure = &self.set_preprocessor; - let preprocessor_config = PreprocessorConfiguration::from_options(code, module); + let preprocessor_config = PreprocessorConfiguration::from_options(code, module, fun); (*closure)(preprocessor_config); } } diff --git a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js index 95100458a88e..cb6a30dd0ae5 100644 --- a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js +++ b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js @@ -17,6 +17,7 @@ export class Visualization { // invoke `__emitPreprocessorChange__()` on this. this.__preprocessorCode__ = null this.__preprocessorModule__ = null + this.__preprocessorFunction__ = null this.dom = api.root() this.theme = api.theme() @@ -30,7 +31,8 @@ export class Visualization { __emitPreprocessorChange__() { this.__api__.emit_preprocessor_change( this.__preprocessorCode__, - this.__preprocessorModule__ + this.__preprocessorModule__, + this.__preprocessorFunction__ ) } diff --git a/app/gui/view/graph-editor/src/component/visualization/instance.rs b/app/gui/view/graph-editor/src/component/visualization/instance.rs index e0e6e976c8dd..9a1ac8164c7f 100644 --- a/app/gui/view/graph-editor/src/component/visualization/instance.rs +++ b/app/gui/view/graph-editor/src/component/visualization/instance.rs @@ -19,7 +19,7 @@ use ensogl::display::Scene; /// An invocable language expression that serialize given input into JSON. pub const DEFAULT_VISUALIZATION_EXPRESSION: &str = "x -> x.to_default_visualization_data"; /// A module containing the default visualization function. -pub const DEFAULT_VISUALIZATION_MODULE: &str = "Default_Visualization"; +pub const DEFAULT_VISUALIZATION_MODULE: &str = "Standard.Visualization.Preprocessor"; /// A name of the default visualization function. pub const DEFAULT_VISUALIZATION_FUNCTION: &str = "default_preprocessor"; @@ -62,11 +62,11 @@ impl ContextModule { pub struct PreprocessorConfiguration { /// The code of the preprocessor. Should be a lambda that transforms node value into whatever /// that visualizations expect. - pub code: enso::Code, + pub code: enso::Code, /// The module that provides context for `code` evaluation. - pub module: enso::Module, - /// The method being invoked. - pub method: enso::Function, + pub module: enso::Module, + /// The function being invoked. + pub function: enso::Function, } impl PreprocessorConfiguration { @@ -74,7 +74,7 @@ impl PreprocessorConfiguration { pub fn from_options( code: Option>, module: Option>, - method: Option>, + function: Option>, ) -> Self { let mut ret = Self::default(); if let Some(code) = code { @@ -83,8 +83,8 @@ impl PreprocessorConfiguration { if let Some(module) = module { ret.module = module.into(); } - if let Some(method) = method { - ret.method = method.into(); + if let Some(function) = function { + ret.function = function.into(); } ret } @@ -93,12 +93,12 @@ impl PreprocessorConfiguration { pub fn new( code: impl Into, module: impl Into, - method: impl Into, + function: impl Into, ) -> PreprocessorConfiguration { PreprocessorConfiguration { - code: code.into(), - module: module.into(), - method: method.into(), + code: code.into(), + module: module.into(), + function: function.into(), } } } @@ -106,9 +106,9 @@ impl PreprocessorConfiguration { impl Default for PreprocessorConfiguration { fn default() -> Self { Self { - code: DEFAULT_VISUALIZATION_EXPRESSION.into(), - module: DEFAULT_VISUALIZATION_MODULE.into(), - method: DEFAULT_VISUALIZATION_FUNCTION.into(), + code: DEFAULT_VISUALIZATION_EXPRESSION.into(), + module: DEFAULT_VISUALIZATION_MODULE.into(), + function: DEFAULT_VISUALIZATION_FUNCTION.into(), } } } diff --git a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Main.enso index ca07210e5167..1857d0c13c65 100644 --- a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Main.enso @@ -2,6 +2,7 @@ from Standard.Base import all import Standard.Visualization.File_Upload import Standard.Visualization.Id +import Standard.Visualization.Preprocessor from Standard.Visualization.File_Upload export file_uploading export Standard.Visualization.Id diff --git a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Preprocessor.enso b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Preprocessor.enso new file mode 100644 index 000000000000..0ae396224703 --- /dev/null +++ b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Preprocessor.enso @@ -0,0 +1,3 @@ +from Standard.Base import all + +default_preprocessor x = x.to_default_visualization_data From bd73e1c5a41713384224d4c39ad5170e52b23264 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Thu, 18 Aug 2022 17:21:57 +0300 Subject: [PATCH 03/25] update js --- app/gui/docs/product/visualizations.md | 111 ++++++++---------- app/gui/src/model/execution_context.rs | 26 ++-- app/gui/src/presenter/graph/visualization.rs | 6 +- .../presenter/graph/visualization/manager.rs | 11 +- .../view/debug_scene/visualization/src/lib.rs | 2 +- .../visualization/java_script/geoMap.js | 2 +- .../visualization/java_script/heatmap.js | 3 +- .../visualization/java_script/histogram.js | 13 +- .../visualization/java_script/scatterPlot.js | 4 +- .../builtin/visualization/java_script/sql.js | 3 +- .../visualization/java_script/table.js | 3 +- .../src/builtin/visualization/native/error.rs | 19 +-- .../native/inc/error_preprocessor.enso | 6 - .../foreign/java_script/binding.rs | 9 +- .../foreign/java_script/visualization.js | 91 +++----------- .../src/component/visualization/instance.rs | 19 +-- .../0.0.0-dev/src/Preprocessor.enso | 13 ++ .../0.0.0-dev/src/Table/Visualization.enso | 2 +- .../RuntimeVisualizationsTest.scala | 26 ++-- 19 files changed, 120 insertions(+), 249 deletions(-) delete mode 100644 app/gui/view/graph-editor/src/builtin/visualization/native/inc/error_preprocessor.enso diff --git a/app/gui/docs/product/visualizations.md b/app/gui/docs/product/visualizations.md index 4080fe0f574c..3584867ad504 100644 --- a/app/gui/docs/product/visualizations.md +++ b/app/gui/docs/product/visualizations.md @@ -11,22 +11,22 @@ tags: [product] Visualizations have two main purposes: -- **Display results of nodes** - Each node can be assigned with one or more visualization. After a node - computes its new value, the visualization shows it in an understandable way to - the user. Please note that a single node can be assigned with multiple - visualizations at the same time. For example, a node might want to display a - map of locations, and their list at the same time next to each other. - -- **Provide interactive way to generate new data** - In a widget mode (described in detail later), visualizations provide users - with an interactive GUI to define data. For example, a map visualization can - both display locations, as well as allowing the user to pick locations by - clicking with a mouse. Similarly, the histogram can both display a list of - numbers, and can be manually draw with the mouse producing such a list. - Several numbers can be visualized as a table of sliders, which can also be - used to interactively generate a table of numbers. Image visualizations can - behave like an image editor, etc. +- **Display results of nodes** Each node can be assigned with one or more + visualization. After a node computes its new value, the visualization shows it + in an understandable way to the user. Please note that a single node can be + assigned with multiple visualizations at the same time. For example, a node + might want to display a map of locations, and their list at the same time next + to each other. + +- **Provide interactive way to generate new data** In a widget mode (described + in detail later), visualizations provide users with an interactive GUI to + define data. For example, a map visualization can both display locations, as + well as allowing the user to pick locations by clicking with a mouse. + Similarly, the histogram can both display a list of numbers, and can be + manually draw with the mouse producing such a list. Several numbers can be + visualized as a table of sliders, which can also be used to interactively + generate a table of numbers. Image visualizations can behave like an image + editor, etc. ## Visualization Display Forms @@ -37,34 +37,33 @@ Visualizations can be displayed in the following ways: you move the node, the visualization moves as well. This mode can be toggled by tapping the spacebar. -- **Fullscreen** - Visualization attached to node can grow (animate) to ocupy full IDE visual - space. This mode can be triggered on the recently selected node (in case many - nodes are selected, the last selected node will be used) by either pressing - keeping the spacebar pressed for longer than approx 0.5s, or by tapping it - twice. In the former case, the visualization shrinks to each original form - whenever we release space, in the later, whenever we press space again. - -- **Detached** - Visualizations attached to nodes can be detached, scaled, and placed freely - across the visual canvas (we might introduce a special place where you can put - such visualizations). This is useful when defining dashboards or reports. We - also plan to provide a notebook-like experience where you can write text mixed - with visualizations (including widgets for an interactive experience). - -- **Widgets** - In this mode visualizations behave like nodes but do not display expressions. - They have one input and one output port. If the input port is connected, the - visualization displays its value and passes its to the output port. In case it - is not connected, the visualization becomes an interactive widget allowing the - user to specify data. For example, a map visualization will allow the user to - manually pick locations. After each change, the new locations will be sent to - the output port. Under the hood, widgets are represented as nodes and their - code lines are assigned with a dedicated "visualization" metadata. - Visualizations generate expressions always in the form of `name = data`, where - data is a hardcoded data produced from the visualization. For example, when - user clicks the map to define locations, the data could be a string literal - containing locations encoded in JSON. +- **Fullscreen** Visualization attached to node can grow (animate) to ocupy full + IDE visual space. This mode can be triggered on the recently selected node (in + case many nodes are selected, the last selected node will be used) by either + pressing keeping the spacebar pressed for longer than approx 0.5s, or by + tapping it twice. In the former case, the visualization shrinks to each + original form whenever we release space, in the later, whenever we press space + again. + +- **Detached** Visualizations attached to nodes can be detached, scaled, and + placed freely across the visual canvas (we might introduce a special place + where you can put such visualizations). This is useful when defining + dashboards or reports. We also plan to provide a notebook-like experience + where you can write text mixed with visualizations (including widgets for an + interactive experience). + +- **Widgets** In this mode visualizations behave like nodes but do not display + expressions. They have one input and one output port. If the input port is + connected, the visualization displays its value and passes its to the output + port. In case it is not connected, the visualization becomes an interactive + widget allowing the user to specify data. For example, a map visualization + will allow the user to manually pick locations. After each change, the new + locations will be sent to the output port. Under the hood, widgets are + represented as nodes and their code lines are assigned with a dedicated + "visualization" metadata. Visualizations generate expressions always in the + form of `name = data`, where data is a hardcoded data produced from the + visualization. For example, when user clicks the map to define locations, the + data could be a string literal containing locations encoded in JSON. ### Choosing a Visualization Type. @@ -201,24 +200,11 @@ In particular: as visualizations. The superclass defines a default constructor and a set of utilities: - - #### Method `setPreprocessorCode(code)` - Set an Enso code which will be evaluated on the server-side before sending - data to visualization. If not called, a default unspecified code is used + - #### Method `setPreprocessor(module,method)` + Set an Enso method which will be evaluated on the server-side before sending + data to visualization. If not called, a default unspecified method is used that will provide some JSON representation of the value. See [Lazy visualizations](#lazy-visualizations) section for details. - - #### Method `setPreprocessorModule(module)` - Define in which module's context the preprocessor code should be evaluated. - If not called, the `Main` module of the project that defines visualization - will be used. See [Lazy visualizations](#lazy-visualizations) section for - details. - - #### Method `setPreprocessor(code,mode)` - Set both code and its module context at once. If both need to be updated, - using this method can save an update processing and needless evaluation. - Note that using both `setPreprocessorCode` and `setPreprocessorModule` from - the visualization's custom constructor will not cause any unnecessary - updates, as the preprocessor is applied only after visualization is fully - constructed. See [Lazy visualizations](#lazy-visualizations) section for - details. - #### Field `dom` It is initialized in the constructor to the DOM symbol used to host the visualization content. Users are free to modify the DOM element, including @@ -297,9 +283,8 @@ visualization you may use the `setPreprocessor` method). This code defines an Enso function, which will be run by the compiler on data the visualization is attached to. Only the results of this code will be sent to the GUI. In the case of the JSON input format, the result of the call should be a valid JSON string. -The code will be evaluated in the context of the `Main` module in the project -where visualization is defined - you may use any symbol defined or imported in -that module. +The code will be evaluated in the context of the module where the preprocessor +method is defined - you may use any symbol defined or imported in that module. For example, imagine you want to display a heatmap of 10 million points on a map, and these points change rapidly. Sending such an amount of information via diff --git a/app/gui/src/model/execution_context.rs b/app/gui/src/model/execution_context.rs index c812e68edfe7..3020ecdfb7ee 100644 --- a/app/gui/src/model/execution_context.rs +++ b/app/gui/src/model/execution_context.rs @@ -232,15 +232,6 @@ pub struct QualifiedMethodPointer { } impl QualifiedMethodPointer { - /// Creates a new method pointer from its components. - pub fn new( - module: module::QualifiedName, - defined_on_type: module::QualifiedName, - name: String, - ) -> QualifiedMethodPointer { - QualifiedMethodPointer { module, defined_on_type, name } - } - /// Tries to create a new method pointer from string components. pub fn from_unqualified( module: &str, @@ -249,7 +240,11 @@ impl QualifiedMethodPointer { ) -> FallibleResult { let resolved_module = module.try_into()?; let resolved_type = defined_on_type.try_into()?; - Ok(QualifiedMethodPointer::new(resolved_module, resolved_type, name.to_owned())) + Ok(QualifiedMethodPointer { + module: resolved_module, + defined_on_type: resolved_type, + name: name.to_owned(), + }) } } @@ -300,13 +295,11 @@ pub type VisualizationId = Uuid; #[derive(Clone, Debug, PartialEq)] pub struct Visualization { /// Unique identifier of this visualization. - pub id: VisualizationId, + pub id: VisualizationId, /// Expression that is to be visualized. - pub expression_id: ExpressionId, - /// An enso lambda that will transform the data into expected format, e.g. `a -> a.json`. - pub preprocessor_code: String, + pub expression_id: ExpressionId, /// A pointer to the enso method that will transform the data into expected format. - pub method_pointer: QualifiedMethodPointer, + pub method_pointer: QualifiedMethodPointer, } impl Visualization { @@ -314,11 +307,10 @@ impl Visualization { /// identifier. pub fn new( expression_id: ExpressionId, - preprocessor_code: String, method_pointer: QualifiedMethodPointer, ) -> Visualization { let id = VisualizationId::new_v4(); - Visualization { id, expression_id, preprocessor_code, method_pointer } + Visualization { id, expression_id, method_pointer } } /// Creates a `VisualisationConfiguration` that is used in communication with language server. diff --git a/app/gui/src/presenter/graph/visualization.rs b/app/gui/src/presenter/graph/visualization.rs index 244ec1436250..9bbcdbb38081 100644 --- a/app/gui/src/presenter/graph/visualization.rs +++ b/app/gui/src/presenter/graph/visualization.rs @@ -181,10 +181,8 @@ impl Visualization { let network = frp::Network::new("presenter::graph::Visualization"); let controller = project.visualization().clone_ref(); - let (manager, notifications) = - Manager::new(&logger, graph.clone_ref(), project.clone_ref()); - let (error_manager, error_notifications) = - Manager::new(&logger, graph.clone_ref(), project); + let (manager, notifications) = Manager::new(&logger, graph.clone_ref()); + let (error_manager, error_notifications) = Manager::new(&logger, graph.clone_ref()); let model = Rc::new(Model { logger, controller, diff --git a/app/gui/src/presenter/graph/visualization/manager.rs b/app/gui/src/presenter/graph/visualization/manager.rs index 25c16e1106cb..177e40553996 100644 --- a/app/gui/src/presenter/graph/visualization/manager.rs +++ b/app/gui/src/presenter/graph/visualization/manager.rs @@ -226,7 +226,6 @@ pub struct Manager { logger: Logger, visualizations: SharedHashMap, executed_graph: ExecutedGraph, - project: model::Project, notification_sender: futures::channel::mpsc::UnboundedSender, } @@ -238,17 +237,10 @@ impl Manager { pub fn new( logger: impl AnyLogger, executed_graph: ExecutedGraph, - project: model::Project, ) -> (Rc, UnboundedReceiver) { let logger = logger.sub("visualization::Manager"); let (notification_sender, notification_receiver) = futures::channel::mpsc::unbounded(); - let ret = Self { - logger, - visualizations: default(), - executed_graph, - project, - notification_sender, - }; + let ret = Self { logger, visualizations: default(), executed_graph, notification_sender }; (Rc::new(ret), notification_receiver) } @@ -364,7 +356,6 @@ impl Manager { Ok(Visualization { id: desired.visualization_id, expression_id: desired.expression_id, - preprocessor_code: desired.metadata.preprocessor.code.to_string(), method_pointer, }) } diff --git a/app/gui/view/debug_scene/visualization/src/lib.rs b/app/gui/view/debug_scene/visualization/src/lib.rs index 7701ab4d9e28..77b9cb5a29a5 100644 --- a/app/gui/view/debug_scene/visualization/src/lib.rs +++ b/app/gui/view/debug_scene/visualization/src/lib.rs @@ -55,7 +55,7 @@ fn constructor_graph() -> visualization::java_script::Definition { console.log("pressed",e); }) } - this.setPreprocessor(`x ->\n IO.println "Preprocessor set after receiving ${data}`) + // this.setPreprocessor(`x ->\n IO.println "Preprocessor set after receiving ${data}`) let first = data.shift(); if (first) { diff --git a/app/gui/view/graph-editor/src/builtin/visualization/java_script/geoMap.js b/app/gui/view/graph-editor/src/builtin/visualization/java_script/geoMap.js index 441265242aba..47537031a5f6 100644 --- a/app/gui/view/graph-editor/src/builtin/visualization/java_script/geoMap.js +++ b/app/gui/view/graph-editor/src/builtin/visualization/java_script/geoMap.js @@ -104,7 +104,7 @@ class GeoMapVisualization extends Visualization { this.initMapElement() this.initStyle() this.dataPoints = [] - this.setPreprocessor('process_to_json_text', 'Standard.Visualization.Geo_Map') + this.setPreprocessor('Standard.Visualization.Geo_Map', 'process_to_json_text') } initMapElement() { diff --git a/app/gui/view/graph-editor/src/builtin/visualization/java_script/heatmap.js b/app/gui/view/graph-editor/src/builtin/visualization/java_script/heatmap.js index 47e3aecc308b..481480c5e23c 100644 --- a/app/gui/view/graph-editor/src/builtin/visualization/java_script/heatmap.js +++ b/app/gui/view/graph-editor/src/builtin/visualization/java_script/heatmap.js @@ -12,8 +12,7 @@ class Heatmap extends Visualization { constructor(data) { super(data) - this.setPreprocessorModule('Standard.Visualization.Table.Visualization') - this.setPreprocessorCode(`x -> prepare_visualization x 1000`) + this.setPreprocessor('Standard.Visualization.Table.Visualization', 'prepare_visualization') } onDataReceived(data) { diff --git a/app/gui/view/graph-editor/src/builtin/visualization/java_script/histogram.js b/app/gui/view/graph-editor/src/builtin/visualization/java_script/histogram.js index 77c05f88b179..662450c7f186 100644 --- a/app/gui/view/graph-editor/src/builtin/visualization/java_script/histogram.js +++ b/app/gui/view/graph-editor/src/builtin/visualization/java_script/histogram.js @@ -6,7 +6,6 @@ loadStyle('https://fontlibrary.org/face/dejavu-sans-mono') let shortcuts = { zoomIn: e => (e.ctrlKey || e.metaKey) && e.key === 'z', showAll: e => (e.ctrlKey || e.metaKey) && e.key === 'a', - debugPreprocessor: e => (e.ctrlKey || e.metaKey) && e.key === 'd', } const LABEL_STYLE = 'font-family: DejaVuSansMonoBook; font-size: 10px;' @@ -44,7 +43,7 @@ class Histogram extends Visualization { constructor(data) { super(data) - this.setPreprocessor('process_to_json_text', 'Standard.Visualization.Histogram') + this.setPreprocessor('Standard.Visualization.Histogram', 'process_to_json_text') this._dataBins = [] } @@ -59,7 +58,6 @@ class Histogram extends Visualization { this.initCanvas() this.initLabels() this.initHistogram() - this.initDebugShortcut() } this.updateLabels() @@ -252,15 +250,6 @@ class Histogram extends Visualization { this.initBrushing(selectedZoomBtn, zoom) } - initDebugShortcut() { - document.addEventListener('keydown', e => { - if (shortcuts.debugPreprocessor(e)) { - this.setPreprocessor('x -> "[1,2,3,4]"') - e.preventDefault() - } - }) - } - /** * Initialise panning and zooming functionality on the visualization. */ diff --git a/app/gui/view/graph-editor/src/builtin/visualization/java_script/scatterPlot.js b/app/gui/view/graph-editor/src/builtin/visualization/java_script/scatterPlot.js index 27f0c79b6a0f..d569461d3809 100644 --- a/app/gui/view/graph-editor/src/builtin/visualization/java_script/scatterPlot.js +++ b/app/gui/view/graph-editor/src/builtin/visualization/java_script/scatterPlot.js @@ -49,7 +49,7 @@ class ScatterPlot extends Visualization { constructor(data) { super(data) - this.setPreprocessor('process_to_json_text', 'Standard.Visualization.Scatter_Plot') + this.setPreprocessor('Standard.Visualization.Scatter_Plot', 'process_to_json_text') this.dataPoints = [] this.axis = { x: { scale: LINEAR_SCALE }, @@ -726,7 +726,7 @@ class ScatterPlot extends Visualization { addStyleToElem( 'button', ` - margin-left: 5px; + margin-left: 5px; margin-bottom: 5px; display: inline-block; padding: 2px 10px; diff --git a/app/gui/view/graph-editor/src/builtin/visualization/java_script/sql.js b/app/gui/view/graph-editor/src/builtin/visualization/java_script/sql.js index 027f4af5c321..d3245c75b5c9 100644 --- a/app/gui/view/graph-editor/src/builtin/visualization/java_script/sql.js +++ b/app/gui/view/graph-editor/src/builtin/visualization/java_script/sql.js @@ -87,8 +87,7 @@ class SqlVisualization extends Visualization { constructor(api) { super(api) - this.setPreprocessorModule('Standard.Visualization.Sql.Visualization') - this.setPreprocessorCode(`x -> prepare_visualization x`) + this.setPreprocessor('Standard.Visualization.Sql.Visualization', 'prepare_visualization') } onDataReceived(data) { diff --git a/app/gui/view/graph-editor/src/builtin/visualization/java_script/table.js b/app/gui/view/graph-editor/src/builtin/visualization/java_script/table.js index 438f0303805e..abedfc401f49 100644 --- a/app/gui/view/graph-editor/src/builtin/visualization/java_script/table.js +++ b/app/gui/view/graph-editor/src/builtin/visualization/java_script/table.js @@ -16,8 +16,7 @@ class TableVisualization extends Visualization { constructor(data) { super(data) - this.setPreprocessorModule('Standard.Visualization.Table.Visualization') - this.setPreprocessorCode(`x -> prepare_visualization x 1000`) + this.setPreprocessor('Standard.Visualization.Table.Visualization', 'prepare_visualization') } onDataReceived(data) { diff --git a/app/gui/view/graph-editor/src/builtin/visualization/native/error.rs b/app/gui/view/graph-editor/src/builtin/visualization/native/error.rs index fca8414ecdd8..0eef9dc73af8 100644 --- a/app/gui/view/graph-editor/src/builtin/visualization/native/error.rs +++ b/app/gui/view/graph-editor/src/builtin/visualization/native/error.rs @@ -31,27 +31,20 @@ pub use crate::component::node::error::Kind; // ================= const PADDING_TEXT: f32 = 10.0; -/// The Error Visualization preprocessor. See also _Lazy Visualization_ section -/// [here](http://dev.enso.org/docs/ide/product/visualizations.html). + +/// The module containing the `PREPROCESSOR_FUNCTION`. See there. // NOTE: contents of this const need to be kept in sync with Scala test in // RuntimeVisualisationsTest.scala, used to verify the snippet's correctness -pub const PREPROCESSOR_CODE: &str = include_str!("inc/error_preprocessor.enso"); +const PREPROCESSOR_MODULE: &str = "Standard.Visualization.Preprocessor"; -/// The context module for the `PREPROCESSOR_CODE`. See there. +/// The method name of the error preprocessor. // NOTE: contents of this const need to be kept in sync with Scala test in // RuntimeVisualisationsTest.scala, used to verify the snippet's correctness -pub const PREPROCESSOR_MODULE: &str = "Standard.Base.Main"; - -/// The method name containing the `PREPROCESSOR_CODE`. -pub const PREPROCESSOR_METHOD: &str = "error_preprocessor"; +const PREPROCESSOR_METHOD: &str = "error_preprocessor"; /// Get preprocessor configuration for error visualization. pub fn preprocessor() -> instance::PreprocessorConfiguration { - instance::PreprocessorConfiguration::new( - PREPROCESSOR_CODE, - PREPROCESSOR_MODULE, - PREPROCESSOR_METHOD, - ) + instance::PreprocessorConfiguration::new(PREPROCESSOR_MODULE, PREPROCESSOR_METHOD) } /// Get metadata description for error visualization. diff --git a/app/gui/view/graph-editor/src/builtin/visualization/native/inc/error_preprocessor.enso b/app/gui/view/graph-editor/src/builtin/visualization/native/inc/error_preprocessor.enso deleted file mode 100644 index 19b795954478..000000000000 --- a/app/gui/view/graph-editor/src/builtin/visualization/native/inc/error_preprocessor.enso +++ /dev/null @@ -1,6 +0,0 @@ -x -> - ok = '{ message: ""}' - result = x.map_error err-> - message = err.to_display_text - '{ "kind": "Dataflow", "message": ' + message.to_json.to_text + '}' - if result.is_error then result.catch else ok diff --git a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs index 41dc13d7a0fa..1792134476d3 100644 --- a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs +++ b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs @@ -197,14 +197,9 @@ impl JsConsArgs { } /// Helper method to emit an preprocessor change event from the visualisation. - pub fn emit_preprocessor_change( - &self, - code: Option, - module: Option, - fun: Option, - ) { + pub fn emit_preprocessor_change(&self, module: Option, fun: Option) { let closure = &self.set_preprocessor; - let preprocessor_config = PreprocessorConfiguration::from_options(code, module, fun); + let preprocessor_config = PreprocessorConfiguration::from_options(module, fun); (*closure)(preprocessor_config); } } diff --git a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js index cb6a30dd0ae5..89cbc27979ca 100644 --- a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js +++ b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js @@ -15,9 +15,8 @@ export class Visualization { // These go before `api` assignment so the `undefined` is not emitted to IDE. // First we will give deriving type a chance to overwrite them, then IDE will // invoke `__emitPreprocessorChange__()` on this. - this.__preprocessorCode__ = null this.__preprocessorModule__ = null - this.__preprocessorFunction__ = null + this.__preprocessorMethod__ = null this.dom = api.root() this.theme = api.theme() @@ -30,57 +29,25 @@ export class Visualization { */ __emitPreprocessorChange__() { this.__api__.emit_preprocessor_change( - this.__preprocessorCode__, this.__preprocessorModule__, - this.__preprocessorFunction__ + this.__preprocessorMethod__ ) } /** - * Get the current preprocessor code. See {@link setPreprocessorCode} for - * more information about purpose of setting preprocessor code. + * Get the current preprocessor method. See {@link setPreprocessor} for + * more information about purpose of setting the preprocessor. * - * @returns {string} Preprocessor code or `null` if no code was set. + * @returns {string} Preprocessor method or `null` if no method was set. */ - getPreprocessorCode() { - return this.__preprocessorCode__ - } - - /** - * Set new preprocessor code. - * - * When visualization is attached to a node, each time a new value is produced from node, - * the preprocessor shall be invoked with it. Result such call shall be serialized and - * transported to visualization by invoking onDataReceived(data) method. - * - * Typically the preprocessor is a lambda, like the example below: - * - * `x -> x.to_default_visualization_data` - * - * - * The code by default runs in the context of the current project's `Main` module. - * If other context is needed (e.g. due to required import or other module-specific - * context dependency), the {@link setPreprocessorModule} should be used to provide - * the module's name. - * - * Please refer to [documentation]{@link https://dev.enso.org/docs/ide/product/visualizations.html#lazy-visualizations} - * for details. - * - * @param {string} code text code in Enso. It must be invokable with one argument and return - * JSON-compatible result. For example: - *
x -> x.to_default_visualization_data
- */ - setPreprocessorCode(code) { - if (code !== this.__preprocessorCode__) { - this.__preprocessorCode__ = code - this.__emitPreprocessorChange__() - } + getPreprocessorMethod() { + return this.__preprocessorMethod__ } /** * Get the current preprocessor's context module. * - * See the [setter documentation]{@link setPreprocessorModule} for more information. + * See the [setter documentation]{@link setPreprocessor} for more information. * * @returns {string} Qualified name to preprocessor's context module. */ @@ -89,43 +56,19 @@ export class Visualization { } /** - * Set preprocessor's context module. - * - * [Preprocessor code]{@link setPreprocessorCode} is executed in the context of - * certain Enso module. This decides what symbols are visible and available to - * preprocessor, as everything that preprocessor uses must defined or imported - * in the context module. - * - * If never set, Engine will use the current project's `Main` module as the context. - * - * @param module - */ - setPreprocessorModule(module) { - if (module !== this.__preprocessorModule__) { - this.__preprocessorModule__ = module - this.__emitPreprocessorChange__() - } else { - console.error('skipping, as', module, ' === ', this.__preprocessorModule__) - } - } - - /** - * Set both preprocessor's code and context module. - * - * This is like calling both {@link setPreprocessorModule} and - * {@link setPreprocessorCode}, however may be more efficient, as it will emit - * only one update request. + * Set the preprocessor. * - * During the visualization construction phase no partial updates are emitted, - * so using this method gives no additional benefit. + * Sets the preprocessor method by providing the method pointer consisting of a + * method name and a module name defining the preprocessor method. * - * @param code preprocessor code to be set. - * @param module context module for the preprocessor execution. + * @param module module containing the preprocessor method. + * @param method the preprocessor method name. The method must be invocable + with one argument and return JSON-compatible result. */ - setPreprocessor(code, module) { - if (code !== this.__preprocessorCode__ || code !== this.__preprocessorModule__) { - this.__preprocessorCode__ = code + setPreprocessor(module, method) { + if (module !== this.__preprocessorModule__ || method !== this.__preprocessorMethod__) { this.__preprocessorModule__ = module + this.__preprocessorMethod__ = method this.__emitPreprocessorChange__() } } diff --git a/app/gui/view/graph-editor/src/component/visualization/instance.rs b/app/gui/view/graph-editor/src/component/visualization/instance.rs index 9a1ac8164c7f..38d3050937c3 100644 --- a/app/gui/view/graph-editor/src/component/visualization/instance.rs +++ b/app/gui/view/graph-editor/src/component/visualization/instance.rs @@ -16,8 +16,6 @@ use ensogl::display::Scene; // === Constants === // ================= -/// An invocable language expression that serialize given input into JSON. -pub const DEFAULT_VISUALIZATION_EXPRESSION: &str = "x -> x.to_default_visualization_data"; /// A module containing the default visualization function. pub const DEFAULT_VISUALIZATION_MODULE: &str = "Standard.Visualization.Preprocessor"; /// A name of the default visualization function. @@ -60,10 +58,7 @@ impl ContextModule { /// Information on how the preprocessor should be set up for the visualization. #[derive(Clone, CloneRef, Debug, PartialEq, Eq)] pub struct PreprocessorConfiguration { - /// The code of the preprocessor. Should be a lambda that transforms node value into whatever - /// that visualizations expect. - pub code: enso::Code, - /// The module that provides context for `code` evaluation. + /// The module containing the `function`. pub module: enso::Module, /// The function being invoked. pub function: enso::Function, @@ -72,14 +67,10 @@ pub struct PreprocessorConfiguration { impl PreprocessorConfiguration { /// Like `new` but arguments are optional. If `None` is given, default value will be used. pub fn from_options( - code: Option>, module: Option>, function: Option>, ) -> Self { let mut ret = Self::default(); - if let Some(code) = code { - ret.code = code.into(); - } if let Some(module) = module { ret.module = module.into(); } @@ -91,22 +82,16 @@ impl PreprocessorConfiguration { /// Create a configuration that runs the given code in the context of the given module. pub fn new( - code: impl Into, module: impl Into, function: impl Into, ) -> PreprocessorConfiguration { - PreprocessorConfiguration { - code: code.into(), - module: module.into(), - function: function.into(), - } + PreprocessorConfiguration { module: module.into(), function: function.into() } } } impl Default for PreprocessorConfiguration { fn default() -> Self { Self { - code: DEFAULT_VISUALIZATION_EXPRESSION.into(), module: DEFAULT_VISUALIZATION_MODULE.into(), function: DEFAULT_VISUALIZATION_FUNCTION.into(), } diff --git a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Preprocessor.enso b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Preprocessor.enso index 0ae396224703..862f4118d5ec 100644 --- a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Preprocessor.enso +++ b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Preprocessor.enso @@ -1,3 +1,16 @@ from Standard.Base import all +## PRIVATE + + Default visualization preprocessor. default_preprocessor x = x.to_default_visualization_data + +## PRIVATE + + Error visualization preprocessor. +error_preprocessor x = + ok = '{ message: ""}' + result = x.map_error err-> + message = err.to_display_text + '{ "kind": "Dataflow", "message": ' + message.to_json.to_text + '}' + if result.is_error then result.catch else ok diff --git a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Table/Visualization.enso b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Table/Visualization.enso index 2c3f04ca9049..514250267762 100644 --- a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Table/Visualization.enso +++ b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Table/Visualization.enso @@ -19,7 +19,7 @@ import Standard.Visualization.Helpers In case of Database backed data, it materializes a fragment of the data. prepare_visualization : Any -> Integer -> Json -prepare_visualization x max_rows = Helpers.recover_errors <| case x of +prepare_visualization x max_rows=1000 = Helpers.recover_errors <| case x of Dataframe_Table.Table _ -> dataframe = x.take_start max_rows all_rows_count = x.row_count diff --git a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala index f703a8366fe3..b90dc686cc2e 100644 --- a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala +++ b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala @@ -20,8 +20,6 @@ import java.io.{ByteArrayOutputStream, File} import java.nio.file.{Files, Path, Paths} import java.util.UUID -import scala.io.Source - @scala.annotation.nowarn("msg=multiarg infix syntax") class RuntimeVisualizationsTest extends AnyFlatSpec @@ -1921,10 +1919,11 @@ class RuntimeVisualizationsTest val moduleName = "Enso_Test.Test.Main" val metadata = new Metadata - val idMain = metadata.addItem(86, 28) + val idMain = metadata.addItem(116, 28) val code = """import Standard.Base.Data.List + |import Standard.Visualization |from Standard.Base.Error.Common import all | |main = @@ -1934,9 +1933,8 @@ class RuntimeVisualizationsTest val mainFile = context.writeMain(contents) // NOTE: below values need to be kept in sync with what is used internally by Rust IDE code - val visualisationModule = "Standard.Base.Main" - val visualisationCode = - Source.fromResource("error_preprocessor.enso").mkString + val visualisationModule = "Standard.Visualization.Preprocessor" + val visualisationFunction = "error_preprocessor" // create context context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) @@ -1959,7 +1957,7 @@ class RuntimeVisualizationsTest context.send( Api.Request(requestId, Api.PushContextRequest(contextId, item1)) ) - val pushContextResponses = context.receiveN(4) + val pushContextResponses = context.receiveNIgnoreStdLib(3) pushContextResponses should contain allOf ( Api.Response(requestId, Api.PushContextResponse(contextId)), TestMessages.error( @@ -1969,11 +1967,6 @@ class RuntimeVisualizationsTest ), context.executionComplete(contextId) ) - val loadedLibraries = pushContextResponses.collect { - case Api.Response(None, Api.LibraryLoaded(namespace, name, _, _)) => - (namespace, name) - } - loadedLibraries should contain(("Standard", "Base")) // attach visualisation context.send( @@ -1984,9 +1977,12 @@ class RuntimeVisualizationsTest idMain, Api.VisualisationConfiguration( contextId, - Api.VisualisationExpression.Text( - visualisationModule, - visualisationCode + Api.VisualisationExpression.ModuleMethod( + Api.MethodPointer( + visualisationModule, + visualisationModule, + visualisationFunction + ) ) ) ) From af3d9343b4a67839d9e3678f87af790ab660a6d6 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Fri, 19 Aug 2022 16:30:09 +0300 Subject: [PATCH 04/25] fix: tests --- app/gui/src/model/execution_context.rs | 4 + .../model/execution_context/synchronized.rs | 46 ++++++----- .../presenter/graph/visualization/manager.rs | 79 +++++++------------ app/gui/tests/language_server.rs | 31 +++++--- 4 files changed, 80 insertions(+), 80 deletions(-) diff --git a/app/gui/src/model/execution_context.rs b/app/gui/src/model/execution_context.rs index 3020ecdfb7ee..1445b72860b2 100644 --- a/app/gui/src/model/execution_context.rs +++ b/app/gui/src/model/execution_context.rs @@ -232,6 +232,10 @@ pub struct QualifiedMethodPointer { } impl QualifiedMethodPointer { + /// Create a method pointer representing a module method. + pub fn module_method(module: module::QualifiedName, name: String) -> QualifiedMethodPointer { + QualifiedMethodPointer { module: module.clone(), defined_on_type: module, name } + } /// Tries to create a new method pointer from string components. pub fn from_unqualified( module: &str, diff --git a/app/gui/src/model/execution_context/synchronized.rs b/app/gui/src/model/execution_context/synchronized.rs index 01b3c0cbfece..5bf303f25839 100644 --- a/app/gui/src/model/execution_context/synchronized.rs +++ b/app/gui/src/model/execution_context/synchronized.rs @@ -448,11 +448,14 @@ pub mod test { #[test] fn attaching_visualizations_and_notifying() { + let method_pointer = QualifiedMethodPointer::module_method( + MockData::new().module_qualified_name(), + "".to_string(), + ); let vis = Visualization { - id: model::execution_context::VisualizationId::new_v4(), - expression_id: model::execution_context::ExpressionId::new_v4(), - preprocessor_code: "".to_string(), - context_module: MockData::new().module_qualified_name(), + id: model::execution_context::VisualizationId::new_v4(), + expression_id: model::execution_context::ExpressionId::new_v4(), + method_pointer, }; let Fixture { mut test, context, .. } = Fixture::new_customized(|ls, data| { let exe_id = data.context_id; @@ -492,11 +495,14 @@ pub mod test { #[ignore] #[test] fn detaching_all_visualizations() { + let method_pointer = QualifiedMethodPointer::module_method( + MockData::new().module_qualified_name(), + "".to_string(), + ); let vis = Visualization { - id: model::execution_context::VisualizationId::new_v4(), - expression_id: model::execution_context::ExpressionId::new_v4(), - preprocessor_code: "".to_string(), - context_module: MockData::new().module_qualified_name(), + id: model::execution_context::VisualizationId::new_v4(), + expression_id: model::execution_context::ExpressionId::new_v4(), + method_pointer, }; let vis2 = Visualization { id: VisualizationId::new_v4(), ..vis.clone() }; @@ -524,14 +530,20 @@ pub mod test { #[test] fn modifying_visualizations() { + let method_pointer = QualifiedMethodPointer::module_method( + MockData::new().module_qualified_name(), + "".to_string(), + ); let vis = Visualization { - id: model::execution_context::VisualizationId::new_v4(), - expression_id: model::execution_context::ExpressionId::new_v4(), - preprocessor_code: "x -> x.to_json.to_string".to_string(), - context_module: MockData::new().module_qualified_name(), + id: model::execution_context::VisualizationId::new_v4(), + expression_id: model::execution_context::ExpressionId::new_v4(), + method_pointer, }; let vis_id = vis.id; - let new_expression = "x -> x"; + let new_expression = QualifiedMethodPointer::module_method( + MockData::new().module_qualified_name(), + "quux".to_string(), + ); let new_module = "Test.Test_Module"; let Fixture { mut test, context, .. } = Fixture::new_customized(|ls, data| { let exe_id = data.context_id; @@ -540,8 +552,7 @@ pub mod test { let expected_config = language_server::types::VisualisationConfiguration { execution_context_id: data.context_id, - visualisation_module: new_module.to_owned(), - expression: new_expression.to_owned(), + expression: new_expression.clone().into(), }; expect_call!(ls.attach_visualisation(vis_id,ast_id,config) => Ok(())); @@ -550,9 +561,8 @@ pub mod test { test.run_task(async move { context.attach_visualization(vis.clone()).await.unwrap(); - let expression = Some(new_expression.to_owned()); - let module = Some(QualifiedName::from_text(new_module).unwrap()); - context.modify_visualization(vis_id, expression, module).await.unwrap(); + let method_pointer = Some(new_expression); + context.modify_visualization(vis_id, method_pointer).await.unwrap(); }); } diff --git a/app/gui/src/presenter/graph/visualization/manager.rs b/app/gui/src/presenter/graph/visualization/manager.rs index 177e40553996..0ef6361306fb 100644 --- a/app/gui/src/presenter/graph/visualization/manager.rs +++ b/app/gui/src/presenter/graph/visualization/manager.rs @@ -12,31 +12,11 @@ use crate::sync::Synchronized; use futures::channel::mpsc::UnboundedReceiver; use futures::future::ready; -use ide_view::graph_editor::component::visualization; -use ide_view::graph_editor::component::visualization::instance::ContextModule; use ide_view::graph_editor::component::visualization::Metadata; use ide_view::graph_editor::SharedHashMap; -// ================================ -// === Resolving Context Module === -// ================================ - -/// Resolve the context module to a fully qualified name. -pub fn resolve_context_module( - context_module: &ContextModule, - main_module_name: impl FnOnce() -> model::module::QualifiedName, -) -> FallibleResult { - use visualization::instance::ContextModule::*; - match context_module { - ProjectMain => Ok(main_module_name()), - Specific(module_name) => model::module::QualifiedName::from_text(module_name), - } -} - - - // ============== // === Errors === // ============== @@ -520,8 +500,8 @@ impl Manager { mod tests { use super::*; + use crate::model::module; use futures::future::ready; - use ide_view::graph_editor::component::visualization::instance::ContextModule; use ide_view::graph_editor::component::visualization::instance::PreprocessorConfiguration; use std::assert_matches::assert_matches; use wasm_bindgen_test::wasm_bindgen_test; @@ -541,11 +521,11 @@ mod tests { Self { inner, node_id } } - fn vis_metadata(&self, code: impl Into) -> Metadata { + fn vis_metadata(&self, function: impl Into) -> Metadata { Metadata { preprocessor: PreprocessorConfiguration { - module: ContextModule::Specific(self.inner.module_name().to_string().into()), - code: code.into().into(), + module: self.inner.module_name().to_string().into(), + function: function.into().into(), }, } } @@ -555,11 +535,7 @@ mod tests { enum ExecutionContextRequest { Attach(Visualization), Detach(VisualizationId), - Modify { - id: VisualizationId, - expression: Option, - module: Option, - }, + Modify { id: VisualizationId, method_pointer: Option }, } #[derive(Shrinkwrap)] @@ -578,12 +554,14 @@ mod tests { impl VisOperationsTester { fn new(inner: Fixture) -> Self { - let faux_vis = Visualization { - id: default(), - expression_id: default(), - context_module: inner.project.qualified_module_name(inner.module.path()), - preprocessor_code: "faux value".into(), + let qualified_module = inner.project.qualified_module_name(inner.module.path()); + let method_pointer = QualifiedMethodPointer { + module: qualified_module.clone(), + defined_on_type: qualified_module, + name: "faux".to_string(), }; + let faux_vis = + Visualization { id: default(), expression_id: default(), method_pointer }; let is_ready = Synchronized::new(false); let mut execution_context = model::execution_context::MockAPI::new(); let (request_sender, requests_receiver) = futures::channel::mpsc::unbounded(); @@ -607,8 +585,8 @@ mod tests { let sender = request_sender; execution_context.expect_modify_visualization().returning_st( - move |id, expression, module| { - let request = ExecutionContextRequest::Modify { id, expression, module }; + move |id, method_pointer| { + let request = ExecutionContextRequest::Modify { id, method_pointer }; sender.unbounded_send(request).unwrap(); ready(Ok(())).boxed_local() }, @@ -621,20 +599,19 @@ mod tests { execution_context, ); let logger: Logger = inner.logger.sub("manager"); - let (manager, notifier) = - Manager::new(logger, executed_graph.clone_ref(), inner.project.clone_ref()); + let (manager, notifier) = Manager::new(logger, executed_graph.clone_ref()); Self { inner, is_ready, manager, notifier, requests } } } fn matching_metadata( - manager: &Manager, visualization: &Visualization, metadata: &Metadata, ) -> bool { - let PreprocessorConfiguration { module, code } = &metadata.preprocessor; - visualization.preprocessor_code == code.to_string() - && visualization.context_module == manager.resolve_context_module(module).unwrap() + let PreprocessorConfiguration { module, function } = &metadata.preprocessor; + let qualified_module: module::QualifiedName = module.deref().try_into().unwrap(); + visualization.method_pointer.module == qualified_module + && visualization.method_pointer.name == function.deref() } #[wasm_bindgen_test] @@ -674,15 +651,15 @@ mod tests { manager.request_visualization(node_id, desired_vis_1.clone()); manager.request_visualization(node_id, desired_vis_1.clone()); inner.run_until_stalled(); - if let ExecutionContextRequest::Modify { id, expression, module } = requests.expect_one() { - assert!(expression.contains(&desired_vis_1.preprocessor.code.to_string())); + if let ExecutionContextRequest::Modify { id, method_pointer } = requests.expect_one() { + let desired_method_pointer = QualifiedMethodPointer::from_unqualified( + &desired_vis_1.preprocessor.module, + &desired_vis_1.preprocessor.module, + &desired_vis_1.preprocessor.function, + ) + .unwrap(); + assert!(method_pointer.contains(&desired_method_pointer)); assert_eq!(id, attached_id); - let get_main_module = || inner.inner.project.main_module(); - let expected_module = - resolve_context_module(&desired_vis_1.preprocessor.module, get_main_module) - .unwrap(); - assert_eq!(module, Some(expected_module)); - // assert!(module.is_none()); } // If visualization changes ID, then we need to use detach-attach API. @@ -704,6 +681,6 @@ mod tests { other => panic!("Expected a detach request, got: {:?}", other), } assert_matches!(requests.expect_next(), ExecutionContextRequest::Attach(vis) - if matching_metadata(&manager,&vis,&desired_vis_3.metadata)); + if matching_metadata(&vis,&desired_vis_3.metadata)); } } diff --git a/app/gui/tests/language_server.rs b/app/gui/tests/language_server.rs index ce09297e7aed..dd85e356a0a0 100644 --- a/app/gui/tests/language_server.rs +++ b/app/gui/tests/language_server.rs @@ -18,6 +18,7 @@ use engine_protocol::language_server::*; use engine_protocol::types::*; use enso_gui::prelude::*; +use enso_gui::model::execution_context::QualifiedMethodPointer; use enso_gui::model::execution_context::Visualization; use enso_gui::model::module; use enso_gui::transport::web::WebSocket; @@ -125,18 +126,26 @@ async fn ls_text_protocol_test() { let visualisation_id = uuid::Uuid::new_v4(); let expression_id = uuid::Uuid::parse_str("c553533e-a2b9-4305-9f12-b8fe7781f933"); let expression_id = expression_id.expect("Couldn't parse expression id."); - let expression = "x -> here.encode x".to_string(); - let visualisation_module = "Test.Visualisation".to_string(); - let visualisation_config = - VisualisationConfiguration { execution_context_id, expression, visualisation_module }; + let visualization_function = "foo".to_string(); + let visualization_module = "Test.Visualisation"; + let expression = MethodPointer { + module: visualization_module.to_string(), + defined_on_type: visualization_module.to_string(), + name: visualization_function, + }; + let visualisation_config = VisualisationConfiguration { execution_context_id, expression }; let response = client.attach_visualisation(&visualisation_id, &expression_id, &visualisation_config); response.await.expect("Couldn't attach visualisation."); - let expression = "x -> here.incAndEncode".to_string(); - let visualisation_module = "Test.Visualisation".to_string(); - let visualisation_config = - VisualisationConfiguration { execution_context_id, expression, visualisation_module }; + let visualization_function = "bar".to_string(); + let visualization_module = "Test.Visualisation"; + let expression = MethodPointer { + module: visualization_module.to_string(), + defined_on_type: visualization_module.to_string(), + name: visualization_function, + }; + let visualisation_config = VisualisationConfiguration { execution_context_id, expression }; let response = client.modify_visualisation(&visualisation_id, &visualisation_config).await; response.expect("Couldn't modify visualisation."); @@ -331,8 +340,6 @@ async fn binary_visualization_updates_test_hlp() { let project = ide.current_project().expect("IDE is configured without an open project."); info!(logger, "Got project: {project:?}"); - let expression = "x -> x.json_serialize".to_owned(); - use controller::project::MAIN_DEFINITION_NAME; use ensogl::system::web::sleep; @@ -354,7 +361,9 @@ async fn binary_visualization_updates_test_hlp() { info!(logger, "The code is: {module.ast().repr():?}"); info!(logger, "Main node: {the_node:?} with {the_node.expression().repr()}"); - let visualization = Visualization::new(the_node.id(), expression, module_qualified_name); + let method_pointer = + QualifiedMethodPointer::module_method(module_qualified_name, "quux".to_string()); + let visualization = Visualization::new(the_node.id(), method_pointer); let stream = graph_executed.attach_visualization(visualization.clone()).await.unwrap(); info!(logger, "Attached the visualization {visualization.id}"); let mut stream = stream.boxed_local(); From 8218bf722f9cb651b484f9a6aed3f3a9c55eee85 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Mon, 22 Aug 2022 11:43:16 +0300 Subject: [PATCH 05/25] cleanup --- app/gui/src/model/execution_context.rs | 6 ++--- .../model/execution_context/synchronized.rs | 1 - .../presenter/graph/visualization/manager.rs | 21 ++++++++---------- .../view/debug_scene/visualization/src/lib.rs | 2 +- .../foreign/java_script/binding.rs | 4 ++-- .../src/component/visualization/instance.rs | 22 +++++++++---------- app/gui/view/graph-editor/src/data.rs | 4 ++-- .../RuntimeVisualizationsTest.scala | 2 +- 8 files changed, 29 insertions(+), 33 deletions(-) diff --git a/app/gui/src/model/execution_context.rs b/app/gui/src/model/execution_context.rs index 1445b72860b2..0056abbd04ec 100644 --- a/app/gui/src/model/execution_context.rs +++ b/app/gui/src/model/execution_context.rs @@ -216,9 +216,9 @@ pub struct LocalCall { -// ===================== -// === Visualization === -// ===================== +// ============================== +// === QualifiedMethodPointer === +// ============================== /// A method pointer containing the qualified module and type names. #[derive(Clone, Debug, PartialEq)] diff --git a/app/gui/src/model/execution_context/synchronized.rs b/app/gui/src/model/execution_context/synchronized.rs index 5bf303f25839..b879eb0ac165 100644 --- a/app/gui/src/model/execution_context/synchronized.rs +++ b/app/gui/src/model/execution_context/synchronized.rs @@ -544,7 +544,6 @@ pub mod test { MockData::new().module_qualified_name(), "quux".to_string(), ); - let new_module = "Test.Test_Module"; let Fixture { mut test, context, .. } = Fixture::new_customized(|ls, data| { let exe_id = data.context_id; let ast_id = vis.expression_id; diff --git a/app/gui/src/presenter/graph/visualization/manager.rs b/app/gui/src/presenter/graph/visualization/manager.rs index 0ef6361306fb..e970ca897a15 100644 --- a/app/gui/src/presenter/graph/visualization/manager.rs +++ b/app/gui/src/presenter/graph/visualization/manager.rs @@ -327,11 +327,11 @@ impl Manager { fn prepare_visualization(&self, desired: Desired) -> FallibleResult { let context_module = desired.metadata.preprocessor.module; - let preprocessor_function = desired.metadata.preprocessor.function; + let preprocessor_method = desired.metadata.preprocessor.method; let method_pointer = QualifiedMethodPointer::from_unqualified( &context_module, &context_module, - &preprocessor_function, + &preprocessor_method, )?; Ok(Visualization { id: desired.visualization_id, @@ -521,11 +521,11 @@ mod tests { Self { inner, node_id } } - fn vis_metadata(&self, function: impl Into) -> Metadata { + fn vis_metadata(&self, method: impl Into) -> Metadata { Metadata { preprocessor: PreprocessorConfiguration { - module: self.inner.module_name().to_string().into(), - function: function.into().into(), + module: self.inner.module_name().to_string().into(), + method: method.into().into(), }, } } @@ -604,14 +604,11 @@ mod tests { } } - fn matching_metadata( - visualization: &Visualization, - metadata: &Metadata, - ) -> bool { - let PreprocessorConfiguration { module, function } = &metadata.preprocessor; + fn matching_metadata(visualization: &Visualization, metadata: &Metadata) -> bool { + let PreprocessorConfiguration { module, method } = &metadata.preprocessor; let qualified_module: module::QualifiedName = module.deref().try_into().unwrap(); visualization.method_pointer.module == qualified_module - && visualization.method_pointer.name == function.deref() + && visualization.method_pointer.name == method.deref() } #[wasm_bindgen_test] @@ -655,7 +652,7 @@ mod tests { let desired_method_pointer = QualifiedMethodPointer::from_unqualified( &desired_vis_1.preprocessor.module, &desired_vis_1.preprocessor.module, - &desired_vis_1.preprocessor.function, + &desired_vis_1.preprocessor.method, ) .unwrap(); assert!(method_pointer.contains(&desired_method_pointer)); diff --git a/app/gui/view/debug_scene/visualization/src/lib.rs b/app/gui/view/debug_scene/visualization/src/lib.rs index 77b9cb5a29a5..a58fbb3e1124 100644 --- a/app/gui/view/debug_scene/visualization/src/lib.rs +++ b/app/gui/view/debug_scene/visualization/src/lib.rs @@ -55,7 +55,7 @@ fn constructor_graph() -> visualization::java_script::Definition { console.log("pressed",e); }) } - // this.setPreprocessor(`x ->\n IO.println "Preprocessor set after receiving ${data}`) + this.setPreprocessor('Standard.Visualization.Preprocessor', 'default_preprocessor'); let first = data.shift(); if (first) { diff --git a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs index 1792134476d3..4c25c4531562 100644 --- a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs +++ b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs @@ -197,9 +197,9 @@ impl JsConsArgs { } /// Helper method to emit an preprocessor change event from the visualisation. - pub fn emit_preprocessor_change(&self, module: Option, fun: Option) { + pub fn emit_preprocessor_change(&self, module: Option, method: Option) { let closure = &self.set_preprocessor; - let preprocessor_config = PreprocessorConfiguration::from_options(module, fun); + let preprocessor_config = PreprocessorConfiguration::from_options(module, method); (*closure)(preprocessor_config); } } diff --git a/app/gui/view/graph-editor/src/component/visualization/instance.rs b/app/gui/view/graph-editor/src/component/visualization/instance.rs index 38d3050937c3..be6561fb4986 100644 --- a/app/gui/view/graph-editor/src/component/visualization/instance.rs +++ b/app/gui/view/graph-editor/src/component/visualization/instance.rs @@ -58,24 +58,24 @@ impl ContextModule { /// Information on how the preprocessor should be set up for the visualization. #[derive(Clone, CloneRef, Debug, PartialEq, Eq)] pub struct PreprocessorConfiguration { - /// The module containing the `function`. - pub module: enso::Module, - /// The function being invoked. - pub function: enso::Function, + /// The module containing the `method`. + pub module: enso::Module, + /// The method being invoked. + pub method: enso::Method, } impl PreprocessorConfiguration { /// Like `new` but arguments are optional. If `None` is given, default value will be used. pub fn from_options( module: Option>, - function: Option>, + method: Option>, ) -> Self { let mut ret = Self::default(); if let Some(module) = module { ret.module = module.into(); } - if let Some(function) = function { - ret.function = function.into(); + if let Some(method) = method { + ret.method = method.into(); } ret } @@ -83,17 +83,17 @@ impl PreprocessorConfiguration { /// Create a configuration that runs the given code in the context of the given module. pub fn new( module: impl Into, - function: impl Into, + method: impl Into, ) -> PreprocessorConfiguration { - PreprocessorConfiguration { module: module.into(), function: function.into() } + PreprocessorConfiguration { module: module.into(), method: method.into() } } } impl Default for PreprocessorConfiguration { fn default() -> Self { Self { - module: DEFAULT_VISUALIZATION_MODULE.into(), - function: DEFAULT_VISUALIZATION_FUNCTION.into(), + module: DEFAULT_VISUALIZATION_MODULE.into(), + method: DEFAULT_VISUALIZATION_FUNCTION.into(), } } } diff --git a/app/gui/view/graph-editor/src/data.rs b/app/gui/view/graph-editor/src/data.rs index a5e4929871d1..a74457d60ffd 100644 --- a/app/gui/view/graph-editor/src/data.rs +++ b/app/gui/view/graph-editor/src/data.rs @@ -20,8 +20,8 @@ pub mod enso { /// The Enso type representation. Can be a complex type, like `String|Int`. Type, - /// The Enso function name, like `foo` or `my_cool_method`. - Function, + /// The Enso method name, like `main` or `my_cool_method`. + Method, /// The Enso module represented as qualified path, like `Project.Data.Vector`. Module, diff --git a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala index b90dc686cc2e..59f02641d8cc 100644 --- a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala +++ b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala @@ -1933,7 +1933,7 @@ class RuntimeVisualizationsTest val mainFile = context.writeMain(contents) // NOTE: below values need to be kept in sync with what is used internally by Rust IDE code - val visualisationModule = "Standard.Visualization.Preprocessor" + val visualisationModule = "Standard.Visualization.Preprocessor" val visualisationFunction = "error_preprocessor" // create context From 6bc6d01e68e792a92e668f7183b977f6173f03e0 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Mon, 22 Aug 2022 11:44:59 +0300 Subject: [PATCH 06/25] Update app/gui/docs/product/visualizations.md Co-authored-by: Jaroslav Tulach --- app/gui/docs/product/visualizations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/gui/docs/product/visualizations.md b/app/gui/docs/product/visualizations.md index 3584867ad504..86b630c8b115 100644 --- a/app/gui/docs/product/visualizations.md +++ b/app/gui/docs/product/visualizations.md @@ -37,7 +37,7 @@ Visualizations can be displayed in the following ways: you move the node, the visualization moves as well. This mode can be toggled by tapping the spacebar. -- **Fullscreen** Visualization attached to node can grow (animate) to ocupy full +- **Fullscreen** Visualization attached to node can grow (animate) to occupy full IDE visual space. This mode can be triggered on the recently selected node (in case many nodes are selected, the last selected node will be used) by either pressing keeping the spacebar pressed for longer than approx 0.5s, or by From 1376ba62206a622483fb052f547ff2dde5d08ec2 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Mon, 22 Aug 2022 11:46:55 +0300 Subject: [PATCH 07/25] run format --- app/gui/docs/product/visualizations.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/gui/docs/product/visualizations.md b/app/gui/docs/product/visualizations.md index 86b630c8b115..0210aca12658 100644 --- a/app/gui/docs/product/visualizations.md +++ b/app/gui/docs/product/visualizations.md @@ -37,11 +37,11 @@ Visualizations can be displayed in the following ways: you move the node, the visualization moves as well. This mode can be toggled by tapping the spacebar. -- **Fullscreen** Visualization attached to node can grow (animate) to occupy full - IDE visual space. This mode can be triggered on the recently selected node (in - case many nodes are selected, the last selected node will be used) by either - pressing keeping the spacebar pressed for longer than approx 0.5s, or by - tapping it twice. In the former case, the visualization shrinks to each +- **Fullscreen** Visualization attached to node can grow (animate) to occupy + full IDE visual space. This mode can be triggered on the recently selected + node (in case many nodes are selected, the last selected node will be used) by + either pressing keeping the spacebar pressed for longer than approx 0.5s, or + by tapping it twice. In the former case, the visualization shrinks to each original form whenever we release space, in the later, whenever we press space again. From 6f44e143f3c3723584ec7187e79e89814df8d6bf Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Tue, 23 Aug 2022 12:26:21 +0300 Subject: [PATCH 08/25] add: runtime support for positional arguments in visualization expressions --- .../org/enso/polyglot/runtime/Runtime.scala | 13 +- .../RuntimeVisualizationsTest.scala | 201 ++++++++++++++++-- .../interpreter/service/ExecutionService.java | 18 +- .../instrument/Visualisation.scala | 3 +- .../job/ProgramExecutionSupport.scala | 4 +- .../job/UpsertVisualisationJob.scala | 59 +++-- 6 files changed, 257 insertions(+), 41 deletions(-) diff --git a/engine/polyglot-api/src/main/scala/org/enso/polyglot/runtime/Runtime.scala b/engine/polyglot-api/src/main/scala/org/enso/polyglot/runtime/Runtime.scala index 18cf1e2be38a..1906c98c66e2 100644 --- a/engine/polyglot-api/src/main/scala/org/enso/polyglot/runtime/Runtime.scala +++ b/engine/polyglot-api/src/main/scala/org/enso/polyglot/runtime/Runtime.scala @@ -515,16 +515,23 @@ object Runtime { /** Visualization expression represented as a module method. * * @param methodPointer a pointer to a method definition + * @param positionalArgumentsExpressions the list of arguments that will + * be passed to the method */ - case class ModuleMethod(methodPointer: MethodPointer) - extends VisualisationExpression { + case class ModuleMethod( + methodPointer: MethodPointer, + positionalArgumentsExpressions: Vector[String] + ) extends VisualisationExpression { /** @inheritdoc */ override val module: String = methodPointer.module /** @inheritdoc */ override def toLogString(shouldMask: Boolean): String = - s"ModuleMethod(methodPointer=$methodPointer)" + s"ModuleMethod(methodPointer=$methodPointer," + + s"positionalArgumentsExpressions=" + + (if (shouldMask) STUB else positionalArgumentsExpressions) + + s")" } } diff --git a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala index 59f02641d8cc..c25b19fe2415 100644 --- a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala +++ b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeVisualizationsTest.scala @@ -251,22 +251,26 @@ class RuntimeVisualizationsTest object AnnotatedVisualisation { val metadata = new Metadata - val idIncY = metadata.addItem(50, 5) - val idIncRes = metadata.addItem(66, 8) - val idIncMethod = metadata.addItem(25, 58) + val idIncY = metadata.addItem(111, 7) + val idIncRes = metadata.addItem(129, 8) + val idIncMethod = metadata.addItem(102, 43) + + println(s"idIncY=$idIncY") + println(s"idIncRes=$idIncRes") + println(s"idIncMethod=$idIncMethod") val code = metadata.appendToCode( """import Standard.Base.IO - | - |incAndEncode x = - | y = x + 1 - | res = encode y - | res | |encode x = | IO.println "encoding..." | x.to_text + | + |incAndEncode x a=1 b=1 = + | y = a*x + b + | res = encode y + | res |""".stripMargin.linesIterator.mkString("\n") ) @@ -1982,7 +1986,8 @@ class RuntimeVisualizationsTest visualisationModule, visualisationModule, visualisationFunction - ) + ), + Vector() ) ) ) @@ -2011,7 +2016,7 @@ class RuntimeVisualizationsTest stringified shouldEqual """{ "kind": "Dataflow", "message": "The List is empty."}""" } - it should "attach method pointer visualisation" in { + it should "attach method pointer visualisation without arguments" in { val idMainRes = context.Main.metadata.addItem(99, 1) val contents = context.Main.code val mainFile = context.writeMain(context.Main.code) @@ -2075,7 +2080,8 @@ class RuntimeVisualizationsTest "Enso_Test.Test.Visualisation", "Enso_Test.Test.Visualisation", "incAndEncode" - ) + ), + Vector() ) ) ) @@ -2129,7 +2135,7 @@ class RuntimeVisualizationsTest data2.sameElements("51".getBytes) shouldBe true } - it should "cache intermediate visualization expressions" in { + it should "attach method pointer visualisation with arguments" in { val idMainRes = context.Main.metadata.addItem(99, 1) val contents = context.Main.code val mainFile = context.writeMain(context.Main.code) @@ -2198,7 +2204,176 @@ class RuntimeVisualizationsTest "Enso_Test.Test.Visualisation", "Enso_Test.Test.Visualisation", "incAndEncode" + ), + Vector("2", "3") + ) + ) + ) + ) + ) + val attachVisualisationResponses = context.receiveN(3) + attachVisualisationResponses should contain allOf ( + Api.Response(requestId, Api.VisualisationAttached()), + context.executionComplete(contextId) + ) + val Some(data) = attachVisualisationResponses.collectFirst { + case Api.Response( + None, + Api.VisualisationUpdate( + Api.VisualisationContext( + `visualisationId`, + `contextId`, + `idMainRes` + ), + data + ) + ) => + data + } + data.sameElements("103".getBytes) shouldBe true + context.consumeOut shouldEqual List("encoding...") + + // recompute + context.send( + Api.Request(requestId, Api.RecomputeContextRequest(contextId, None)) + ) + + val recomputeResponses = context.receiveN(3) + recomputeResponses should contain allOf ( + Api.Response(requestId, Api.RecomputeContextResponse(contextId)), + context.executionComplete(contextId) + ) + val Some(data2) = recomputeResponses.collectFirst { + case Api.Response( + None, + Api.VisualisationUpdate( + Api.VisualisationContext( + `visualisationId`, + `contextId`, + `idMainRes` + ), + data + ) + ) => + data + } + data2.sameElements("103".getBytes) shouldBe true + context.consumeOut shouldEqual List() + + // modify visualisation + context.send( + Api.Request( + requestId, + Api.ModifyVisualisation( + visualisationId, + Api.VisualisationConfiguration( + contextId, + Api.VisualisationExpression.ModuleMethod( + Api.MethodPointer( + "Enso_Test.Test.Visualisation", + "Enso_Test.Test.Visualisation", + "incAndEncode" + ), + Vector("2", "4") + ) + ) + ) + ) + ) + val modifyVisualisationResponses = context.receiveN(2) + modifyVisualisationResponses should contain( + Api.Response(requestId, Api.VisualisationModified()) + ) + val Some(data3) = + modifyVisualisationResponses.collectFirst { + case Api.Response( + None, + Api.VisualisationUpdate( + Api.VisualisationContext( + `visualisationId`, + `contextId`, + `idMainRes` + ), + data ) + ) => + data + } + data3.sameElements("104".getBytes) shouldBe true + context.consumeOut shouldEqual List("encoding...") + } + + it should "cache intermediate visualization expressions" in { + val idMainRes = context.Main.metadata.addItem(99, 1) + val contents = context.Main.code + val mainFile = context.writeMain(context.Main.code) + val moduleName = "Enso_Test.Test.Main" + val visualisationFile = + context.writeInSrcDir( + "Visualisation", + context.AnnotatedVisualisation.code + ) + + context.send( + Api.Request( + Api.OpenFileNotification( + visualisationFile, + context.AnnotatedVisualisation.code + ) + ) + ) + + val contextId = UUID.randomUUID() + val requestId = UUID.randomUUID() + val visualisationId = UUID.randomUUID() + + // create context + context.send(Api.Request(requestId, Api.CreateContextRequest(contextId))) + context.receive shouldEqual Some( + Api.Response(requestId, Api.CreateContextResponse(contextId)) + ) + + // Open the new file + context.send( + Api.Request(Api.OpenFileNotification(mainFile, contents)) + ) + context.receiveNone shouldEqual None + + // push main + val item1 = Api.StackItem.ExplicitCall( + Api.MethodPointer(moduleName, "Enso_Test.Test.Main", "main"), + None, + Vector() + ) + context.send( + Api.Request(requestId, Api.PushContextRequest(contextId, item1)) + ) + context.receiveNIgnoreStdLib(6) should contain theSameElementsAs Seq( + Api.Response(requestId, Api.PushContextResponse(contextId)), + context.Main.Update.mainX(contextId), + context.Main.Update.mainY(contextId), + context.Main.Update.mainZ(contextId), + TestMessages.update(contextId, idMainRes, ConstantsGen.INTEGER), + context.executionComplete(contextId) + ) + context.consumeOut shouldEqual List() + + // attach visualisation + context.send( + Api.Request( + requestId, + Api.AttachVisualisation( + visualisationId, + idMainRes, + Api.VisualisationConfiguration( + contextId, + Api.VisualisationExpression.ModuleMethod( + Api.MethodPointer( + "Enso_Test.Test.Visualisation", + "Enso_Test.Test.Visualisation", + "incAndEncode" + ), + Vector() ) ) ) @@ -2260,7 +2435,7 @@ class RuntimeVisualizationsTest visualisationFile, Seq( TextEdit( - model.Range(model.Position(3, 12), model.Position(3, 13)), + model.Range(model.Position(6, 21), model.Position(6, 22)), "2" ) ), diff --git a/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java b/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java index 8d7f366e8499..4cce22595784 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java @@ -170,7 +170,7 @@ public void execute( interopLibrary.execute(call); } finally { context.getThreadManager().leave(p); - listener.ifPresent(binding -> binding.dispose()); + listener.ifPresent(EventBinding::dispose); } } @@ -259,14 +259,14 @@ public Object callFunction(Object fn, Object argument) /** * Calls a function with the given argument and attaching an execution instrument. * + * @param cache the runtime cache * @param module the module providing scope for the function * @param function the function object - * @param argument the argument applied to the function - * @param cache the runtime cache + * @param arguments the sequence of arguments applied to the function * @return the result of calling the function */ public Object callFunctionWithInstrument( - Module module, Object function, Object argument, RuntimeCache cache) + RuntimeCache cache, Module module, Object function, Object... arguments) throws UnsupportedTypeException, ArityException, UnsupportedMessageException { UUID nextExecutionItem = null; CallTarget entryCallTarget = @@ -274,13 +274,13 @@ public Object callFunctionWithInstrument( MethodCallsCache methodCallsCache = new MethodCallsCache(); UpdatesSynchronizationState syncState = new UpdatesSynchronizationState(); Consumer funCallCallback = - (value) -> context.getLogger().finest("ON_CACHED_CALL " + value.getExpressionId()); + (value) -> {}; Consumer onComputedCallback = - (value) -> context.getLogger().finest("ON_COMPUTED " + value.getExpressionId()); + (value) -> context.getLogger().finest("_ON_COMPUTED " + value.getExpressionId()); Consumer onCachedCallback = - (value) -> context.getLogger().finest("ON_CACHED_VALUE " + value.getExpressionId()); + (value) -> context.getLogger().finest("_ON_CACHED_VALUE " + value.getExpressionId()); Consumer onExceptionalCallback = - (value) -> context.getLogger().finest("ON_ERROR " + value); + (value) -> context.getLogger().finest("_ON_ERROR " + value); Optional> listener = idExecutionInstrument.map( @@ -298,7 +298,7 @@ public Object callFunctionWithInstrument( onExceptionalCallback)); Object p = context.getThreadManager().enter(); try { - return interopLibrary.execute(function, argument); + return interopLibrary.execute(function, arguments); } finally { context.getThreadManager().leave(p); listener.ifPresent(EventBinding::dispose); diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/instrument/Visualisation.scala b/engine/runtime/src/main/scala/org/enso/interpreter/instrument/Visualisation.scala index a8649681a51d..f8d2e4fb2d1d 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/instrument/Visualisation.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/instrument/Visualisation.scala @@ -21,5 +21,6 @@ case class Visualisation( module: Module, config: VisualisationConfiguration, visualisationExpressionId: Option[ExpressionId], - callback: AnyRef + callback: AnyRef, + arguments: Vector[AnyRef] ) diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/instrument/job/ProgramExecutionSupport.scala b/engine/runtime/src/main/scala/org/enso/interpreter/instrument/job/ProgramExecutionSupport.scala index 75eccc1eabf2..a32b064e531b 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/instrument/job/ProgramExecutionSupport.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/instrument/job/ProgramExecutionSupport.scala @@ -433,10 +433,10 @@ object ProgramExecutionSupport { s"Executing visualisation ${visualisation.expressionId}" ) ctx.executionService.callFunctionWithInstrument( + visualisation.cache, visualisation.module, visualisation.callback, - expressionValue, - visualisation.cache + expressionValue +: visualisation.arguments: _* ) } .flatMap { diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/instrument/job/UpsertVisualisationJob.scala b/engine/runtime/src/main/scala/org/enso/interpreter/instrument/job/UpsertVisualisationJob.scala index 84f8b431abaf..3d296daf2dbf 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/instrument/job/UpsertVisualisationJob.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/instrument/job/UpsertVisualisationJob.scala @@ -62,14 +62,15 @@ class UpsertVisualisationJob( replyWithExpressionFailedError(message, result) None - case Right(EvaluationResult(module, callable)) => + case Right(EvaluationResult(module, callable, arguments)) => val visualisation = UpsertVisualisationJob.updateVisualisation( visualisationId, expressionId, module, config, - callable + callable, + arguments ) ctx.endpoint.sendToClient(Api.Response(requestId, response)) val stack = ctx.contextManager.getStack(config.executionContextId) @@ -146,11 +147,23 @@ object UpsertVisualisationJob { failure: Option[Api.ExecutionResult.Diagnostic] ) extends EvaluationFailure - case class EvaluationResult(module: Module, callback: AnyRef) + /** The result of evaluating the method pointer and positional argument + * expressions. + * + * @param module the resolved module + * @param callback the Enso function + * @param arguments the list of arguments that will be passed to the callback + */ + case class EvaluationResult( + module: Module, + callback: AnyRef, + arguments: Vector[AnyRef] + ) /** Upsert the provided visualisation. * * @param visualisation the visualisation to update + * @param ctx the runtime context */ def upsertVisualisation( visualisation: Visualisation @@ -167,7 +180,8 @@ object UpsertVisualisationJob { expressionId, result.module, visualisationConfig, - result.callback + result.callback, + result.arguments ) val stack = ctx.contextManager.getStack(visualisationConfig.executionContextId) @@ -196,6 +210,7 @@ object UpsertVisualisationJob { * @param module the module where to evaluate the expression * @param expression the visualisation expression * @param retryCount the number of attempted retries + * @param ctx the runtime context * @return either the evaluation result or an evaluation failure */ private def evaluateModuleExpression( @@ -207,19 +222,29 @@ object UpsertVisualisationJob { ): Either[EvaluationFailure, EvaluationResult] = Either .catchNonFatal { - val callback = expression match { + val (callback, arguments) = expression match { case Api.VisualisationExpression.Text(_, expression) => - ctx.executionService.evaluateExpression(module, expression) + val callback = ctx.executionService.evaluateExpression( + module, + expression + ) + val arguments = Vector() + (callback, arguments) case Api.VisualisationExpression.ModuleMethod( - Api.MethodPointer(_, definedOnType, name) + Api.MethodPointer(_, definedOnType, name), + argumentExpressions ) => - ctx.executionService.prepareFunctionCall( + val callback = ctx.executionService.prepareFunctionCall( module, QualifiedName.fromString(definedOnType).item, name ) + val arguments = argumentExpressions.map( + ctx.executionService.evaluateExpression(module, _) + ) + (callback, arguments) } - EvaluationResult(module, callback) + EvaluationResult(module, callback, arguments) } .leftFlatMap { case _: ThreadInterruptedException @@ -265,6 +290,7 @@ object UpsertVisualisationJob { /** Evaluate the visualisation expression. * * @param expression the visualisation expression to evaluate + * @param ctx the runtime context * @return either the evaluation result or an evaluation error */ private def evaluateVisualisationExpression( @@ -285,6 +311,7 @@ object UpsertVisualisationJob { * @param module the module containing the visualisation * @param visualisationConfig the visualisation configuration * @param callback the visualisation callback function + * @param arguments the list of arugments that will be passed to the callback * @param ctx the runtime context * @return the re-evaluated visualisation */ @@ -293,7 +320,8 @@ object UpsertVisualisationJob { expressionId: ExpressionId, module: Module, visualisationConfig: VisualisationConfiguration, - callback: AnyRef + callback: AnyRef, + arguments: Vector[AnyRef] )(implicit ctx: RuntimeContext): Visualisation = { val visualisationExpressionId = findVisualisationExpressionId(module, visualisationConfig.expression) @@ -304,7 +332,8 @@ object UpsertVisualisationJob { module, visualisationConfig, visualisationExpressionId, - callback + callback, + arguments ) setCacheWeights(visualisation) ctx.contextManager.upsertVisualisation( @@ -325,14 +354,18 @@ object UpsertVisualisationJob { visualisationExpression: VisualisationExpression ): Option[ExpressionId] = visualisationExpression match { - case VisualisationExpression.ModuleMethod(methodPointer) => + case VisualisationExpression.ModuleMethod(methodPointer, _) => module.getIr.bindings .collect { case method: IR.Module.Scope.Definition.Method => val methodReference = method.methodReference val methodReferenceName = methodReference.methodName.name val methodReferenceTypeOpt = methodReference.typePointer.map(_.name) - method.getExternalId.filter { _ => + val externalIdOpt = method.body match { + case fun: IR.Function => fun.body.getExternalId + case _ => method.getExternalId + } + externalIdOpt.filter { _ => methodReferenceName == methodPointer.name && methodReferenceTypeOpt.isEmpty } From 68f707ad8cd3a68bed0b37daa84e5497ebcf2721 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Tue, 23 Aug 2022 13:24:15 +0300 Subject: [PATCH 09/25] add: ls support for positional arguments in visualization expressions --- .../protocol-language-server.md | 3 + .../runtime/VisualisationConfiguration.scala | 38 +++++++++-- .../json/ExecutionContextJsonMessages.scala | 65 ++++++++++++++++++- .../json/VisualisationOperationsTest.scala | 59 ++++++++++++++++- 4 files changed, 153 insertions(+), 12 deletions(-) diff --git a/docs/language-server/protocol-language-server.md b/docs/language-server/protocol-language-server.md index 0ea4f99fcd36..9c6cd9bf1c97 100644 --- a/docs/language-server/protocol-language-server.md +++ b/docs/language-server/protocol-language-server.md @@ -391,6 +391,9 @@ interface VisualisationConfiguration { /** An expression that creates a visualisation. */ expression: String | MethodPointer; + + /** A list of arguments to pass to the visualization expression. */ + positionalArgumentsExpressions?: string[]; } ``` diff --git a/engine/language-server/src/main/scala/org/enso/languageserver/runtime/VisualisationConfiguration.scala b/engine/language-server/src/main/scala/org/enso/languageserver/runtime/VisualisationConfiguration.scala index 7684627ebbf1..f194ed3286e8 100644 --- a/engine/language-server/src/main/scala/org/enso/languageserver/runtime/VisualisationConfiguration.scala +++ b/engine/language-server/src/main/scala/org/enso/languageserver/runtime/VisualisationConfiguration.scala @@ -59,19 +59,27 @@ object VisualisationConfiguration { * * @param contextId an execution context of the visualisation * @param expression a visualisation expression + * @param positionalArgumentsExpressions the list of arguments that will + * be passed to the visualisation expression * @return an instance of [[VisualisationConfiguration]] */ def apply( contextId: UUID, - expression: MethodPointer + expression: MethodPointer, + positionalArgumentsExpressions: Vector[String] ): VisualisationConfiguration = new VisualisationConfiguration( contextId, - VisualisationExpression.ModuleMethod(expression) + VisualisationExpression.ModuleMethod( + expression, + positionalArgumentsExpressions + ) ) private object CodecField { + val Arguments = "positionalArgumentsExpressions" + val Expression = "expression" val ExecutionContextId = "executionContextId" @@ -91,7 +99,14 @@ object VisualisationConfiguration { expression <- cursor .downField(CodecField.Expression) .as[MethodPointer] - } yield VisualisationConfiguration(contextId, expression) + arguments <- cursor + .downField(CodecField.Arguments) + .as[Option[Vector[String]]] + } yield VisualisationConfiguration( + contextId, + expression, + arguments.getOrElse(Vector()) + ) case Right(expression) => for { @@ -144,20 +159,29 @@ object VisualisationExpression { /** Visualization expression represented as a module method. * * @param methodPointer a pointer to a method definition + * @param positionalArgumentsExpressions the list of arguments that will + * be passed to the method */ - case class ModuleMethod(methodPointer: MethodPointer) - extends VisualisationExpression { + case class ModuleMethod( + methodPointer: MethodPointer, + positionalArgumentsExpressions: Vector[String] + ) extends VisualisationExpression { /** @inheritdoc */ override val module: String = methodPointer.module /** @inheritdoc */ override def toApi: Api.VisualisationExpression = - Api.VisualisationExpression.ModuleMethod(methodPointer.toApi) + Api.VisualisationExpression.ModuleMethod( + methodPointer.toApi, + positionalArgumentsExpressions + ) /** @inheritdoc */ override def toLogString(shouldMask: Boolean): String = - s"ModuleMethod(methodPointer=$methodPointer)" + s"ModuleMethod(methodPointer=$methodPointer,positionalArgumentsExpressions=" + + (if (shouldMask) STUB else positionalArgumentsExpressions) + + s")" } private object CodecField { diff --git a/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/ExecutionContextJsonMessages.scala b/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/ExecutionContextJsonMessages.scala index b5e2e7f24440..5f9e5ff4f720 100644 --- a/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/ExecutionContextJsonMessages.scala +++ b/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/ExecutionContextJsonMessages.scala @@ -130,7 +130,7 @@ object ExecutionContextJsonMessages { } } """ - case VisualisationExpression.ModuleMethod(methodPointer) => + case VisualisationExpression.ModuleMethod(methodPointer, Vector()) => json""" { "jsonrpc": "2.0", "method": "executionContext/executeExpression", @@ -149,6 +149,26 @@ object ExecutionContextJsonMessages { } } """ + case VisualisationExpression.ModuleMethod(methodPointer, arguments) => + json""" + { "jsonrpc": "2.0", + "method": "executionContext/executeExpression", + "id": $reqId, + "params": { + "visualisationId": $visualisationId, + "expressionId": $expressionId, + "visualisationConfig": { + "executionContextId": ${configuration.executionContextId}, + "expression": { + "module": ${methodPointer.module}, + "definedOnType": ${methodPointer.definedOnType}, + "name": ${methodPointer.name} + }, + "positionalArgumentsExpressions": $arguments + } + } + } + """ } def executionContextAttachVisualisationRequest( @@ -174,7 +194,7 @@ object ExecutionContextJsonMessages { } } """ - case VisualisationExpression.ModuleMethod(methodPointer) => + case VisualisationExpression.ModuleMethod(methodPointer, Vector()) => json""" { "jsonrpc": "2.0", "method": "executionContext/attachVisualisation", @@ -193,6 +213,26 @@ object ExecutionContextJsonMessages { } } """ + case VisualisationExpression.ModuleMethod(methodPointer, arguments) => + json""" + { "jsonrpc": "2.0", + "method": "executionContext/attachVisualisation", + "id": $reqId, + "params": { + "visualisationId": $visualisationId, + "expressionId": $expressionId, + "visualisationConfig": { + "executionContextId": ${configuration.executionContextId}, + "expression": { + "module": ${methodPointer.module}, + "definedOnType": ${methodPointer.definedOnType}, + "name": ${methodPointer.name} + }, + "positionalArgumentsExpressions": $arguments + } + } + } + """ } } @@ -280,7 +320,7 @@ object ExecutionContextJsonMessages { } } """ - case VisualisationExpression.ModuleMethod(methodPointer) => + case VisualisationExpression.ModuleMethod(methodPointer, Vector()) => json""" { "jsonrpc": "2.0", "method": "executionContext/modifyVisualisation", @@ -298,6 +338,25 @@ object ExecutionContextJsonMessages { } } """ + case VisualisationExpression.ModuleMethod(methodPointer, arguments) => + json""" + { "jsonrpc": "2.0", + "method": "executionContext/modifyVisualisation", + "id": $reqId, + "params": { + "visualisationId": $visualisationId, + "visualisationConfig": { + "executionContextId": ${configuration.executionContextId}, + "expression": { + "module": ${methodPointer.module}, + "definedOnType": ${methodPointer.definedOnType}, + "name": ${methodPointer.name} + }, + "positionalArgumentsExpressions": $arguments + } + } + } + """ } } diff --git a/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/VisualisationOperationsTest.scala b/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/VisualisationOperationsTest.scala index 81ed400c59f7..4c58db612d33 100644 --- a/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/VisualisationOperationsTest.scala +++ b/engine/language-server/src/test/scala/org/enso/languageserver/websocket/json/VisualisationOperationsTest.scala @@ -55,7 +55,7 @@ class VisualisationOperationsTest extends BaseServerTest { client.expectJson(ExecutionContextJsonMessages.ok(1)) } - "allow attaching method pointer as a visualisation expression" in { + "allow attaching method pointer without arguments as a visualisation expression" in { val visualisationId = UUID.randomUUID() val expressionId = UUID.randomUUID() @@ -70,7 +70,62 @@ class VisualisationOperationsTest extends BaseServerTest { visualisationModule, visualisationModule, visualisationMethod - ) + ), + Vector() + ) + + client.send( + ExecutionContextJsonMessages.executionContextAttachVisualisationRequest( + 1, + visualisationId, + expressionId, + visualisationConfig + ) + ) + + val requestId = + runtimeConnectorProbe.receiveN(1).head match { + case Api.Request( + requestId, + Api.AttachVisualisation( + `visualisationId`, + `expressionId`, + config + ) + ) => + config.expression shouldBe visualisationConfig.expression.toApi + config.visualisationModule shouldBe visualisationConfig.visualisationModule + config.executionContextId shouldBe visualisationConfig.executionContextId + requestId + + case msg => + fail(s"Unexpected message: $msg") + } + + runtimeConnectorProbe.lastSender ! Api.Response( + requestId, + Api.VisualisationAttached() + ) + client.expectJson(ExecutionContextJsonMessages.ok(1)) + } + + "allow attaching method pointer with arguments as a visualisation expression" in { + val visualisationId = UUID.randomUUID() + val expressionId = UUID.randomUUID() + + val client = getInitialisedWsClient() + val contextId = createExecutionContext(client) + val visualisationModule = "Foo.Bar" + val visualisationMethod = "baz" + val visualisationConfig = + VisualisationConfiguration( + contextId, + MethodPointer( + visualisationModule, + visualisationModule, + visualisationMethod + ), + Vector("1", "2", "3") ) client.send( From 0d24bf99a8ac2357fa35e3eddd353bb4491494c6 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Tue, 23 Aug 2022 14:08:18 +0300 Subject: [PATCH 10/25] misc: review comments --- app/gui/controller/double-representation/src/module.rs | 8 -------- app/gui/src/model/execution_context.rs | 5 +++-- app/gui/src/presenter/graph/visualization/manager.rs | 3 ++- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/app/gui/controller/double-representation/src/module.rs b/app/gui/controller/double-representation/src/module.rs index 9219bd892c02..da61ceb46974 100644 --- a/app/gui/controller/double-representation/src/module.rs +++ b/app/gui/controller/double-representation/src/module.rs @@ -325,14 +325,6 @@ impl TryFrom for QualifiedName { } } -impl TryFrom<&String> for QualifiedName { - type Error = failure::Error; - - fn try_from(text: &String) -> Result { - Self::from_text(text) - } -} - impl TryFrom for QualifiedName { type Error = failure::Error; diff --git a/app/gui/src/model/execution_context.rs b/app/gui/src/model/execution_context.rs index 0056abbd04ec..06698874b201 100644 --- a/app/gui/src/model/execution_context.rs +++ b/app/gui/src/model/execution_context.rs @@ -7,6 +7,7 @@ use crate::model::suggestion_database::entry as suggestion; use crate::notification::Publisher; use double_representation::project; +use double_representation::tp; use engine_protocol::language_server; use engine_protocol::language_server::ExpressionUpdate; use engine_protocol::language_server::ExpressionUpdatePayload; @@ -226,7 +227,7 @@ pub struct QualifiedMethodPointer { /// A module name containing the method. pub module: module::QualifiedName, /// A type on which the method is defined. - pub defined_on_type: module::QualifiedName, + pub defined_on_type: tp::QualifiedName, /// A method name. pub name: String, } @@ -234,7 +235,7 @@ pub struct QualifiedMethodPointer { impl QualifiedMethodPointer { /// Create a method pointer representing a module method. pub fn module_method(module: module::QualifiedName, name: String) -> QualifiedMethodPointer { - QualifiedMethodPointer { module: module.clone(), defined_on_type: module, name } + QualifiedMethodPointer { module: module.clone(), defined_on_type: module.into(), name } } /// Tries to create a new method pointer from string components. pub fn from_unqualified( diff --git a/app/gui/src/presenter/graph/visualization/manager.rs b/app/gui/src/presenter/graph/visualization/manager.rs index e970ca897a15..ba576b9210a8 100644 --- a/app/gui/src/presenter/graph/visualization/manager.rs +++ b/app/gui/src/presenter/graph/visualization/manager.rs @@ -501,6 +501,7 @@ mod tests { use super::*; use crate::model::module; + use futures::future::ready; use ide_view::graph_editor::component::visualization::instance::PreprocessorConfiguration; use std::assert_matches::assert_matches; @@ -557,7 +558,7 @@ mod tests { let qualified_module = inner.project.qualified_module_name(inner.module.path()); let method_pointer = QualifiedMethodPointer { module: qualified_module.clone(), - defined_on_type: qualified_module, + defined_on_type: qualified_module.into(), name: "faux".to_string(), }; let faux_vis = From 861ec59d495cc869f6d6614943afec043d0e26f4 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Tue, 23 Aug 2022 15:49:15 +0300 Subject: [PATCH 11/25] add: ide positional arguments --- .../src/language_server/types.rs | 6 ++-- app/gui/src/model/execution_context.rs | 12 +++++-- .../presenter/graph/visualization/manager.rs | 8 +++++ .../src/builtin/visualization/native/error.rs | 9 ++++- .../foreign/java_script/binding.rs | 12 +++++-- .../foreign/java_script/visualization.js | 35 ++++++++++++++++--- .../src/component/visualization/instance.rs | 24 ++++++++++--- lib/rust/prelude/src/string.rs | 6 ++++ 8 files changed, 96 insertions(+), 16 deletions(-) diff --git a/app/gui/controller/engine-protocol/src/language_server/types.rs b/app/gui/controller/engine-protocol/src/language_server/types.rs index 8d5ae2d69ecc..889dcb9b589d 100644 --- a/app/gui/controller/engine-protocol/src/language_server/types.rs +++ b/app/gui/controller/engine-protocol/src/language_server/types.rs @@ -649,10 +649,12 @@ pub type ExpressionId = Uuid; #[serde(rename_all = "camelCase")] #[allow(missing_docs)] pub struct VisualisationConfiguration { - #[allow(missing_docs)] + /// An execution context of the visualization. pub execution_context_id: ContextId, /// An enso function that will transform the data into expected format. - pub expression: MethodPointer, + pub expression: MethodPointer, + /// A list of arguments to pass to the visualization expression. + pub positional_arguments_expressions: Vec, } /// Used to enter deeper in the execution context stack. In general, all consequent stack items diff --git a/app/gui/src/model/execution_context.rs b/app/gui/src/model/execution_context.rs index 06698874b201..64f7c1f17fa8 100644 --- a/app/gui/src/model/execution_context.rs +++ b/app/gui/src/model/execution_context.rs @@ -305,6 +305,8 @@ pub struct Visualization { pub expression_id: ExpressionId, /// A pointer to the enso method that will transform the data into expected format. pub method_pointer: QualifiedMethodPointer, + /// Enso expressions for positional arguments + pub arguments: Vec, } impl Visualization { @@ -313,15 +315,21 @@ impl Visualization { pub fn new( expression_id: ExpressionId, method_pointer: QualifiedMethodPointer, + arguments: Vec, ) -> Visualization { let id = VisualizationId::new_v4(); - Visualization { id, expression_id, method_pointer } + Visualization { id, expression_id, method_pointer, arguments } } /// Creates a `VisualisationConfiguration` that is used in communication with language server. pub fn config(&self, execution_context_id: Uuid) -> VisualisationConfiguration { let expression = self.method_pointer.clone().into(); - VisualisationConfiguration { execution_context_id, expression } + let positional_arguments_expressions = self.arguments.clone(); + VisualisationConfiguration { + execution_context_id, + expression, + positional_arguments_expressions, + } } } diff --git a/app/gui/src/presenter/graph/visualization/manager.rs b/app/gui/src/presenter/graph/visualization/manager.rs index ba576b9210a8..0d340aa051f1 100644 --- a/app/gui/src/presenter/graph/visualization/manager.rs +++ b/app/gui/src/presenter/graph/visualization/manager.rs @@ -337,6 +337,14 @@ impl Manager { id: desired.visualization_id, expression_id: desired.expression_id, method_pointer, + arguments: desired + .metadata + .preprocessor + .arguments + .deref() + .into_iter() + .map_into() + .collect(), }) } diff --git a/app/gui/view/graph-editor/src/builtin/visualization/native/error.rs b/app/gui/view/graph-editor/src/builtin/visualization/native/error.rs index 0eef9dc73af8..f0287c719bcc 100644 --- a/app/gui/view/graph-editor/src/builtin/visualization/native/error.rs +++ b/app/gui/view/graph-editor/src/builtin/visualization/native/error.rs @@ -42,9 +42,16 @@ const PREPROCESSOR_MODULE: &str = "Standard.Visualization.Preprocessor"; // RuntimeVisualisationsTest.scala, used to verify the snippet's correctness const PREPROCESSOR_METHOD: &str = "error_preprocessor"; +/// The list of arguments passed to the error preprocessor. +const PREPROCESSOR_ARGUMENTS: Vec = vec![]; + /// Get preprocessor configuration for error visualization. pub fn preprocessor() -> instance::PreprocessorConfiguration { - instance::PreprocessorConfiguration::new(PREPROCESSOR_MODULE, PREPROCESSOR_METHOD) + instance::PreprocessorConfiguration::new( + PREPROCESSOR_MODULE, + PREPROCESSOR_METHOD, + PREPROCESSOR_ARGUMENTS, + ) } /// Get metadata description for error visualization. diff --git a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs index 4c25c4531562..554ae258adea 100644 --- a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs +++ b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/binding.rs @@ -197,9 +197,17 @@ impl JsConsArgs { } /// Helper method to emit an preprocessor change event from the visualisation. - pub fn emit_preprocessor_change(&self, module: Option, method: Option) { + pub fn emit_preprocessor_change( + &self, + module: Option, + method: Option, + args: Option>, + ) { let closure = &self.set_preprocessor; - let preprocessor_config = PreprocessorConfiguration::from_options(module, method); + let arguments: Option> = + args.map(|argss| argss.into_iter().map_into().collect()); + let preprocessor_config = + PreprocessorConfiguration::from_options(module, method, arguments); (*closure)(preprocessor_config); } } diff --git a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js index 89cbc27979ca..cb76059877cf 100644 --- a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js +++ b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js @@ -17,6 +17,7 @@ export class Visualization { // invoke `__emitPreprocessorChange__()` on this. this.__preprocessorModule__ = null this.__preprocessorMethod__ = null + this.__preprocessorArguments__ = null this.dom = api.root() this.theme = api.theme() @@ -28,9 +29,16 @@ export class Visualization { * @private */ __emitPreprocessorChange__() { + console.log( + 'emitPreprocessorChange', + this.__preprocessorModule__, + this.__preprocessorMethod__, + this.__preprocessorArguments__ + ) this.__api__.emit_preprocessor_change( this.__preprocessorModule__, - this.__preprocessorMethod__ + this.__preprocessorMethod__, + this.__preprocessorArguments__ ) } @@ -55,6 +63,18 @@ export class Visualization { return this.__preprocessorModule__ } + /** + * Set the arguments of the preprocessor function. + * + * @param arguments the arguments passed to the preprocessor function. + */ + setArguments(...args) { + if (args !== this.__preprocessorArguments__) { + this.__preprocessorArguments__ = args + this.__emitPreprocessorChange__() + } + } + /** * Set the preprocessor. * @@ -63,12 +83,19 @@ export class Visualization { * * @param module module containing the preprocessor method. * @param method the preprocessor method name. The method must be invocable - with one argument and return JSON-compatible result. + * with one argument and return JSON-compatible result. + * @param arguments the arguments passed to the preprocessor function. */ - setPreprocessor(module, method) { - if (module !== this.__preprocessorModule__ || method !== this.__preprocessorMethod__) { + setPreprocessor(module, method, ...args) { + console.log('setPreprocessor', module, method, ...args) + if ( + module !== this.__preprocessorModule__ || + method !== this.__preprocessorMethod__ || + args !== this.__preprocessorArguments__ + ) { this.__preprocessorModule__ = module this.__preprocessorMethod__ = method + this.__preprocessorArguments__ = args this.__emitPreprocessorChange__() } } diff --git a/app/gui/view/graph-editor/src/component/visualization/instance.rs b/app/gui/view/graph-editor/src/component/visualization/instance.rs index be6561fb4986..08ba38fb8080 100644 --- a/app/gui/view/graph-editor/src/component/visualization/instance.rs +++ b/app/gui/view/graph-editor/src/component/visualization/instance.rs @@ -20,6 +20,8 @@ use ensogl::display::Scene; pub const DEFAULT_VISUALIZATION_MODULE: &str = "Standard.Visualization.Preprocessor"; /// A name of the default visualization function. pub const DEFAULT_VISUALIZATION_FUNCTION: &str = "default_preprocessor"; +/// A list of arguments passed to the default visualization function. +const DEFAULT_VISUALIZATION_ARGUMENTS: Vec = vec![]; // ==================== @@ -59,9 +61,11 @@ impl ContextModule { #[derive(Clone, CloneRef, Debug, PartialEq, Eq)] pub struct PreprocessorConfiguration { /// The module containing the `method`. - pub module: enso::Module, + pub module: enso::Module, /// The method being invoked. - pub method: enso::Method, + pub method: enso::Method, + /// A list of arguments to pass to the visualization expression. + pub arguments: Rc>, } impl PreprocessorConfiguration { @@ -69,6 +73,7 @@ impl PreprocessorConfiguration { pub fn from_options( module: Option>, method: Option>, + arguments: Option>>, ) -> Self { let mut ret = Self::default(); if let Some(module) = module { @@ -77,6 +82,9 @@ impl PreprocessorConfiguration { if let Some(method) = method { ret.method = method.into(); } + if let Some(arguments) = arguments { + ret.arguments = Rc::new(arguments.into_iter().map_into().collect()); + } ret } @@ -84,16 +92,22 @@ impl PreprocessorConfiguration { pub fn new( module: impl Into, method: impl Into, + arguments: Vec>, ) -> PreprocessorConfiguration { - PreprocessorConfiguration { module: module.into(), method: method.into() } + PreprocessorConfiguration { + module: module.into(), + method: method.into(), + arguments: Rc::new(arguments.into_iter().map_into().collect()), + } } } impl Default for PreprocessorConfiguration { fn default() -> Self { Self { - module: DEFAULT_VISUALIZATION_MODULE.into(), - method: DEFAULT_VISUALIZATION_FUNCTION.into(), + module: DEFAULT_VISUALIZATION_MODULE.into(), + method: DEFAULT_VISUALIZATION_FUNCTION.into(), + arguments: Rc::new(DEFAULT_VISUALIZATION_ARGUMENTS), } } } diff --git a/lib/rust/prelude/src/string.rs b/lib/rust/prelude/src/string.rs index b08aa5aa7838..0ba99a9820e6 100644 --- a/lib/rust/prelude/src/string.rs +++ b/lib/rust/prelude/src/string.rs @@ -315,6 +315,12 @@ macro_rules! im_string_newtype_without_serde { Self::new(t) } } + + impl From<&$name> for String { + fn from(t:&$name) -> Self { + t.content.to_string() + } + } )*}; } From 869f80a016ae10ee52ae6dcd9b769046726dc228 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Tue, 23 Aug 2022 21:21:57 +0300 Subject: [PATCH 12/25] misc: cleanup --- .../presenter/graph/visualization/manager.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/app/gui/src/presenter/graph/visualization/manager.rs b/app/gui/src/presenter/graph/visualization/manager.rs index 0d340aa051f1..e6757c65fccd 100644 --- a/app/gui/src/presenter/graph/visualization/manager.rs +++ b/app/gui/src/presenter/graph/visualization/manager.rs @@ -326,25 +326,20 @@ impl Manager { } fn prepare_visualization(&self, desired: Desired) -> FallibleResult { - let context_module = desired.metadata.preprocessor.module; + let preprocessor_module = desired.metadata.preprocessor.module; let preprocessor_method = desired.metadata.preprocessor.method; let method_pointer = QualifiedMethodPointer::from_unqualified( - &context_module, - &context_module, + &preprocessor_module, + &preprocessor_module, &preprocessor_method, )?; + let arguments = + desired.metadata.preprocessor.arguments.deref().into_iter().map_into().collect(); Ok(Visualization { id: desired.visualization_id, expression_id: desired.expression_id, method_pointer, - arguments: desired - .metadata - .preprocessor - .arguments - .deref() - .into_iter() - .map_into() - .collect(), + arguments, }) } From b2f5f7457eccdb34982df1e33cd5f4b8b1ac8e24 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Tue, 23 Aug 2022 22:43:19 +0300 Subject: [PATCH 13/25] update scatterPlot visualization --- .../visualization/java_script/scatterPlot.js | 13 ++++++++++--- .../foreign/java_script/visualization.js | 11 ++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/app/gui/view/graph-editor/src/builtin/visualization/java_script/scatterPlot.js b/app/gui/view/graph-editor/src/builtin/visualization/java_script/scatterPlot.js index f7c91888a65d..d9c1d0553996 100644 --- a/app/gui/view/graph-editor/src/builtin/visualization/java_script/scatterPlot.js +++ b/app/gui/view/graph-editor/src/builtin/visualization/java_script/scatterPlot.js @@ -62,11 +62,18 @@ class ScatterPlot extends Visualization { } updatePreprocessor() { - let fn = 'x -> process_to_json_text x limit=' + this.limit + let args = [] if (this.bounds) { - fn += ' bounds=[' + this.bounds.join(',') + ']' + args.push('[' + this.bounds.join(',') + ']') + } else { + args.push('Nothing') } - this.setPreprocessor(fn, 'Standard.Visualization.Scatter_Plot') + args.push(this.limit.toString()) + this.setPreprocessor( + 'Standard.Visualization.Scatter_Plot', + 'process_to_json_text', + ...args + ) } /** diff --git a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js index cb76059877cf..c5711ca6d1f1 100644 --- a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js +++ b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js @@ -29,12 +29,6 @@ export class Visualization { * @private */ __emitPreprocessorChange__() { - console.log( - 'emitPreprocessorChange', - this.__preprocessorModule__, - this.__preprocessorMethod__, - this.__preprocessorArguments__ - ) this.__api__.emit_preprocessor_change( this.__preprocessorModule__, this.__preprocessorMethod__, @@ -66,6 +60,10 @@ export class Visualization { /** * Set the arguments of the preprocessor function. * + * Arguments should be strings representing valid Enso expressions that can + * be evaluated in the preprocessor module. See the + * [setter documentation]{@link setPreprocessor} for more information. + * * @param arguments the arguments passed to the preprocessor function. */ setArguments(...args) { @@ -87,7 +85,6 @@ export class Visualization { * @param arguments the arguments passed to the preprocessor function. */ setPreprocessor(module, method, ...args) { - console.log('setPreprocessor', module, method, ...args) if ( module !== this.__preprocessorModule__ || method !== this.__preprocessorMethod__ || From c78f339b3e026560fb7d66839b98323aa9597d38 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Tue, 23 Aug 2022 22:55:15 +0300 Subject: [PATCH 14/25] misc: javafmt --- .../java/org/enso/interpreter/service/ExecutionService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java b/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java index 4cce22595784..9ec31a163ed6 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/service/ExecutionService.java @@ -273,8 +273,7 @@ public Object callFunctionWithInstrument( (function instanceof Function) ? ((Function) function).getCallTarget() : null; MethodCallsCache methodCallsCache = new MethodCallsCache(); UpdatesSynchronizationState syncState = new UpdatesSynchronizationState(); - Consumer funCallCallback = - (value) -> {}; + Consumer funCallCallback = (value) -> {}; Consumer onComputedCallback = (value) -> context.getLogger().finest("_ON_COMPUTED " + value.getExpressionId()); Consumer onCachedCallback = From 338aac14fca0f0a605e0c4c45cb0913e51c87741 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Tue, 23 Aug 2022 22:59:44 +0300 Subject: [PATCH 15/25] misc: changelog --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f734077a1065..beb454f47b99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,8 +58,9 @@ - [Fixed error handling during startup.][3648] This prevents entering IDE into a "zombie" state, where processes were started but not visible to user. They could cause issues with starting further IDE instances. -- [New nodes are created in the project source when the searcher is opened - and a new node is created.][5250] +- [New nodes are created in the project source when the searcher is opened and a + new node is created.][3645] +- [IDE uses new visualization API.][3661] #### EnsoGL (rendering engine) @@ -286,7 +287,8 @@ [3643]: https://github.com/enso-org/enso/pull/3643 [3644]: https://github.com/enso-org/enso/pull/3644 [3648]: https://github.com/enso-org/enso/pull/3648 -[5250]: https://github.com/enso-org/enso/pull/5250 +[3645]: https://github.com/enso-org/enso/pull/3645 +[3661]: https://github.com/enso-org/enso/pull/3661 #### Enso Compiler From e6ab9c8c9cea53d6fe0a20ca43626d170ea62473 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Wed, 24 Aug 2022 12:50:06 +0300 Subject: [PATCH 16/25] test: fix --- .../model/execution_context/synchronized.rs | 9 +++++- .../presenter/graph/visualization/manager.rs | 30 ++++++++++++------- app/gui/tests/language_server.rs | 16 ++++++++-- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/app/gui/src/model/execution_context/synchronized.rs b/app/gui/src/model/execution_context/synchronized.rs index b879eb0ac165..4520c9b27f73 100644 --- a/app/gui/src/model/execution_context/synchronized.rs +++ b/app/gui/src/model/execution_context/synchronized.rs @@ -452,10 +452,12 @@ pub mod test { MockData::new().module_qualified_name(), "".to_string(), ); + let arguments = vec![]; let vis = Visualization { id: model::execution_context::VisualizationId::new_v4(), expression_id: model::execution_context::ExpressionId::new_v4(), method_pointer, + arguments, }; let Fixture { mut test, context, .. } = Fixture::new_customized(|ls, data| { let exe_id = data.context_id; @@ -499,10 +501,12 @@ pub mod test { MockData::new().module_qualified_name(), "".to_string(), ); + let arguments = vec!["foo".to_owned()]; let vis = Visualization { id: model::execution_context::VisualizationId::new_v4(), expression_id: model::execution_context::ExpressionId::new_v4(), method_pointer, + arguments, }; let vis2 = Visualization { id: VisualizationId::new_v4(), ..vis.clone() }; @@ -534,10 +538,12 @@ pub mod test { MockData::new().module_qualified_name(), "".to_string(), ); + let arguments = vec!["bar".to_owned()]; let vis = Visualization { id: model::execution_context::VisualizationId::new_v4(), expression_id: model::execution_context::ExpressionId::new_v4(), method_pointer, + arguments: arguments.clone(), }; let vis_id = vis.id; let new_expression = QualifiedMethodPointer::module_method( @@ -551,7 +557,8 @@ pub mod test { let expected_config = language_server::types::VisualisationConfiguration { execution_context_id: data.context_id, - expression: new_expression.clone().into(), + expression: new_expression.clone().into(), + positional_arguments_expressions: arguments, }; expect_call!(ls.attach_visualisation(vis_id,ast_id,config) => Ok(())); diff --git a/app/gui/src/presenter/graph/visualization/manager.rs b/app/gui/src/presenter/graph/visualization/manager.rs index e6757c65fccd..19725e3ae0f3 100644 --- a/app/gui/src/presenter/graph/visualization/manager.rs +++ b/app/gui/src/presenter/graph/visualization/manager.rs @@ -525,12 +525,17 @@ mod tests { Self { inner, node_id } } - fn vis_metadata(&self, method: impl Into) -> Metadata { + fn vis_metadata( + &self, + method: impl Into, + arguments: Vec>, + ) -> Metadata { Metadata { - preprocessor: PreprocessorConfiguration { - module: self.inner.module_name().to_string().into(), - method: method.into().into(), - }, + preprocessor: PreprocessorConfiguration::new( + self.inner.module_name().to_string(), + method.into(), + arguments.into_iter().map_into().collect(), + ), } } } @@ -564,8 +569,13 @@ mod tests { defined_on_type: qualified_module.into(), name: "faux".to_string(), }; - let faux_vis = - Visualization { id: default(), expression_id: default(), method_pointer }; + let arguments = vec!["foo".to_owned()]; + let faux_vis = Visualization { + id: default(), + expression_id: default(), + method_pointer, + arguments, + }; let is_ready = Synchronized::new(false); let mut execution_context = model::execution_context::MockAPI::new(); let (request_sender, requests_receiver) = futures::channel::mpsc::unbounded(); @@ -609,7 +619,7 @@ mod tests { } fn matching_metadata(visualization: &Visualization, metadata: &Metadata) -> bool { - let PreprocessorConfiguration { module, method } = &metadata.preprocessor; + let PreprocessorConfiguration { module, method, .. } = &metadata.preprocessor; let qualified_module: module::QualifiedName = module.deref().try_into().unwrap(); visualization.method_pointer.module == qualified_module && visualization.method_pointer.name == method.deref() @@ -620,8 +630,8 @@ mod tests { let fixture = Fixture::new(); let node_id = fixture.node_id; let fixture = VisOperationsTester::new(fixture); - let desired_vis_1 = fixture.vis_metadata("expr1"); - let desired_vis_2 = fixture.vis_metadata("expr2"); + let desired_vis_1 = fixture.vis_metadata("expr1", vec!["one"]); + let desired_vis_2 = fixture.vis_metadata("expr2", vec!["two"]); let VisOperationsTester { mut requests, manager, mut inner, is_ready, .. } = fixture; // No requests are sent before execution context is ready. diff --git a/app/gui/tests/language_server.rs b/app/gui/tests/language_server.rs index dd85e356a0a0..7537fdc9bb12 100644 --- a/app/gui/tests/language_server.rs +++ b/app/gui/tests/language_server.rs @@ -133,7 +133,12 @@ async fn ls_text_protocol_test() { defined_on_type: visualization_module.to_string(), name: visualization_function, }; - let visualisation_config = VisualisationConfiguration { execution_context_id, expression }; + let positional_arguments_expressions = vec!["1".to_owned()]; + let visualisation_config = VisualisationConfiguration { + execution_context_id, + expression, + positional_arguments_expressions, + }; let response = client.attach_visualisation(&visualisation_id, &expression_id, &visualisation_config); response.await.expect("Couldn't attach visualisation."); @@ -145,7 +150,12 @@ async fn ls_text_protocol_test() { defined_on_type: visualization_module.to_string(), name: visualization_function, }; - let visualisation_config = VisualisationConfiguration { execution_context_id, expression }; + let positional_arguments_expressions = vec!["1".to_owned(), "2".to_owned()]; + let visualisation_config = VisualisationConfiguration { + execution_context_id, + expression, + positional_arguments_expressions, + }; let response = client.modify_visualisation(&visualisation_id, &visualisation_config).await; response.expect("Couldn't modify visualisation."); @@ -363,7 +373,7 @@ async fn binary_visualization_updates_test_hlp() { let method_pointer = QualifiedMethodPointer::module_method(module_qualified_name, "quux".to_string()); - let visualization = Visualization::new(the_node.id(), method_pointer); + let visualization = Visualization::new(the_node.id(), method_pointer, vec![]); let stream = graph_executed.attach_visualization(visualization.clone()).await.unwrap(); info!(logger, "Attached the visualization {visualization.id}"); let mut stream = stream.boxed_local(); From 1a5c9f6f13aac557d1578bc49303b4e9d8b409d8 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Wed, 24 Aug 2022 13:30:45 +0300 Subject: [PATCH 17/25] doc: update --- .../foreign/java_script/visualization.js | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js index c5711ca6d1f1..a2bf9773556e 100644 --- a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js +++ b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js @@ -57,6 +57,17 @@ export class Visualization { return this.__preprocessorModule__ } + /** + * Get the current preprocessor's arguments. + * + * See the [setter documentation]{@link setPreprocessor} for more information. + * + * @returns {Array} The list of preprocessor arguments or `null` if none were set. + */ + getPreprocessorArguments() { + return this.__preprocessorArguments__ + } + /** * Set the arguments of the preprocessor function. * @@ -66,7 +77,7 @@ export class Visualization { * * @param arguments the arguments passed to the preprocessor function. */ - setArguments(...args) { + setPreprocessorArguments(...args) { if (args !== this.__preprocessorArguments__) { this.__preprocessorArguments__ = args this.__emitPreprocessorChange__() @@ -79,10 +90,34 @@ export class Visualization { * Sets the preprocessor method by providing the method pointer consisting of a * method name and a module name defining the preprocessor method. * - * @param module module containing the preprocessor method. - * @param method the preprocessor method name. The method must be invocable + * An example of setting a preprocessor without arguments. + * @example + * this.setPreprocessor('Standard.Visualization.Preprocessor', 'default_preprocessor') + * + * The arguments should be strings representing Enso expressions. These + * expressions will be evaluated in the context of preprocessor module and + * passed to the visualization function in the same order. + * + * For example, given the `prepare_visualization` Enso method defined as + * @example + * prepare_visualization data param_1=10 param_2=True param_3=Nothing = ... + * + * the `setPreprocessor` method call can look like + * @example + * this.setPreprocessor('Foo.Bar.Baz', 'prepare_visualization', '42', 'False') + * + * First argument of the visualization function is always the node value + * (`data`) followed by the setting parameters. In this example `setPreprocessor` + * call passes the number `42` as `param_1` and boolean `False` as `param_2`. + * `param_3` has been left unchanged with a default value `Nothing` since it + * was not specified in the example `setPreprocessor` call. + * + * @param module The qualified module containing the preprocessor method. + * @param method The preprocessor method name. The method must be invocable * with one argument and return JSON-compatible result. - * @param arguments the arguments passed to the preprocessor function. + * @param arguments Positional arguments passed to the preprocessor function. + * Arguments should be strings representing valid Enso + * expressions that can be evaluated in the preprocessor module. */ setPreprocessor(module, method, ...args) { if ( From 0600286b7e89193d4de3092826bf1c4e27cc924b Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Wed, 24 Aug 2022 15:24:03 +0300 Subject: [PATCH 18/25] fix: lint --- .../src/language_server/tests.rs | 24 ++++++++++++++----- .../presenter/graph/visualization/manager.rs | 4 ++-- .../visualization/java_script/scatterPlot.js | 6 +---- .../foreign/java_script/visualization.js | 6 ++--- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/app/gui/controller/engine-protocol/src/language_server/tests.rs b/app/gui/controller/engine-protocol/src/language_server/tests.rs index 1c4affb42b7c..bf23a87ec546 100644 --- a/app/gui/controller/engine-protocol/src/language_server/tests.rs +++ b/app/gui/controller/engine-protocol/src/language_server/tests.rs @@ -424,12 +424,18 @@ fn test_execution_context() { ); let visualisation_id = uuid::Uuid::default(); let expression_id = uuid::Uuid::default(); - let expression = "1 + 1".to_string(); - let visualisation_module = "[Foo.Bar.Baz]".to_string(); + let visualization_function = "foo"; + let visualization_module = "[Foo.Bar.Baz]"; + let expression = MethodPointer { + module: visualization_module.to_string(), + defined_on_type: visualization_module.to_string(), + name: visualization_function.to_string(), + }; + let positional_arguments_expressions = vec![]; let visualisation_config = VisualisationConfiguration { execution_context_id: context_id, expression, - visualisation_module, + positional_arguments_expressions, }; test_request( |client| { @@ -459,12 +465,18 @@ fn test_execution_context() { unit_json.clone(), (), ); - let expression = "1 + 1".to_string(); - let visualisation_module = "[Foo.Bar.Baz]".to_string(); + let visualization_function = "foo"; + let visualization_module = "[Foo.Bar.Baz]"; + let expression = MethodPointer { + module: visualization_module.to_string(), + defined_on_type: visualization_module.to_string(), + name: visualization_function.to_string(), + }; + let positional_arguments_expressions = vec![]; let visualisation_config = VisualisationConfiguration { execution_context_id: context_id, expression, - visualisation_module, + positional_arguments_expressions, }; test_request( |client| client.modify_visualisation(&visualisation_id, &visualisation_config), diff --git a/app/gui/src/presenter/graph/visualization/manager.rs b/app/gui/src/presenter/graph/visualization/manager.rs index 19725e3ae0f3..4871fb13ee56 100644 --- a/app/gui/src/presenter/graph/visualization/manager.rs +++ b/app/gui/src/presenter/graph/visualization/manager.rs @@ -75,6 +75,7 @@ pub enum Notification { /// Describes the state of the visualization on the Language Server. #[derive(Clone, Debug, PartialEq)] +#[allow(clippy::large_enum_variant)] pub enum Status { /// Not attached and no ongoing background work. NotAttached, @@ -333,8 +334,7 @@ impl Manager { &preprocessor_module, &preprocessor_method, )?; - let arguments = - desired.metadata.preprocessor.arguments.deref().into_iter().map_into().collect(); + let arguments = desired.metadata.preprocessor.arguments.deref().iter().map_into().collect(); Ok(Visualization { id: desired.visualization_id, expression_id: desired.expression_id, diff --git a/app/gui/view/graph-editor/src/builtin/visualization/java_script/scatterPlot.js b/app/gui/view/graph-editor/src/builtin/visualization/java_script/scatterPlot.js index d9c1d0553996..e0cc8c4ab3bb 100644 --- a/app/gui/view/graph-editor/src/builtin/visualization/java_script/scatterPlot.js +++ b/app/gui/view/graph-editor/src/builtin/visualization/java_script/scatterPlot.js @@ -69,11 +69,7 @@ class ScatterPlot extends Visualization { args.push('Nothing') } args.push(this.limit.toString()) - this.setPreprocessor( - 'Standard.Visualization.Scatter_Plot', - 'process_to_json_text', - ...args - ) + this.setPreprocessor('Standard.Visualization.Scatter_Plot', 'process_to_json_text', ...args) } /** diff --git a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js index a2bf9773556e..9760154598e3 100644 --- a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js +++ b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js @@ -121,9 +121,9 @@ export class Visualization { */ setPreprocessor(module, method, ...args) { if ( - module !== this.__preprocessorModule__ || - method !== this.__preprocessorMethod__ || - args !== this.__preprocessorArguments__ + module !== this.__preprocessorModule__ || + method !== this.__preprocessorMethod__ || + args !== this.__preprocessorArguments__ ) { this.__preprocessorModule__ = module this.__preprocessorMethod__ = method From af756e41aad734497e7222ed8fa75b909ce9bf69 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Wed, 24 Aug 2022 17:52:40 +0300 Subject: [PATCH 19/25] fix: test --- .../engine-protocol/src/language_server/tests.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/gui/controller/engine-protocol/src/language_server/tests.rs b/app/gui/controller/engine-protocol/src/language_server/tests.rs index bf23a87ec546..873b5d371bce 100644 --- a/app/gui/controller/engine-protocol/src/language_server/tests.rs +++ b/app/gui/controller/engine-protocol/src/language_server/tests.rs @@ -447,8 +447,12 @@ fn test_execution_context() { "expressionId" : "00000000-0000-0000-0000-000000000000", "visualisationConfig" : { "executionContextId" : "00000000-0000-0000-0000-000000000000", - "visualisationModule" : "[Foo.Bar.Baz]", - "expression" : "1 + 1" + "expression" : { + "module" : "[Foo.Bar.Baz]", + "definedOnType" : "[Foo.Bar.Baz]", + "name" : "foo" + }, + "positionalArgumentsExpressions" : [] } }), unit_json.clone(), @@ -485,8 +489,12 @@ fn test_execution_context() { "visualisationId" : "00000000-0000-0000-0000-000000000000", "visualisationConfig" : { "executionContextId" : "00000000-0000-0000-0000-000000000000", - "visualisationModule" : "[Foo.Bar.Baz]", - "expression" : "1 + 1" + "expression" : { + "module" : "[Foo.Bar.Baz]", + "definedOnType" : "[Foo.Bar.Baz]", + "name" : "foo" + }, + "positionalArgumentsExpressions" : [] } }), unit_json.clone(), From 99416819ca5f14268acfd0f752c9213ee228be01 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Wed, 24 Aug 2022 18:03:20 +0300 Subject: [PATCH 20/25] misc: cleanup --- app/gui/src/model/execution_context/plain.rs | 1 + .../visualization/foreign/java_script/visualization.js | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/gui/src/model/execution_context/plain.rs b/app/gui/src/model/execution_context/plain.rs index 838681decfe8..b0fc493bc38f 100644 --- a/app/gui/src/model/execution_context/plain.rs +++ b/app/gui/src/model/execution_context/plain.rs @@ -16,6 +16,7 @@ use engine_protocol::language_server::VisualisationConfiguration; use futures::future::LocalBoxFuture; + // ============== // === Errors === // ============== diff --git a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js index 9760154598e3..5b768d7359e8 100644 --- a/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js +++ b/app/gui/view/graph-editor/src/component/visualization/foreign/java_script/visualization.js @@ -107,10 +107,11 @@ export class Visualization { * this.setPreprocessor('Foo.Bar.Baz', 'prepare_visualization', '42', 'False') * * First argument of the visualization function is always the node value - * (`data`) followed by the setting parameters. In this example `setPreprocessor` - * call passes the number `42` as `param_1` and boolean `False` as `param_2`. - * `param_3` has been left unchanged with a default value `Nothing` since it - * was not specified in the example `setPreprocessor` call. + * (`data`), followed by the configuration parameters. In this example + * `setPreprocessor` call passes the number `42` as `param_1` and boolean + * `False` as `param_2`. `param_3` has been left unchanged with the default + * value `Nothing` since it was not specified in the example + * `setPreprocessor` call. * * @param module The qualified module containing the preprocessor method. * @param method The preprocessor method name. The method must be invocable From 76c69a66b62e8329663004406920822cc7e361ef Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Thu, 25 Aug 2022 09:31:20 +0300 Subject: [PATCH 21/25] misc: refactoring --- app/gui/src/model/execution_context.rs | 2 +- app/gui/src/presenter/graph/visualization/manager.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/gui/src/model/execution_context.rs b/app/gui/src/model/execution_context.rs index 64f7c1f17fa8..e1d288a672f4 100644 --- a/app/gui/src/model/execution_context.rs +++ b/app/gui/src/model/execution_context.rs @@ -238,7 +238,7 @@ impl QualifiedMethodPointer { QualifiedMethodPointer { module: module.clone(), defined_on_type: module.into(), name } } /// Tries to create a new method pointer from string components. - pub fn from_unqualified( + pub fn from_qualified_text( module: &str, defined_on_type: &str, name: &str, diff --git a/app/gui/src/presenter/graph/visualization/manager.rs b/app/gui/src/presenter/graph/visualization/manager.rs index 4871fb13ee56..639a2ebc0200 100644 --- a/app/gui/src/presenter/graph/visualization/manager.rs +++ b/app/gui/src/presenter/graph/visualization/manager.rs @@ -329,7 +329,7 @@ impl Manager { fn prepare_visualization(&self, desired: Desired) -> FallibleResult { let preprocessor_module = desired.metadata.preprocessor.module; let preprocessor_method = desired.metadata.preprocessor.method; - let method_pointer = QualifiedMethodPointer::from_unqualified( + let method_pointer = QualifiedMethodPointer::from_qualified_text( &preprocessor_module, &preprocessor_module, &preprocessor_method, @@ -663,7 +663,7 @@ mod tests { manager.request_visualization(node_id, desired_vis_1.clone()); inner.run_until_stalled(); if let ExecutionContextRequest::Modify { id, method_pointer } = requests.expect_one() { - let desired_method_pointer = QualifiedMethodPointer::from_unqualified( + let desired_method_pointer = QualifiedMethodPointer::from_qualified_text( &desired_vis_1.preprocessor.module, &desired_vis_1.preprocessor.module, &desired_vis_1.preprocessor.method, From 1c26a9053cd05301c158d8b44434790ae6f35494 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Thu, 25 Aug 2022 17:10:09 +0300 Subject: [PATCH 22/25] misc: fix modify visualization request --- app/gui/src/controller/graph/executed.rs | 14 ++------------ app/gui/src/model/execution_context.rs | 1 + app/gui/src/model/execution_context/plain.rs | 10 +++++++--- .../src/model/execution_context/synchronized.rs | 7 ++++--- .../src/presenter/graph/visualization/manager.rs | 6 ++++-- 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/app/gui/src/controller/graph/executed.rs b/app/gui/src/controller/graph/executed.rs index 7b0f373ede1a..22e9da357f18 100644 --- a/app/gui/src/controller/graph/executed.rs +++ b/app/gui/src/controller/graph/executed.rs @@ -144,8 +144,9 @@ impl Handle { &self, id: VisualizationId, method_pointer: Option, + arguments: Option>, ) -> BoxFuture { - self.execution_ctx.modify_visualization(id, method_pointer) + self.execution_ctx.modify_visualization(id, method_pointer, arguments) } /// See [`model::ExecutionContext::detach_visualization`]. @@ -165,17 +166,6 @@ impl Handle { self.execution_ctx.computed_value_info_registry() } - /// Modify preprocessor code in visualization. See also - /// [`model::ExecutionContext::modify_visualization`]. - #[profile(Detail)] - pub async fn set_visualization_preprocessor( - &self, - id: VisualizationId, - method_pointer: QualifiedMethodPointer, - ) -> FallibleResult { - self.execution_ctx.modify_visualization(id, Some(method_pointer)).await - } - /// See [`model::ExecutionContext::component_groups`]. pub fn component_groups(&self) -> Rc> { self.execution_ctx.component_groups() diff --git a/app/gui/src/model/execution_context.rs b/app/gui/src/model/execution_context.rs index e1d288a672f4..9cee804c6b7f 100644 --- a/app/gui/src/model/execution_context.rs +++ b/app/gui/src/model/execution_context.rs @@ -469,6 +469,7 @@ pub trait API: Debug { &'a self, id: VisualizationId, method_pointer: Option, + arguments: Option>, ) -> BoxFuture<'a, FallibleResult>; /// Dispatches the visualization update data (typically received from as LS binary notification) diff --git a/app/gui/src/model/execution_context/plain.rs b/app/gui/src/model/execution_context/plain.rs index b0fc493bc38f..9f20e11b6dd3 100644 --- a/app/gui/src/model/execution_context/plain.rs +++ b/app/gui/src/model/execution_context/plain.rs @@ -135,15 +135,17 @@ impl ExecutionContext { &self, id: VisualizationId, method_pointer: Option, + arguments: Option>, ) -> FallibleResult { let err = || InvalidVisualizationId(id); let mut visualizations = self.visualizations.borrow_mut(); let visualization = &mut visualizations.get_mut(&id).ok_or_else(err)?.visualization; - if let Some(method_pointer) = method_pointer { visualization.method_pointer = method_pointer; } - + if let Some(arguments) = arguments { + visualization.arguments = arguments; + } Ok(()) } @@ -226,8 +228,10 @@ impl model::execution_context::API for ExecutionContext { &self, id: VisualizationId, method_pointer: Option, + arguments: Option>, ) -> BoxFuture { - futures::future::ready(self.modify_visualization(id, method_pointer)).boxed_local() + futures::future::ready(self.modify_visualization(id, method_pointer, arguments)) + .boxed_local() } fn dispatch_visualization_update( diff --git a/app/gui/src/model/execution_context/synchronized.rs b/app/gui/src/model/execution_context/synchronized.rs index 4520c9b27f73..6b07731968b7 100644 --- a/app/gui/src/model/execution_context/synchronized.rs +++ b/app/gui/src/model/execution_context/synchronized.rs @@ -270,8 +270,9 @@ impl model::execution_context::API for ExecutionContext { &self, id: VisualizationId, method_pointer: Option, + arguments: Option>, ) -> BoxFuture { - let result = self.model.modify_visualization(id, method_pointer); + let result = self.model.modify_visualization(id, method_pointer, arguments); let new_config = self.model.visualization_config(id, self.id); async move { result?; @@ -558,7 +559,7 @@ pub mod test { let expected_config = language_server::types::VisualisationConfiguration { execution_context_id: data.context_id, expression: new_expression.clone().into(), - positional_arguments_expressions: arguments, + positional_arguments_expressions: arguments.clone(), }; expect_call!(ls.attach_visualisation(vis_id,ast_id,config) => Ok(())); @@ -568,7 +569,7 @@ pub mod test { test.run_task(async move { context.attach_visualization(vis.clone()).await.unwrap(); let method_pointer = Some(new_expression); - context.modify_visualization(vis_id, method_pointer).await.unwrap(); + context.modify_visualization(vis_id, method_pointer, Some(arguments)).await.unwrap(); }); } diff --git a/app/gui/src/presenter/graph/visualization/manager.rs b/app/gui/src/presenter/graph/visualization/manager.rs index 639a2ebc0200..1c02e9740c4a 100644 --- a/app/gui/src/presenter/graph/visualization/manager.rs +++ b/app/gui/src/presenter/graph/visualization/manager.rs @@ -476,7 +476,9 @@ impl Manager { self.update_status(target, status); let id = so_far.id; let method_pointer = new_visualization.method_pointer.clone(); - let modifying_result = self.executed_graph.modify_visualization(id, Some(method_pointer)); + let arguments = new_visualization.arguments.clone(); + let modifying_result = + self.executed_graph.modify_visualization(id, Some(method_pointer), Some(arguments)); match modifying_result.await { Ok(_) => { let status = Status::Attached(new_visualization); @@ -599,7 +601,7 @@ mod tests { let sender = request_sender; execution_context.expect_modify_visualization().returning_st( - move |id, method_pointer| { + move |id, method_pointer, _| { let request = ExecutionContextRequest::Modify { id, method_pointer }; sender.unbounded_send(request).unwrap(); ready(Ok(())).boxed_local() From 0c4efc7820102422400f9e16d17f713e997ee14e Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Thu, 25 Aug 2022 17:11:32 +0300 Subject: [PATCH 23/25] misc: review comments --- app/gui/docs/product/visualizations.md | 2 +- app/gui/src/model/execution_context.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/gui/docs/product/visualizations.md b/app/gui/docs/product/visualizations.md index 0210aca12658..9ccf3fe7d416 100644 --- a/app/gui/docs/product/visualizations.md +++ b/app/gui/docs/product/visualizations.md @@ -200,7 +200,7 @@ In particular: as visualizations. The superclass defines a default constructor and a set of utilities: - - #### Method `setPreprocessor(module,method)` + - #### Method `setPreprocessor(module,method,arguments)` Set an Enso method which will be evaluated on the server-side before sending data to visualization. If not called, a default unspecified method is used that will provide some JSON representation of the value. See diff --git a/app/gui/src/model/execution_context.rs b/app/gui/src/model/execution_context.rs index 9cee804c6b7f..629fbc446327 100644 --- a/app/gui/src/model/execution_context.rs +++ b/app/gui/src/model/execution_context.rs @@ -222,7 +222,7 @@ pub struct LocalCall { // ============================== /// A method pointer containing the qualified module and type names. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct QualifiedMethodPointer { /// A module name containing the method. pub module: module::QualifiedName, From 7f34282d176d175ccb1447e0c7750b1813f5e864 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Thu, 1 Sep 2022 14:19:01 +0300 Subject: [PATCH 24/25] misc: review comments --- .../src/language_server/tests.rs | 18 ++++---- app/gui/docs/product/visualizations.md | 9 ++-- app/gui/src/model/execution_context.rs | 43 +++++++++++-------- .../model/execution_context/synchronized.rs | 9 ++-- .../presenter/graph/visualization/manager.rs | 5 ++- app/gui/tests/language_server.rs | 8 ++-- 6 files changed, 51 insertions(+), 41 deletions(-) diff --git a/app/gui/controller/engine-protocol/src/language_server/tests.rs b/app/gui/controller/engine-protocol/src/language_server/tests.rs index 873b5d371bce..a7b989321d64 100644 --- a/app/gui/controller/engine-protocol/src/language_server/tests.rs +++ b/app/gui/controller/engine-protocol/src/language_server/tests.rs @@ -1,14 +1,14 @@ -use super::*; +use std::future::Future; use futures::task::LocalSpawnExt; -use json_rpc::messages::Message; -use json_rpc::messages::RequestMessage; -use json_rpc::test_util::transport::mock::MockTransport; use serde_json::json; use serde_json::Value; -use std::future::Future; +use json_rpc::messages::Message; +use json_rpc::messages::RequestMessage; +use json_rpc::test_util::transport::mock::MockTransport; +use super::*; // =============== // === Fixture === @@ -431,7 +431,7 @@ fn test_execution_context() { defined_on_type: visualization_module.to_string(), name: visualization_function.to_string(), }; - let positional_arguments_expressions = vec![]; + let positional_arguments_expressions = vec![1, 2, 3].iter().map(|x| x.to_string()).collect(); let visualisation_config = VisualisationConfiguration { execution_context_id: context_id, expression, @@ -452,7 +452,7 @@ fn test_execution_context() { "definedOnType" : "[Foo.Bar.Baz]", "name" : "foo" }, - "positionalArgumentsExpressions" : [] + "positionalArgumentsExpressions" : ["1", "2", "3"] } }), unit_json.clone(), @@ -476,7 +476,7 @@ fn test_execution_context() { defined_on_type: visualization_module.to_string(), name: visualization_function.to_string(), }; - let positional_arguments_expressions = vec![]; + let positional_arguments_expressions = vec!["foo"].iter().map(|x| x.to_string()).collect(); let visualisation_config = VisualisationConfiguration { execution_context_id: context_id, expression, @@ -494,7 +494,7 @@ fn test_execution_context() { "definedOnType" : "[Foo.Bar.Baz]", "name" : "foo" }, - "positionalArgumentsExpressions" : [] + "positionalArgumentsExpressions" : ["foo"] } }), unit_json.clone(), diff --git a/app/gui/docs/product/visualizations.md b/app/gui/docs/product/visualizations.md index 9ccf3fe7d416..b675ed6664d1 100644 --- a/app/gui/docs/product/visualizations.md +++ b/app/gui/docs/product/visualizations.md @@ -200,11 +200,12 @@ In particular: as visualizations. The superclass defines a default constructor and a set of utilities: - - #### Method `setPreprocessor(module,method,arguments)` + - #### Method `setPreprocessor(module,method,...arguments)` Set an Enso method which will be evaluated on the server-side before sending - data to visualization. If not called, a default unspecified method is used - that will provide some JSON representation of the value. See - [Lazy visualizations](#lazy-visualizations) section for details. + data to visualization. Note that `arguments` is a vararg. If not called, a + default unspecified method is used that will provide some JSON + representation of the value. See [Lazy visualizations](#lazy-visualizations) + section for details. - #### Field `dom` It is initialized in the constructor to the DOM symbol used to host the visualization content. Users are free to modify the DOM element, including diff --git a/app/gui/src/model/execution_context.rs b/app/gui/src/model/execution_context.rs index 629fbc446327..9d3668bcdf80 100644 --- a/app/gui/src/model/execution_context.rs +++ b/app/gui/src/model/execution_context.rs @@ -1,11 +1,14 @@ //! This module consists of all structures describing Execution Context. -use crate::prelude::*; +use std::collections::HashMap; -use crate::model::module; -use crate::model::suggestion_database::entry as suggestion; -use crate::notification::Publisher; +use flo_stream::Subscriber; +use mockall::automock; +use serde::Deserialize; +use serde::Serialize; +use uuid::Uuid; +use double_representation::identifier::Identifier; use double_representation::project; use double_representation::tp; use engine_protocol::language_server; @@ -15,13 +18,11 @@ use engine_protocol::language_server::MethodPointer; use engine_protocol::language_server::SuggestionId; use engine_protocol::language_server::VisualisationConfiguration; use ensogl::data::color; -use flo_stream::Subscriber; -use mockall::automock; -use serde::Deserialize; -use serde::Serialize; -use std::collections::HashMap; -use uuid::Uuid; +use crate::model::module; +use crate::model::suggestion_database::entry as suggestion; +use crate::notification::Publisher; +use crate::prelude::*; // ============== // === Export === @@ -229,12 +230,15 @@ pub struct QualifiedMethodPointer { /// A type on which the method is defined. pub defined_on_type: tp::QualifiedName, /// A method name. - pub name: String, + pub name: Identifier, } impl QualifiedMethodPointer { /// Create a method pointer representing a module method. - pub fn module_method(module: module::QualifiedName, name: String) -> QualifiedMethodPointer { + pub fn module_method( + module: module::QualifiedName, + name: Identifier, + ) -> QualifiedMethodPointer { QualifiedMethodPointer { module: module.clone(), defined_on_type: module.into(), name } } /// Tries to create a new method pointer from string components. @@ -245,10 +249,11 @@ impl QualifiedMethodPointer { ) -> FallibleResult { let resolved_module = module.try_into()?; let resolved_type = defined_on_type.try_into()?; + let name_identifier = Identifier::from_text(name)?; Ok(QualifiedMethodPointer { module: resolved_module, defined_on_type: resolved_type, - name: name.to_owned(), + name: name_identifier, }) } } @@ -267,7 +272,7 @@ impl TryFrom<&MethodPointer> for QualifiedMethodPointer { fn try_from(method_pointer: &MethodPointer) -> Result { let module = method_pointer.module.as_str().try_into()?; let defined_on_type = method_pointer.defined_on_type.as_str().try_into()?; - let name = method_pointer.name.clone(); + let name = Identifier::from_text(method_pointer.name.clone())?; Ok(QualifiedMethodPointer { module, defined_on_type, name }) } } @@ -282,7 +287,7 @@ impl From<&QualifiedMethodPointer> for MethodPointer { fn from(qualified_method_pointer: &QualifiedMethodPointer) -> Self { let module = qualified_method_pointer.module.clone().into(); let defined_on_type = qualified_method_pointer.defined_on_type.clone().into(); - let name = qualified_method_pointer.name.clone(); + let name = qualified_method_pointer.name.name().to_owned(); MethodPointer { module, defined_on_type, name } } } @@ -519,14 +524,14 @@ pub type Synchronized = synchronized::ExecutionContext; #[cfg(test)] mod tests { - use super::*; - - use crate::executor::test_utils::TestWithLocalPoolExecutor; - use engine_protocol::language_server::types::test::value_update_with_dataflow_error; use engine_protocol::language_server::types::test::value_update_with_dataflow_panic; use engine_protocol::language_server::types::test::value_update_with_type; + use crate::executor::test_utils::TestWithLocalPoolExecutor; + + use super::*; + #[test] fn getting_future_type_from_registry() { let mut fixture = TestWithLocalPoolExecutor::set_up(); diff --git a/app/gui/src/model/execution_context/synchronized.rs b/app/gui/src/model/execution_context/synchronized.rs index 6b07731968b7..26e31451a5d2 100644 --- a/app/gui/src/model/execution_context/synchronized.rs +++ b/app/gui/src/model/execution_context/synchronized.rs @@ -315,6 +315,7 @@ impl Drop for ExecutionContext { #[cfg(test)] pub mod test { use super::*; + use double_representation::identifier::Identifier; use crate::executor::test_utils::TestWithLocalPoolExecutor; use crate::model::execution_context::plain::test::MockData; @@ -451,7 +452,7 @@ pub mod test { fn attaching_visualizations_and_notifying() { let method_pointer = QualifiedMethodPointer::module_method( MockData::new().module_qualified_name(), - "".to_string(), + Identifier::from_text("foo").unwrap(), ); let arguments = vec![]; let vis = Visualization { @@ -500,7 +501,7 @@ pub mod test { fn detaching_all_visualizations() { let method_pointer = QualifiedMethodPointer::module_method( MockData::new().module_qualified_name(), - "".to_string(), + Identifier::from_text("foo").unwrap(), ); let arguments = vec!["foo".to_owned()]; let vis = Visualization { @@ -537,7 +538,7 @@ pub mod test { fn modifying_visualizations() { let method_pointer = QualifiedMethodPointer::module_method( MockData::new().module_qualified_name(), - "".to_string(), + Identifier::from_text("foo").unwrap(), ); let arguments = vec!["bar".to_owned()]; let vis = Visualization { @@ -549,7 +550,7 @@ pub mod test { let vis_id = vis.id; let new_expression = QualifiedMethodPointer::module_method( MockData::new().module_qualified_name(), - "quux".to_string(), + Identifier::from_text("quux").unwrap(), ); let Fixture { mut test, context, .. } = Fixture::new_customized(|ls, data| { let exe_id = data.context_id; diff --git a/app/gui/src/presenter/graph/visualization/manager.rs b/app/gui/src/presenter/graph/visualization/manager.rs index 1c02e9740c4a..247f9aff2c05 100644 --- a/app/gui/src/presenter/graph/visualization/manager.rs +++ b/app/gui/src/presenter/graph/visualization/manager.rs @@ -507,6 +507,7 @@ mod tests { use crate::model::module; + use double_representation::identifier::Identifier; use futures::future::ready; use ide_view::graph_editor::component::visualization::instance::PreprocessorConfiguration; use std::assert_matches::assert_matches; @@ -569,7 +570,7 @@ mod tests { let method_pointer = QualifiedMethodPointer { module: qualified_module.clone(), defined_on_type: qualified_module.into(), - name: "faux".to_string(), + name: Identifier::from_text("faux").unwrap(), }; let arguments = vec!["foo".to_owned()]; let faux_vis = Visualization { @@ -624,7 +625,7 @@ mod tests { let PreprocessorConfiguration { module, method, .. } = &metadata.preprocessor; let qualified_module: module::QualifiedName = module.deref().try_into().unwrap(); visualization.method_pointer.module == qualified_module - && visualization.method_pointer.name == method.deref() + && visualization.method_pointer.name.name() == method.deref() } #[wasm_bindgen_test] diff --git a/app/gui/tests/language_server.rs b/app/gui/tests/language_server.rs index 7537fdc9bb12..772640ebc674 100644 --- a/app/gui/tests/language_server.rs +++ b/app/gui/tests/language_server.rs @@ -18,6 +18,7 @@ use engine_protocol::language_server::*; use engine_protocol::types::*; use enso_gui::prelude::*; +use double_representation::identifier::Identifier; use enso_gui::model::execution_context::QualifiedMethodPointer; use enso_gui::model::execution_context::Visualization; use enso_gui::model::module; @@ -28,7 +29,6 @@ use wasm_bindgen_test::wasm_bindgen_test; use wasm_bindgen_test::wasm_bindgen_test_configure; - /// The endpoint at which the Language Server should be accepting WS connections. const SERVER_ENDPOINT: &str = "ws://localhost:30616"; @@ -371,8 +371,10 @@ async fn binary_visualization_updates_test_hlp() { info!(logger, "The code is: {module.ast().repr():?}"); info!(logger, "Main node: {the_node:?} with {the_node.expression().repr()}"); - let method_pointer = - QualifiedMethodPointer::module_method(module_qualified_name, "quux".to_string()); + let method_pointer = QualifiedMethodPointer::module_method( + module_qualified_name, + Identifier::from_text("quux").unwrap(), + ); let visualization = Visualization::new(the_node.id(), method_pointer, vec![]); let stream = graph_executed.attach_visualization(visualization.clone()).await.unwrap(); info!(logger, "Attached the visualization {visualization.id}"); From 020b0b16721f4f34f059d1378f6a15dfc12dbf19 Mon Sep 17 00:00:00 2001 From: Dmitry Bushev Date: Thu, 1 Sep 2022 14:37:47 +0300 Subject: [PATCH 25/25] misc: bump wasm-size-limit --- build-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-config.yaml b/build-config.yaml index 8547a2e16418..8973dbd9e4b9 100644 --- a/build-config.yaml +++ b/build-config.yaml @@ -1,6 +1,6 @@ # Options intended to be common for all developers. -wasm-size-limit: 14.44 MiB +wasm-size-limit: 14.45 MiB required-versions: cargo-watch: ^8.1.1