Skip to content

Commit

Permalink
IDE uses new visualization API (#3661)
Browse files Browse the repository at this point in the history
  • Loading branch information
4e6 authored Sep 1, 2022
1 parent 65140f4 commit de0a231
Show file tree
Hide file tree
Showing 38 changed files with 936 additions and 446 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.][3645]
- [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]
- [Visualization of long textual values improved][3665]

#### EnsoGL (rendering engine)
Expand Down Expand Up @@ -296,6 +297,7 @@
[3644]: https://github.com/enso-org/enso/pull/3644
[3645]: https://github.com/enso-org/enso/pull/3645
[3648]: https://github.com/enso-org/enso/pull/3648
[3661]: https://github.com/enso-org/enso/pull/3661
[3665]: https://github.com/enso-org/enso/pull/3665
[3634]: https://github.com/enso-org/enso/pull/3634
[3669]: https://github.com/enso-org/enso/pull/3669
Expand Down
50 changes: 35 additions & 15 deletions app/gui/controller/engine-protocol/src/language_server/tests.rs
Original file line number Diff line number Diff line change
@@ -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 ===
Expand Down Expand Up @@ -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![1, 2, 3].iter().map(|x| x.to_string()).collect();
let visualisation_config = VisualisationConfiguration {
execution_context_id: context_id,
expression,
visualisation_module,
positional_arguments_expressions,
};
test_request(
|client| {
Expand All @@ -441,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" : ["1", "2", "3"]
}
}),
unit_json.clone(),
Expand All @@ -459,12 +469,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!["foo"].iter().map(|x| x.to_string()).collect();
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),
Expand All @@ -473,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" : ["foo"]
}
}),
unit_json.clone(),
Expand Down
10 changes: 5 additions & 5 deletions app/gui/controller/engine-protocol/src/language_server/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -649,12 +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,
/// 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,
/// A list of arguments to pass to the visualization expression.
pub positional_arguments_expressions: Vec<String>,
}

/// Used to enter deeper in the execution context stack. In general, all consequent stack items
Expand Down
116 changes: 51 additions & 65 deletions app/gui/docs/product/visualizations.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 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.

- **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.

Expand Down Expand Up @@ -201,24 +200,12 @@ 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
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.
- #### Method `setPreprocessor(module,method,...arguments)`
Set an Enso method which will be evaluated on the server-side before sending
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
Expand Down Expand Up @@ -297,9 +284,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
Expand Down
19 changes: 4 additions & 15 deletions app/gui/src/controller/graph/executed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -142,10 +143,10 @@ impl Handle {
pub fn modify_visualization(
&self,
id: VisualizationId,
expression: Option<String>,
module: Option<model::module::QualifiedName>,
method_pointer: Option<QualifiedMethodPointer>,
arguments: Option<Vec<String>>,
) -> BoxFuture<FallibleResult> {
self.execution_ctx.modify_visualization(id, expression, module)
self.execution_ctx.modify_visualization(id, method_pointer, arguments)
}

/// See [`model::ExecutionContext::detach_visualization`].
Expand All @@ -165,18 +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,
code: String,
module: model::module::QualifiedName,
) -> FallibleResult {
self.execution_ctx.modify_visualization(id, Some(code), Some(module)).await
}

/// See [`model::ExecutionContext::component_groups`].
pub fn component_groups(&self) -> Rc<Vec<ComponentGroup>> {
self.execution_ctx.component_groups()
Expand Down
Loading

0 comments on commit de0a231

Please sign in to comment.