Skip to content

Commit

Permalink
Support source maps for JS-based visualizations (#3208)
Browse files Browse the repository at this point in the history
  • Loading branch information
vitvakatu authored Jan 11, 2022
1 parent b7c9833 commit 1daf1db
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 44 deletions.
36 changes: 36 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions app/gui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#### Visual Environment

- [Added support of source maps for JS-based visualizations.][3208]
- [Fixed histograms coloring and added a color legend.][3153]
- [Fixed broken node whose expression contains non-ASCII characters.][3166]
- [Fixed developer console warnings about views being created but not
Expand All @@ -17,6 +18,7 @@
[3181]: https://github.com/enso-org/enso/pull/3181
[3186]: https://github.com/enso-org/enso/pull/3186
[3193]: https://github.com/enso-org/enso/pull/3193
[3208]: https://github.com/enso-org/enso/pull/3208

# Enso 2.0.0-alpha.18 (2021-10-12)

Expand Down
27 changes: 22 additions & 5 deletions app/gui/src/controller/visualization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::constants::VISUALIZATION_DIRECTORY;
use engine_protocol::language_server;
use ide_view::graph_editor::component::visualization;
use ide_view::graph_editor::component::visualization::definition;
use ide_view::graph_editor::component::visualization::java_script::Sources;
use std::rc::Rc;


Expand Down Expand Up @@ -89,16 +90,19 @@ pub struct EmbeddedVisualizations {
pub struct Handle {
language_server_rpc: Rc<language_server::Connection>,
embedded_visualizations: Rc<RefCell<EmbeddedVisualizations>>,
logger: Logger,
}

impl Handle {
/// Creates a new visualization controller.
pub fn new(
language_server_rpc: Rc<language_server::Connection>,
embedded_visualizations: EmbeddedVisualizations,
logger: &Logger,
) -> Self {
let logger = logger.sub("VisualizationController");
let embedded_visualizations = Rc::new(RefCell::new(embedded_visualizations));
Self { language_server_rpc, embedded_visualizations }
Self { language_server_rpc, embedded_visualizations, logger }
}

async fn list_project_specific_visualizations(&self) -> FallibleResult<Vec<VisualizationPath>> {
Expand Down Expand Up @@ -155,7 +159,17 @@ impl Handle {
let js_code = self.language_server_rpc.read_file(path).await?.contents;
let wrap_error =
|err| Error::js_preparation_error(visualization.clone(), err).into();
visualization::java_script::Definition::new(project, &js_code)
let sources = if let Some(file_name) = path.file_name() {
let sources: &[(&str, &str)] = &[(file_name, &js_code)];
Sources::from_files(sources)
} else {
warning!(
self.logger,
"Unable to get a file name from {path}. Visualization source map will not be provided."
);
Sources::empty()
};
visualization::java_script::Definition::new(project, sources)
.map(Into::into)
.map_err(wrap_error)
}
Expand Down Expand Up @@ -233,7 +247,8 @@ mod tests {
let embedded_visualization = builtin::visualization::native::BubbleChart::definition();
embedded_visualizations
.insert("[Demo] Bubble Visualization".to_string(), embedded_visualization.clone());
let vis_controller = Handle::new(language_server, embedded_visualizations);
let logger = Logger::new("Mock logger");
let vis_controller = Handle::new(language_server, embedded_visualizations, &logger);

let visualizations = vis_controller.list_visualizations().await;
let visualizations = visualizations.expect("Couldn't list visualizations.");
Expand All @@ -247,8 +262,10 @@ mod tests {
assert_eq!(visualizations.len(), 3);

let owner = visualization::Project::CurrentProject;
let javascript_vis0 = js_vis::Definition::new(owner.clone_ref(), &file_content0);
let javascript_vis1 = js_vis::Definition::new(owner, &file_content1);
let sources_vis0 = Sources::from_files(&[("file0.js", &file_content0)]);
let javascript_vis0 = js_vis::Definition::new(owner.clone_ref(), sources_vis0);
let sources_vis1 = Sources::from_files(&[("file0.js", &file_content1)]);
let javascript_vis1 = js_vis::Definition::new(owner, sources_vis1);
let javascript_vis0 = javascript_vis0.expect("Couldn't create visualization class.");
let javascript_vis1 = javascript_vis1.expect("Couldn't create visualization class.");
let javascript_vis0: visualization::Definition = javascript_vis0.into();
Expand Down
2 changes: 1 addition & 1 deletion app/gui/src/model/project/synchronized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ impl Project {
let module_registry = default();
let execution_contexts = default();
let visualization =
controller::Visualization::new(language_server, embedded_visualizations);
controller::Visualization::new(language_server, embedded_visualizations, &logger);
let parser = Parser::new_or_panic();
let language_server = &*language_server_rpc;
let suggestion_db = SuggestionDatabase::create_synchronized(language_server);
Expand Down
4 changes: 3 additions & 1 deletion app/gui/view/debug_scene/visualization/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ fn constructor_graph() -> visualization::java_script::Definition {
return Graph
"#;
visualization::java_script::Definition::new_builtin(source).unwrap()
let mut sources = visualization::java_script::Sources::empty();
sources.add_file("demo.js", source);
visualization::java_script::Definition::new_builtin(sources).unwrap()
}

#[wasm_bindgen]
Expand Down
2 changes: 2 additions & 0 deletions app/gui/view/graph-editor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
analytics = { version = "0.1.0", path = "../../analytics" }
ast = { version = "0.1.0", path = "../../language/ast/impl" }
base64 = "0.13"
bimap = { version = "0.4.0" }
enso-config = { version = "0.1.0", path = "../../config" }
enso-frp = { version = "0.1.0", path = "../../../../lib/rust/frp" }
Expand All @@ -30,6 +31,7 @@ js-sys = { version = "0.3.28" }
nalgebra = { version = "0.26.1", features = ["serde-serialize"] }
serde_json = { version = "1.0" }
serde = { version = "1.0", features = ["derive"] }
sourcemap = "6.0"
uuid = { version = "0.8", features = ["serde", "v4", "wasm-bindgen"] }
wasm-bindgen = { version = "=0.2.58", features = ["nightly", "serde-serialize"] }

Expand Down
63 changes: 35 additions & 28 deletions app/gui/view/graph-editor/src/builtin/visualization/java_script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// These implementations are neither efficient nor pretty, but get the idea across.

use crate::component::visualization;
use crate::component::visualization::java_script::source::from_files;



Expand All @@ -12,77 +13,81 @@ use crate::component::visualization;

/// Return a `JavaScript` Table visualization.
pub fn table_visualization() -> visualization::java_script::FallibleDefinition {
let loading_scripts = include_str!("java_script/helpers/loading.js");
let scrollable = include_str!("java_script/helpers/scrollable.js");
let source = include_str!("java_script/table.js");
let source = format!("{}{}{}", loading_scripts, scrollable, source);
let source = from_files!(
"java_script/helpers/loading.js",
"java_script/helpers/scrollable.js",
"java_script/table.js"
);

visualization::java_script::Definition::new_builtin(source)
}

/// Return a `JavaScript` SQL visualization.
pub fn sql_visualization() -> visualization::java_script::FallibleDefinition {
let loading_scripts = include_str!("java_script/helpers/loading.js");
let scrollable = include_str!("java_script/helpers/scrollable.js");
let source = include_str!("java_script/sql.js");
let source = format!("{}{}{}", loading_scripts, scrollable, source);
let source = from_files!(
"java_script/helpers/loading.js",
"java_script/helpers/scrollable.js",
"java_script/sql.js"
);

visualization::java_script::Definition::new_builtin(source)
}

/// Return a `JavaScript` Scatter plot visualization.
pub fn scatter_plot_visualization() -> visualization::java_script::FallibleDefinition {
let loading_scripts = include_str!("java_script/helpers/loading.js");
let number = include_str!("java_script/helpers/number.js");
let source = include_str!("java_script/scatterPlot.js");
let source = format!("{}{}{}", loading_scripts, number, source);
let source = from_files!(
"java_script/helpers/loading.js",
"java_script/helpers/number.js",
"java_script/scatterPlot.js"
);

visualization::java_script::Definition::new_builtin(source)
}

/// Return a `JavaScript` Histogram visualization.
pub fn histogram_visualization() -> visualization::java_script::FallibleDefinition {
let loading_scripts = include_str!("java_script/helpers/loading.js");
let number = include_str!("java_script/helpers/number.js");
let source = include_str!("java_script/histogram.js");
let source = format!("{}{}{}", loading_scripts, number, source);
let source = from_files!(
"java_script/helpers/loading.js",
"java_script/helpers/number.js",
"java_script/histogram.js"
);

visualization::java_script::Definition::new_builtin(source)
}

/// Return a `JavaScript` Heatmap visualization.
pub fn heatmap_visualization() -> visualization::java_script::FallibleDefinition {
let loading_scripts = include_str!("java_script/helpers/loading.js");
let number = include_str!("java_script/helpers/number.js");
let source = include_str!("java_script/heatmap.js");
let source = format!("{}{}{}", loading_scripts, number, source);
let source = from_files!(
"java_script/helpers/loading.js",
"java_script/helpers/number.js",
"java_script/heatmap.js"
);

visualization::java_script::Definition::new_builtin(source)
}

/// Return a `JavaScript` Map visualization.
pub fn geo_map_visualization() -> visualization::java_script::FallibleDefinition {
let loading_scripts = include_str!("java_script/helpers/loading.js");
let number = include_str!("java_script/helpers/number.js");
let source = include_str!("java_script/geoMap.js");
let source = format!("{}{}{}", loading_scripts, number, source);
let source = from_files!(
"java_script/helpers/loading.js",
"java_script/helpers/number.js",
"java_script/geoMap.js"
);

visualization::java_script::Definition::new_builtin(source)
}

/// Return a `JavaScript` Bubble visualization. This should not be used as it is a demo
/// visualization.
pub fn bubble_visualization() -> visualization::java_script::FallibleDefinition {
let source = include_str!("java_script/bubbleVisualization.js");
let source = from_files!("java_script/bubbleVisualization.js");

visualization::java_script::Definition::new_builtin(source)
}

/// Return a `JavaScript` Image visualization.
pub fn image_base64_visualization() -> visualization::java_script::FallibleDefinition {
let loading_scripts = include_str!("java_script/helpers/loading.js");
let source = include_str!("java_script/imageBase64.js");
let source = format!("{}{}", loading_scripts, source);
let source = from_files!("java_script/helpers/loading.js", "java_script/imageBase64.js");

visualization::java_script::Definition::new_builtin(source)
}
Expand All @@ -93,6 +98,8 @@ pub fn empty_visualization() -> visualization::java_script::FallibleDefinition {
class EmptyVisualization extends Visualization {}
return EmptyVisualization;
"#;
let files = [("java_script/empty.js", source)];
let source = visualization::java_script::Sources::from_files(&files);

visualization::java_script::Definition::new_builtin(source)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
pub mod binding;
pub mod definition;
pub mod instance;
pub mod source;

pub use definition::*;
pub use instance::*;
pub use source::Sources;
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use super::instance::Instance;
use crate::component::visualization;
use crate::component::visualization::InstantiationError;
use crate::component::visualization::InstantiationResult;
use crate::visualization::foreign::java_script::Sources;

use ensogl::display::Scene;
use ensogl::system::web::JsValue;
Expand Down Expand Up @@ -59,14 +60,10 @@ pub struct Definition {

impl Definition {
/// Create a visualization source from piece of JS source code. Signature needs to be inferred.
pub fn new(
project: visualization::path::Project,
source: impl AsRef<str>,
) -> Result<Self, Error> {
let source = source.as_ref();
let source = source;
pub fn new(project: visualization::path::Project, sources: Sources) -> Result<Self, Error> {
let source = sources.to_string(&project);
let context = JsValue::NULL;
let function = Function::new_with_args(binding::JS_CLASS_NAME, source)
let function = Function::new_with_args(binding::JS_CLASS_NAME, &source)
.map_err(Error::InvalidFunction)?;
let js_class = binding::js_class();
let class = function.call1(&context, &js_class).map_err(Error::InvalidFunction)?;
Expand All @@ -84,8 +81,8 @@ impl Definition {
}

/// Create a definition of visualization that is built into the IDE.
pub fn new_builtin(source: impl AsRef<str>) -> Result<Self, Error> {
Self::new(visualization::path::Project::Builtin, source)
pub fn new_builtin(sources: Sources) -> Result<Self, Error> {
Self::new(visualization::path::Project::Builtin, sources)
}

fn new_instance(&self, scene: &Scene) -> InstantiationResult {
Expand Down
Loading

0 comments on commit 1daf1db

Please sign in to comment.