From 83c66d265a9a15e752c52926407e0d6825110d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Wawrzyniec=20Urba=C5=84czyk?= Date: Wed, 18 Mar 2020 08:43:52 +0100 Subject: [PATCH] Graph and Node controllers stub and mock implementations (https://github.com/enso-org/ide/pull/262) For early design / API review. Authored by michal.urbanczyk@luna-lang.org Original commit: https://github.com/enso-org/ide/commit/461e6ae780eb528dbf8f2e512c077a68b11fcf4a --- gui/src/rust/ide/ast/impl/src/lib.rs | 45 ++- gui/src/rust/ide/src/controller.rs | 6 +- gui/src/rust/ide/src/controller/graph.rs | 301 ++++++++++++++++++ gui/src/rust/ide/src/controller/module.rs | 34 +- .../rust/ide/src/controller/notification.rs | 26 +- gui/src/rust/ide/src/controller/project.rs | 2 - gui/src/rust/ide/src/controller/text.rs | 1 - .../src/double_representation/definition.rs | 20 +- .../ide/src/double_representation/graph.rs | 83 ++++- .../ide/src/double_representation/node.rs | 15 +- gui/src/rust/ide/src/lib.rs | 2 + gui/src/rust/ide/src/view/project.rs | 1 - gui/src/rust/ide/utils/src/fail.rs | 4 + gui/src/rust/ide/utils/src/lib.rs | 1 + 14 files changed, 484 insertions(+), 57 deletions(-) create mode 100644 gui/src/rust/ide/src/controller/graph.rs create mode 100644 gui/src/rust/ide/utils/src/fail.rs diff --git a/gui/src/rust/ide/ast/impl/src/lib.rs b/gui/src/rust/ide/ast/impl/src/lib.rs index 7ba5f4ab14..25900d2339 100644 --- a/gui/src/rust/ide/ast/impl/src/lib.rs +++ b/gui/src/rust/ide/ast/impl/src/lib.rs @@ -172,6 +172,8 @@ impl Clone for Ast { } } +impl CloneRef for Ast {} + /// `IntoIterator` for `&Ast` that just delegates to `&Shape`'s `IntoIterator`. impl<'t> IntoIterator for &'t Ast { type Item = <&'t Shape as IntoIterator>::Item; @@ -181,6 +183,24 @@ impl<'t> IntoIterator for &'t Ast { } } +impl ToString for Ast { + fn to_string(&self) -> String { + self.repr() + } +} + +impl From for String { + fn from(ast:Ast) -> Self { + ast.to_string() + } +} + +impl From<&Ast> for String { + fn from(ast:&Ast) -> Self { + ast.to_string() + } +} + impl Ast { pub fn shape(&self) -> &Shape { self @@ -916,37 +936,46 @@ impl Ast { // TODO smart constructors for other cases // as part of https://github.com/luna/enso/issues/338 + /// Creates an Ast node with Number inside. pub fn number(number:i64) -> Ast { let number = Number {base:None,int:number.to_string()}; Ast::from(number) } - pub fn cons(name:Str) -> Ast { + /// Creates an Ast node with Cons inside. + pub fn cons(name:Str) -> Ast { let cons = Cons {name:name.to_string()}; Ast::from(cons) } - pub fn var(name:Str) -> Ast { + /// Creates an Ast node with Var inside and given ID. + pub fn var_with_id(name:Name, id:ID) -> Ast { + let name = name.into(); + let var = Var{name}; + Ast::new(var,Some(id)) + } + + pub fn var(name:Str) -> Ast { let var = Var{name:name.to_string()}; Ast::from(var) } - pub fn opr(name:Str) -> Ast { + pub fn opr(name:Str) -> Ast { let opr = Opr{name:name.to_string() }; Ast::from(opr) } pub fn prefix, Arg:Into>(func:Func, arg:Arg) -> Ast { let off = 1; - let opr = Prefix{ func:func.into(), off, arg:arg.into() }; + let opr = Prefix { func:func.into(), off, arg:arg.into() }; Ast::from(opr) } /// Creates an AST node with `Infix` shape, where both its operands are Vars. - pub fn infix_var(larg:Str0, opr:Str1, rarg:Str2) -> Ast - where Str0: ToString - , Str1: ToString - , Str2: ToString { + pub fn infix_var(larg:Str0, opr:Str1, rarg:Str2) -> Ast + where Str0 : ToString + , Str1 : ToString + , Str2 : ToString { let larg = Ast::var(larg); let loff = 1; let opr = Ast::opr(opr); diff --git a/gui/src/rust/ide/src/controller.rs b/gui/src/rust/ide/src/controller.rs index 6b6038259f..81ad2bfcf0 100644 --- a/gui/src/rust/ide/src/controller.rs +++ b/gui/src/rust/ide/src/controller.rs @@ -15,10 +15,8 @@ //! Controllers store their handles using `utils::cell` handle types to ensure //! that mutable state is safely accessed. -pub mod text; +pub mod graph; pub mod module; pub mod notification; pub mod project; - -/// General-purpose `Result` supporting any `Error`-compatible failures. -pub type FallibleResult = Result; +pub mod text; diff --git a/gui/src/rust/ide/src/controller/graph.rs b/gui/src/rust/ide/src/controller/graph.rs new file mode 100644 index 0000000000..146adee7ed --- /dev/null +++ b/gui/src/rust/ide/src/controller/graph.rs @@ -0,0 +1,301 @@ +//! Graph Controller. +//! +//! This controller provides access to a specific graph. It lives under a module controller, as +//! each graph belongs to some module. + + +use crate::prelude::*; + +pub use crate::double_representation::graph::Id; + +use flo_stream::MessagePublisher; +use flo_stream::Subscriber; +use utils::channel::process_stream_with_handle; + + + +// ============== +// === Errors === +// ============== + +/// Error raised when node with given Id was not found in the graph's body. +#[derive(Clone,Copy,Debug,Fail)] +#[fail(display="Node with Id {} was not found.", _0)] +pub struct NodeNotFound(ast::ID); + + + +// ============ +// === Node === +// ============ + +/// TODO: replace with usage of the structure to be provided by Josef +#[derive(Clone,Copy,Debug)] +pub struct NodeMetadata; // here goes position + +/// Description of the node with all information available to the graph controller. +#[derive(Clone,Debug)] +pub struct Node { + /// Information based on AST, from double_representation module. + pub info : double_representation::node::NodeInfo, + /// Information about this node stored in the module's metadata. + pub metadata : Option, +} + + + +// =================== +// === NewNodeInfo === +// =================== + +/// Describes the node to be added. +#[derive(Clone,Debug)] +pub struct NewNodeInfo { + /// Expression to be placed on the node + pub expression : String, + /// Visual node position in the graph scene. + pub metadata : Option, + /// ID to be given to the node. + pub id : Option, + /// Where line created by adding this node should appear. + pub location_hint : LocationHint +} + +/// Describes the desired position of the node's line in the graph's code block. +#[derive(Clone,Copy,Debug)] +pub enum LocationHint { + /// Try placing this node's line before the line described by id. + Before(ast::ID), + /// Try placing this node's line after the line described by id. + After(ast::ID), + /// Try placing this node's line at the start of the graph's code block. + Start, + /// Try placing this node's line at the end of the graph's code block. + End, +} + + + +// ================== +// === Controller === +// ================== + +/// Handle providing graph controller interface. +#[derive(Clone,Debug)] +pub struct Handle { + /// Controller of the module which this graph belongs to. + module : controller::module::Handle, + id : Id, + /// Publisher. When creating a controller, it sets up task to emit notifications through this + /// publisher to relay changes from the module controller. + // TODO: [mwu] Remove in favor of streams mapping over centralized module-scope publisher + publisher : Rc>>, +} + +impl Handle { + /// Gets a handle to a controller of the module that this definition belongs to. + pub fn module(&self) -> controller::module::Handle { + self.module.clone_ref() + } + + /// Gets a handle to a controller of the module that this definition belongs to. + pub fn id(&self) -> Id { + self.id.clone() + } + + /// Creates a new controller. Does not check if id is valid. + /// + /// Requires global executor to spawn the events relay task. + pub fn new_unchecked(module:controller::module::Handle, id:Id) -> Handle { + let graphs_notifications = module.subscribe_graph_notifications(); + let publisher = default(); + let ret = Handle {module,id,publisher}; + let weak = Rc::downgrade(&ret.publisher); + let relay_notifications = process_stream_with_handle(graphs_notifications,weak, + |notification,this| { + match notification { + controller::notification::Graphs::Invalidate => + this.borrow_mut().publish(controller::notification::Graph::Invalidate), + } + }); + executor::global::spawn(relay_notifications); + ret + } + + /// Creates a new graph controller. Given ID should uniquely identify a definition in the + /// module. Fails if ID cannot be resolved. + /// + /// Requires global executor to spawn the events relay task. + pub fn new(module:controller::module::Handle, id:Id) -> FallibleResult { + let ret = Self::new_unchecked(module,id); + // Get and discard definition info, we are just making sure it can be obtained. + let _ = ret.graph_definition_info()?; + Ok(ret) + } + + /// Retrieves double rep information about definition providing this graph. + pub fn graph_definition_info + (&self) -> FallibleResult { + let module = self.module(); + let id = self.id(); + module.find_definition(&id) + } + + /// Returns double rep information about all nodes in the graph. + pub fn all_node_infos + (&self) -> FallibleResult> { + let definition = self.graph_definition_info()?; + let graph = double_representation::graph::GraphInfo::from_definition(definition); + Ok(graph.nodes) + } + + /// Retrieves double rep information about node with given ID. + pub fn node_info + (&self, id:ast::ID) -> FallibleResult { + let nodes = self.all_node_infos()?; + let node = nodes.into_iter().find(|node_info| node_info.id() == id); + node.ok_or_else(|| NodeNotFound(id).into()) + } + + /// Gets information about node with given id. + /// + /// Note that it is more efficient to use `get_nodes` to obtain all information at once, + /// rather then repeatedly call this method. + pub fn node(&self, id:ast::ID) -> FallibleResult { + let info = self.node_info(id)?; + let metadata = self.node_metadata(id).ok(); + Ok(Node {info,metadata}) + } + + /// Returns information about all the nodes currently present in this graph. + pub fn nodes(&self) -> FallibleResult> { + let node_infos = self.all_node_infos()?; + let mut nodes = Vec::new(); + for info in node_infos { + let metadata = self.node_metadata(info.id()).ok(); + nodes.push(Node {info,metadata}) + } + Ok(nodes) + } + + /// Adds a new node to the graph and returns information about created node. + pub fn add_node(&self, _node:NewNodeInfo) -> FallibleResult { + todo!() + } + + /// Removes the node with given Id. + pub fn remove_node(&self, _id:ast::ID) -> FallibleResult<()> { + todo!() + } + + /// Subscribe to updates about changes in this graph. + pub fn subscribe(&self) -> Subscriber { + self.publisher.borrow_mut().0.subscribe() + } + + /// Retrieves metadata for the given node. + pub fn node_metadata(&self, _id:ast::ID) -> FallibleResult { + // todo!() + #[derive(Clone,Copy,Debug,Display,Fail)] + struct NotImplemented; + Err(NotImplemented.into()) + } + + /// Update metadata for the given node. + pub fn update_node_metadata(&self, _id:ast::ID, _updater:F) -> FallibleResult + where F : FnOnce(&mut NodeMetadata) { + todo!() + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + use crate::double_representation::definition::DefinitionName; + use crate::executor::test_utils::TestWithLocalPoolExecutor; + use crate::controller::module; + use crate::controller::notification; + + use ast::HasRepr; + use data::text::Index; + use data::text::TextChange; + use json_rpc::test_util::transport::mock::MockTransport; + use parser::Parser; + use utils::test::ExpectTuple; + use wasm_bindgen_test::wasm_bindgen_test; + + struct GraphControllerFixture(TestWithLocalPoolExecutor); + impl GraphControllerFixture { + pub fn set_up() -> GraphControllerFixture { + let nested = TestWithLocalPoolExecutor::set_up(); + Self(nested) + } + + pub fn run_graph_for_program(&mut self, code:impl Str, function_name:impl Str, test:Test) + where Test : FnOnce(module::Handle,Handle) -> Fut + 'static, + Fut : Future { + let fm = file_manager_client::Handle::new(MockTransport::new()); + let loc = controller::module::Location("Main".to_string()); + let parser = Parser::new_or_panic(); + let module = controller::module::Handle::new_mock(loc,code.as_ref(),default(),fm,parser).unwrap(); + let graph_id = Id::new_single_crumb(DefinitionName::new_plain(function_name.into())); + let graph = module.get_graph_controller(graph_id).unwrap(); + self.0.run_test(async move { + test(module,graph).await + }) + } + + pub fn run_inline_graph(&mut self, definition_body:impl Str, test:Test) + where Test : FnOnce(module::Handle,Handle) -> Fut + 'static, + Fut : Future { + assert_eq!(definition_body.as_ref().contains('\n'), false); + let code = format!("main = {}", definition_body.as_ref()); + let name = "main"; + self.run_graph_for_program(code,name,test) + } + } + + #[wasm_bindgen_test] + fn graph_controller_notification_relay() { + let mut test = GraphControllerFixture::set_up(); + test.run_graph_for_program("main = 2 + 2", "main", |module,graph| async move { + let text_change = TextChange::insert(Index::new(12), "2".into()); + module.apply_code_change(&text_change).unwrap(); + + let mut sub = graph.subscribe(); + module.apply_code_change(&TextChange::insert(Index::new(1),"2".to_string())).unwrap(); + assert_eq!(Some(notification::Graph::Invalidate), sub.next().await); + }) + } + + #[wasm_bindgen_test] + fn graph_controller_inline_definition() { + let mut test = GraphControllerFixture::set_up(); + const EXPRESSION: &str = "2+2"; + test.run_inline_graph(EXPRESSION, |_,graph| async move { + let nodes = graph.nodes().unwrap(); + let (node,) = nodes.expect_tuple(); + assert_eq!(node.info.expression().repr(), EXPRESSION); + let id = node.info.id(); + let node = graph.node(id).unwrap(); + assert_eq!(node.info.expression().repr(), EXPRESSION); + }) + } + + #[wasm_bindgen_test] + fn graph_controller_block_definition() { + let mut test = GraphControllerFixture::set_up(); + let program = r" +main = + foo = 2 + print foo"; + test.run_graph_for_program(program, "main", |_,graph| async move { + let nodes = graph.nodes().unwrap(); + let (node1,node2) = nodes.expect_tuple(); + assert_eq!(node1.info.expression().repr(), "2"); + assert_eq!(node2.info.expression().repr(), "print foo"); + }) + } +} diff --git a/gui/src/rust/ide/src/controller/module.rs b/gui/src/rust/ide/src/controller/module.rs index 80830e75a4..58c444d53e 100644 --- a/gui/src/rust/ide/src/controller/module.rs +++ b/gui/src/rust/ide/src/controller/module.rs @@ -7,9 +7,9 @@ use crate::prelude::*; -use crate::controller::FallibleResult; use crate::controller::notification; use crate::double_representation::text::apply_code_change_to_id_map; +use crate::double_representation::definition::DefinitionInfo; use crate::executor::global::spawn; use parser::api::SourceFile; @@ -17,7 +17,9 @@ use ast; use ast::Ast; use ast::HasRepr; use ast::IdMap; +use ast::known; use data::text::*; +use double_representation as dr; use file_manager_client as fmc; use flo_stream::MessagePublisher; use flo_stream::Subscriber; @@ -108,7 +110,7 @@ shared! { Handle /// Publisher of "text changed" notifications text_notifications : notification::Publisher, /// Publisher of "graph changed" notifications - graph_notifications : notification::Publisher, + graph_notifications : notification::Publisher, /// The logger handle. logger: Logger, } @@ -140,6 +142,12 @@ shared! { Handle self.module.ast.repr() } + /// Obtains definition information for given graph id. + pub fn find_definition(&self,id:&dr::graph::Id) -> FallibleResult { + let module = known::Module::try_new(self.module.ast.clone())?; + double_representation::graph::traverse_for_definition(module,id) + } + /// Check if current module state is synchronized with given code. If it's not, log error, /// and update module state to match the `code` passed as argument. pub fn check_code_sync(&mut self, code:String) -> FallibleResult<()> { @@ -159,7 +167,7 @@ shared! { Handle } /// Get subscriber receiving notifications about changes in module's graph representation. - pub fn subscribe_graph_notifications(&mut self) -> Subscriber { + pub fn subscribe_graph_notifications(&mut self) -> Subscriber { self.graph_notifications.subscribe() } } @@ -178,8 +186,9 @@ impl Handle { let text_notifications = default(); let graph_notifications = default(); - let data = Controller {location,module,file_manager,parser,id_map,logger,text_notifications, - graph_notifications}; + + let data = Controller {location,module,file_manager,parser,id_map,logger, + text_notifications,graph_notifications}; let handle = Handle::new_from_data(data); handle.load_file().await?; Ok(handle) @@ -216,6 +225,13 @@ impl Handle { async move { fm.write(path.clone(),code?).await } } + /// Returns a graph controller for graph in this module's subtree identified by `id`. + /// Reuses already existing controller if possible. + pub fn get_graph_controller(&self, id:dr::graph::Id) + -> FallibleResult { + controller::graph::Handle::new(self.clone(),id) + } + #[cfg(test)] pub fn new_mock ( location : Location @@ -240,7 +256,7 @@ impl Controller { fn update_ast(&mut self,ast:Ast) { self.module.ast = ast; let text_change = notification::Text::Invalidate; - let graph_change = notification::Graph::Invalidate; + let graph_change = notification::Graphs::Invalidate; let code_notify = self.text_notifications.publish(text_change); let graph_notify = self.graph_notifications.publish(graph_change); spawn(async move { futures::join!(code_notify,graph_notify); }); @@ -248,6 +264,10 @@ impl Controller { } +// ============= +// === Tests === +// ============= + #[cfg(test)] mod test { use super::*; @@ -318,7 +338,7 @@ mod test { // Check emitted notifications assert_eq!(Some(notification::Text::Invalidate ), text_notifications.next().await ); - assert_eq!(Some(notification::Graph::Invalidate), graph_notifications.next().await); + assert_eq!(Some(notification::Graphs::Invalidate), graph_notifications.next().await); }); } } diff --git a/gui/src/rust/ide/src/controller/notification.rs b/gui/src/rust/ide/src/controller/notification.rs index 40875af311..a703b4039e 100644 --- a/gui/src/rust/ide/src/controller/notification.rs +++ b/gui/src/rust/ide/src/controller/notification.rs @@ -16,7 +16,6 @@ use crate::prelude::*; /// therefore there is no need for setting big buffers. const NOTIFICATION_BUFFER_SIZE : usize = 36; - /// A notification publisher which implements Debug and Default. #[derive(Shrinkwrap)] #[shrinkwrap(mutable)] @@ -36,7 +35,6 @@ impl Debug for Publisher { - // ===================================== // === Double Representation Changes === // ===================================== @@ -51,11 +49,33 @@ pub enum Text { } -// === Graph === +// === Graphs === /// A notification about changes of graph representation of a module. #[derive(Copy,Clone,Debug,Eq,PartialEq)] +pub enum Graphs { + /// The content should be fully reloaded. + Invalidate, +} + + +// === Graph === + +/// A notification about changes of a specific graph in a module. +#[derive(Copy,Clone,Debug,Eq,PartialEq)] pub enum Graph { /// The content should be fully reloaded. Invalidate, } + + +// === Node === + +/// A notification about changes of specific node in a graph. +#[derive(Copy,Clone,Debug,Eq,PartialEq)] +pub enum Node { + /// The content should be fully reloaded. + Invalidate, +} + + diff --git a/gui/src/rust/ide/src/controller/project.rs b/gui/src/rust/ide/src/controller/project.rs index 7a954ca4e3..f3fe30df5a 100644 --- a/gui/src/rust/ide/src/controller/project.rs +++ b/gui/src/rust/ide/src/controller/project.rs @@ -5,8 +5,6 @@ use crate::prelude::*; -use crate::controller::FallibleResult; - use file_manager_client as fmc; use json_rpc::Transport; use parser::Parser; diff --git a/gui/src/rust/ide/src/controller/text.rs b/gui/src/rust/ide/src/controller/text.rs index e06576ff03..bee903ed57 100644 --- a/gui/src/rust/ide/src/controller/text.rs +++ b/gui/src/rust/ide/src/controller/text.rs @@ -5,7 +5,6 @@ //! user. use crate::prelude::*; -use crate::controller::FallibleResult; use crate::controller::notification; use crate::executor::global::spawn; diff --git a/gui/src/rust/ide/src/double_representation/definition.rs b/gui/src/rust/ide/src/double_representation/definition.rs index ac17d58b99..b5632ab6ad 100644 --- a/gui/src/rust/ide/src/double_representation/definition.rs +++ b/gui/src/rust/ide/src/double_representation/definition.rs @@ -8,7 +8,7 @@ use ast::Shape; use ast::known; use ast::prefix; use ast::opr; - +use shapely::EmptyIterator; // ================= @@ -93,16 +93,16 @@ impl DefinitionName { } } -impl ToString for DefinitionName { - fn to_string(&self) -> String { +impl Display for DefinitionName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut pieces = self.extended_target.iter().map(|s| s.as_str()).collect_vec(); pieces.push(&self.name); - pieces.join(opr::predefined::ACCESS) + let text = pieces.join(opr::predefined::ACCESS); + write!(f, "{}", text) } } - // ====================== // === DefinitionInfo === // ====================== @@ -216,6 +216,16 @@ impl DefinitionProvider for known::Block { } } +impl DefinitionProvider for DefinitionInfo { + fn scope_kind() -> ScopeKind { ScopeKind::NonRoot } + fn line_asts<'a>(&'a self) -> Box + 'a> { + match self.ast.rarg.shape() { + ast::Shape::Block(_) => self.ast.rarg.iter(), + _ => Box::new(EmptyIterator::new()) + } + } +} + // ============= diff --git a/gui/src/rust/ide/src/double_representation/graph.rs b/gui/src/rust/ide/src/double_representation/graph.rs index 3a475e9333..03ca60ea52 100644 --- a/gui/src/rust/ide/src/double_representation/graph.rs +++ b/gui/src/rust/ide/src/double_representation/graph.rs @@ -3,6 +3,9 @@ use crate::prelude::*; use crate::double_representation::definition; +use crate::double_representation::definition::DefinitionInfo; +use crate::double_representation::definition::DefinitionName; +use crate::double_representation::definition::DefinitionProvider; use crate::double_representation::node; use ast::Ast; @@ -10,6 +13,59 @@ use ast::known; +// ================ +// === Graph Id === +// ================ + +/// Crumb describes step that needs to be done when going from context (for graph being a module) +/// to the target. +// TODO [mwu] +// Currently we support only entering named definitions. +pub type Crumb = DefinitionName; + +/// Identifies graph in the module. +#[derive(Clone,Debug,Eq,Hash,PartialEq)] +pub struct Id { + /// Sequence of traverses from module root up to the identified graph. + pub crumbs : Vec, +} + +impl Id { + /// Creates a new graph identifier consisting of a single crumb. + pub fn new_single_crumb(crumb:DefinitionName) -> Id { + let crumbs = vec![crumb]; + Id {crumbs} + } +} + + +// =============================== +// === Finding Graph In Module === +// =============================== + +#[derive(Fail,Clone,Debug)] +#[fail(display="Definition ID was empty")] +struct CannotFindDefinition(Id); + +#[derive(Fail,Clone,Debug)] +#[fail(display="Definition ID was empty")] +struct EmptyDefinitionId; + +/// Looks up graph in the module. +pub fn traverse_for_definition +(ast:ast::known::Module, id:&Id) -> FallibleResult { + let err = || CannotFindDefinition(id.clone()); + let mut crumb_iter = id.crumbs.iter(); + let first_crumb = crumb_iter.next().ok_or(EmptyDefinitionId)?; + let mut definition = ast.find_definition(first_crumb).ok_or_else(err)?; + for crumb in crumb_iter { + definition = definition.find_definition(crumb).ok_or_else(err)?; + } + Ok(definition) +} + + + // ================= // === GraphInfo === // ================= @@ -17,20 +73,17 @@ use ast::known; /// Description of the graph, based on information available in AST. #[derive(Clone,Debug)] pub struct GraphInfo { - name : definition::DefinitionName, - args : Vec, + source : DefinitionInfo, /// Describes all known nodes in this graph (does not include special pseudo-nodes like graph /// inputs and outputs). - pub nodes:Vec, + pub nodes : Vec, } impl GraphInfo { /// Describe graph of the given definition. - pub fn from_definition(info:&definition::DefinitionInfo) -> GraphInfo { - let name = info.name.clone(); - let args = info.args.clone(); - let nodes = Self::from_function_binding(info.ast.clone()); - GraphInfo {name,args,nodes} + pub fn from_definition(source:DefinitionInfo) -> GraphInfo { + let nodes = Self::from_function_binding(source.ast.clone()); + GraphInfo {source,nodes} } /// Lists nodes in the given binding's ast (infix expression). @@ -79,6 +132,7 @@ mod tests { use crate::double_representation::definition::DefinitionName; use crate::double_representation::definition::DefinitionProvider; + use ast::HasRepr; use parser::api::IsParser; use wasm_bindgen_test::wasm_bindgen_test; @@ -89,8 +143,7 @@ mod tests { let module = parser.parse_module(program.into(), default()).unwrap(); let name = DefinitionName::new_plain("main"); let main = module.find_definition(&name).unwrap(); - println!("{:?}",module); - GraphInfo::from_definition(&main) + GraphInfo::from_definition(main) } #[wasm_bindgen_test] @@ -107,7 +160,7 @@ mod tests { let graph = main_graph(&mut parser, program); assert_eq!(graph.nodes.len(), 1); let node = &graph.nodes[0]; - assert_eq!(node.expression_text(), "2+2"); + assert_eq!(node.expression().repr(), "2+2"); let _ = node.id(); // just to make sure it is available } } @@ -119,15 +172,13 @@ mod tests { main = foo = node foo a = not_node + Int.= a = node node "; - // TODO [mwu] - // Add case like `Int.= a = node` once https://github.com/luna/enso/issues/565 is fixed - let graph = main_graph(&mut parser, program); assert_eq!(graph.nodes.len(), 2); for node in graph.nodes.iter() { - assert_eq!(node.expression_text(), "node"); + assert_eq!(node.expression().repr(), "node"); } } -} \ No newline at end of file +} diff --git a/gui/src/rust/ide/src/double_representation/node.rs b/gui/src/rust/ide/src/double_representation/node.rs index 5419ea4e99..89d622504c 100644 --- a/gui/src/rust/ide/src/double_representation/node.rs +++ b/gui/src/rust/ide/src/double_representation/node.rs @@ -1,7 +1,6 @@ //! Code for node discovery and other node-related tasks. use ast::Ast; -use ast::HasRepr; use ast::ID; use ast::known; @@ -47,23 +46,18 @@ impl NodeInfo { /// Node's unique ID. pub fn id(&self) -> ID { - // Panic must not happen, as the only available constructor checks that + // Panic must not happen, as the only available constructors checks that // there is an ID present. - self.expression_ast().id.expect("Node AST must bear an ID") + self.expression().id.expect("Node AST must bear an ID") } /// AST of the node's expression. - pub fn expression_ast(&self) -> &Ast { + pub fn expression(&self) -> &Ast { match self { NodeInfo::Binding {infix} => &infix.rarg, NodeInfo::Expression{ast} => &ast, } } - - /// The node's expression textual representation. - pub fn expression_text(&self) -> String { - self.expression_ast().repr() - } } @@ -76,13 +70,14 @@ impl NodeInfo { mod tests { use super::*; + use ast::HasRepr; use wasm_bindgen_test::wasm_bindgen_test; wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); fn expect_node(ast:Ast, expression_text:&str, id:ID) { let node_info = NodeInfo::from_line_ast(&ast).expect("expected a node"); - assert_eq!(node_info.expression_text(),expression_text); + assert_eq!(node_info.expression().repr(),expression_text); assert_eq!(node_info.id(), id); } diff --git a/gui/src/rust/ide/src/lib.rs b/gui/src/rust/ide/src/lib.rs index 0466d500c7..77c2d3181c 100644 --- a/gui/src/rust/ide/src/lib.rs +++ b/gui/src/rust/ide/src/lib.rs @@ -35,6 +35,8 @@ pub mod prelude { pub use futures::Stream; pub use futures::StreamExt; pub use futures::task::LocalSpawnExt; + + pub use utils::fail::FallibleResult; } use crate::prelude::*; diff --git a/gui/src/rust/ide/src/view/project.rs b/gui/src/rust/ide/src/view/project.rs index 87f4f3505d..d9387720e7 100644 --- a/gui/src/rust/ide/src/view/project.rs +++ b/gui/src/rust/ide/src/view/project.rs @@ -4,7 +4,6 @@ use crate::prelude::*; use crate::view::layout::ViewLayout; -use crate::controller::FallibleResult; use ensogl::control::callback::CallbackHandle; use ensogl::control::io::keyboard::listener::KeyboardFrpBindings; diff --git a/gui/src/rust/ide/utils/src/fail.rs b/gui/src/rust/ide/utils/src/fail.rs new file mode 100644 index 0000000000..6ec0b27b42 --- /dev/null +++ b/gui/src/rust/ide/utils/src/fail.rs @@ -0,0 +1,4 @@ +//! General-purpose code related to error handling. + +/// General-purpose `Result` supporting any `Error`-compatible failures. +pub type FallibleResult = Result; diff --git a/gui/src/rust/ide/utils/src/lib.rs b/gui/src/rust/ide/utils/src/lib.rs index 862491e92e..3cb664d25d 100644 --- a/gui/src/rust/ide/utils/src/lib.rs +++ b/gui/src/rust/ide/utils/src/lib.rs @@ -14,4 +14,5 @@ pub use enso_prelude as prelude; pub mod channel; pub mod env; +pub mod fail; pub mod test;