diff --git a/ide/CHANGELOG.md b/ide/CHANGELOG.md index 7a531fb7e08f..c2780e3af5d1 100644 --- a/ide/CHANGELOG.md +++ b/ide/CHANGELOG.md @@ -119,6 +119,8 @@ load.][1413] - [Fixed a case where IDE could lose connection to the backend after some time.][1428] +- [Improved performance of the graph editor, particularly when opening a project + for a first time.][1445] #### EnsoGL (rendering engine) @@ -164,6 +166,7 @@ you can find their release notes [1428]: https://github.com/enso-org/ide/pull/1428 [1438]: https://github.com/enso-org/ide/pull/1438 [1367]: https://github.com/enso-org/ide/pull/1367 +[1445]: https://github.com/enso-org/ide/pull/1445
diff --git a/ide/src/rust/ide/src/ide/integration.rs b/ide/src/rust/ide/src/ide/integration.rs index b880aede27cf..1b262cbdc3db 100644 --- a/ide/src/rust/ide/src/ide/integration.rs +++ b/ide/src/rust/ide/src/ide/integration.rs @@ -182,6 +182,7 @@ struct Model { node_views : RefCell>, node_view_by_expression : RefCell>, expression_views : RefCell>, + expression_types : SharedHashMap>, connection_views : RefCell>, code_view : CloneRefCell, visualizations : SharedHashMap, @@ -427,13 +428,15 @@ impl Model { let node_view_by_expression = default(); let connection_views = default(); let expression_views = default(); + let expression_types = default(); let code_view = default(); let visualizations = default(); let error_visualizations = default(); let searcher = default(); let this = Model - {view,graph,text,searcher,node_views,expression_views,connection_views,code_view,logger - ,visualization,visualizations,error_visualizations,project,node_view_by_expression}; + {view,graph,text,searcher,node_views,expression_views,expression_types,connection_views + ,code_view,logger,visualization,visualizations,error_visualizations,project + ,node_view_by_expression}; this.init_project_name(); this.load_visualizations(); @@ -623,7 +626,9 @@ impl Model { input_span_tree : trees.inputs, output_span_tree : trees.outputs.unwrap_or_else(default) }; - if !self.expression_views.borrow().get(&id).contains(&&code_and_trees) { + let expression_changed = + !self.expression_views.borrow().get(&id).contains(&&code_and_trees); + if expression_changed { for sub_expression in node.info.ast().iter_recursive() { if let Some(expr_id) = sub_expression.id { self.node_view_by_expression.borrow_mut().insert(expr_id,id); @@ -636,7 +641,7 @@ impl Model { // Set initially available type information on ports (identifiable expression's sub-parts). for expression_part in node.info.expression().iter_recursive() { if let Some(id) = expression_part.id { - self.refresh_computed_info(id); + self.refresh_computed_info(id,expression_changed); } } } @@ -645,7 +650,7 @@ impl Model { fn refresh_computed_infos(&self, expressions_to_refresh:&[ExpressionId]) -> FallibleResult { debug!(self.logger, "Refreshing type information for IDs: {expressions_to_refresh:?}."); for id in expressions_to_refresh { - self.refresh_computed_info(*id) + self.refresh_computed_info(*id,false) } Ok(()) } @@ -654,12 +659,12 @@ impl Model { /// graph editor view. /// /// The computed value information includes the expression type and the target method pointer. - fn refresh_computed_info(&self, id:ExpressionId) { + fn refresh_computed_info(&self, id:ExpressionId, force_type_info_refresh:bool) { let info = self.lookup_computed_info(&id); let info = info.as_ref(); let typename = info.and_then(|info| info.typename.clone().map(graph_editor::Type)); if let Some(node_id) = self.node_view_by_expression.borrow().get(&id).cloned() { - self.set_type(node_id,id,typename); + self.set_type(node_id,id,typename, force_type_info_refresh); let method_pointer = info.and_then(|info| { info.method_call.and_then(|entry_id| { let opt_method = self.project.suggestion_db().lookup_method_ptr(entry_id).ok(); @@ -677,9 +682,20 @@ impl Model { } /// Set given type (or lack of such) on the given sub-expression. - fn set_type(&self, node_id:graph_editor::NodeId, id:ExpressionId, typename:Option) { - let event = (node_id,id,typename); - self.view.graph().frp.input.set_expression_usage_type.emit(&event); + fn set_type + ( &self + , node_id : graph_editor::NodeId + , id : ExpressionId + , typename : Option + , force_refresh : bool + ) { + // We suppress spurious type information updates here, as they were causing performance + // issues. See: https://github.com/enso-org/ide/issues/952 + let previous_type_opt = self.expression_types.insert(id,typename.clone()); + if force_refresh || previous_type_opt.as_ref() != Some(&typename) { + let event = (node_id,id,typename); + self.view.graph().frp.input.set_expression_usage_type.emit(&event); + } } /// Set given method pointer (or lack of such) on the given sub-expression. @@ -851,7 +867,7 @@ impl Model { use controller::graph::executed::Notification; use controller::graph::Notification::*; - debug!(self.logger, "Received notification {notification:?}"); + debug!(self.logger, "Received graph notification {notification:?}"); let result = match notification { Some(Notification::Graph(Invalidate)) => self.on_graph_invalidated(), Some(Notification::Graph(PortsUpdate)) => self.on_graph_expression_update(), @@ -874,7 +890,7 @@ impl Model { pub fn handle_text_notification(&self, notification:Option) { use controller::text::Notification; - debug!(self.logger, "Received notification {notification:?}"); + debug!(self.logger, "Received text notification {notification:?}"); let result = match notification { Some(Notification::Invalidate) => self.on_text_invalidated(), other => { @@ -892,6 +908,7 @@ impl Model { pub fn handle_searcher_notification(&self, notification:controller::searcher::Notification) { use controller::searcher::Notification; use controller::searcher::UserAction; + debug!(self.logger, "Received searcher notification {notification:?}"); match notification { Notification::NewActionList => with(self.searcher.borrow(), |searcher| { if let Some(searcher) = &*searcher { diff --git a/ide/src/rust/ide/view/graph-editor/src/component/node/input/port.rs b/ide/src/rust/ide/view/graph-editor/src/component/node/input/port.rs index 3ac163af6596..43f82f7efa73 100644 --- a/ide/src/rust/ide/view/graph-editor/src/component/node/input/port.rs +++ b/ide/src/rust/ide/view/graph-editor/src/component/node/input/port.rs @@ -132,7 +132,7 @@ ensogl::define_endpoints! { set_hover (bool), set_connected (bool,Option), set_parent_connected (bool), - set_definition_type (Option), + set_definition_type (Option), set_usage_type (Option), } diff --git a/ide/src/rust/ide/view/graph-editor/src/component/type_coloring.rs b/ide/src/rust/ide/view/graph-editor/src/component/type_coloring.rs index b913466a1d18..0e9382ae7714 100644 --- a/ide/src/rust/ide/view/graph-editor/src/component/type_coloring.rs +++ b/ide/src/rust/ide/view/graph-editor/src/component/type_coloring.rs @@ -42,12 +42,8 @@ use std::hash::Hasher; /// parametrization, other mechanisms should be used. For example, `Point Float` and `Point Number` /// should have similar colors, completely distinct from their parameter types. pub fn compute(tp:&Type, styles:&StyleWatch) -> color::Lcha { - // FIXME: Left for performance debug purposes. See: https://github.com/enso-org/ide/issues/952 - // println!("Type coloring for: '{}",tp.as_str()); let types_path = theme::code::types::overriden::HERE.path(); let type_path = types_path.into_subs(tp.as_str().split('.')); - // FIXME: Left for performance debug purposes. See: https://github.com/enso-org/ide/issues/952 - // println!("Path: {:?}",type_path); let hue = styles.get(type_path.sub("hue")).number_or_else(|| auto_hue(tp,styles)); let lightness = styles.get(type_path.sub("lightness")).number_or_else(|| styles.get_number_or(theme::code::types::lightness,0.85)); diff --git a/ide/src/rust/ide/view/graph-editor/src/lib.rs b/ide/src/rust/ide/view/graph-editor/src/lib.rs index a260994480c4..aa02d5df6091 100644 --- a/ide/src/rust/ide/view/graph-editor/src/lib.rs +++ b/ide/src/rust/ide/view/graph-editor/src/lib.rs @@ -3043,5 +3043,3 @@ impl display::Object for GraphEditor { self.model.display_object() } } - -