mirror of
https://github.com/enso-org/enso.git
synced 2024-12-18 11:21:37 +03:00
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: 461e6ae780
This commit is contained in:
parent
ef070434f3
commit
83c66d265a
@ -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<Ast> 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<Ast> 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<Ast> {
|
||||
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<Str: ToString>(name:Str) -> Ast {
|
||||
/// Creates an Ast node with Cons inside.
|
||||
pub fn cons<Str:ToString>(name:Str) -> Ast {
|
||||
let cons = Cons {name:name.to_string()};
|
||||
Ast::from(cons)
|
||||
}
|
||||
|
||||
pub fn var<Str: ToString>(name:Str) -> Ast {
|
||||
/// Creates an Ast node with Var inside and given ID.
|
||||
pub fn var_with_id<Name:Str>(name:Name, id:ID) -> Ast {
|
||||
let name = name.into();
|
||||
let var = Var{name};
|
||||
Ast::new(var,Some(id))
|
||||
}
|
||||
|
||||
pub fn var<Str:ToString>(name:Str) -> Ast {
|
||||
let var = Var{name:name.to_string()};
|
||||
Ast::from(var)
|
||||
}
|
||||
|
||||
pub fn opr<Str: ToString>(name:Str) -> Ast {
|
||||
pub fn opr<Str:ToString>(name:Str) -> Ast {
|
||||
let opr = Opr{name:name.to_string() };
|
||||
Ast::from(opr)
|
||||
}
|
||||
|
||||
pub fn prefix<Func:Into<Ast>, Arg:Into<Ast>>(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<Str0, Str1, Str2>(larg:Str0, opr:Str1, rarg:Str2) -> Ast
|
||||
where Str0: ToString
|
||||
, Str1: ToString
|
||||
, Str2: ToString {
|
||||
pub fn infix_var<Str0,Str1,Str2>(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);
|
||||
|
@ -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<T> = Result<T,failure::Error>;
|
||||
pub mod text;
|
||||
|
301
gui/src/rust/ide/src/controller/graph.rs
Normal file
301
gui/src/rust/ide/src/controller/graph.rs
Normal file
@ -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<NodeMetadata>,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===================
|
||||
// === 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<NodeMetadata>,
|
||||
/// ID to be given to the node.
|
||||
pub id : Option<ast::ID>,
|
||||
/// 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<RefCell<controller::notification::Publisher<controller::notification::Graph>>>,
|
||||
}
|
||||
|
||||
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<Handle> {
|
||||
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<double_representation::definition::DefinitionInfo> {
|
||||
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<Vec<double_representation::node::NodeInfo>> {
|
||||
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<double_representation::node::NodeInfo> {
|
||||
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<Node> {
|
||||
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<Vec<Node>> {
|
||||
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<Node> {
|
||||
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<controller::notification::Graph> {
|
||||
self.publisher.borrow_mut().0.subscribe()
|
||||
}
|
||||
|
||||
/// Retrieves metadata for the given node.
|
||||
pub fn node_metadata(&self, _id:ast::ID) -> FallibleResult<NodeMetadata> {
|
||||
// todo!()
|
||||
#[derive(Clone,Copy,Debug,Display,Fail)]
|
||||
struct NotImplemented;
|
||||
Err(NotImplemented.into())
|
||||
}
|
||||
|
||||
/// Update metadata for the given node.
|
||||
pub fn update_node_metadata<F>(&self, _id:ast::ID, _updater:F) -> FallibleResult<NodeMetadata>
|
||||
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<Test,Fut>(&mut self, code:impl Str, function_name:impl Str, test:Test)
|
||||
where Test : FnOnce(module::Handle,Handle) -> Fut + 'static,
|
||||
Fut : Future<Output=()> {
|
||||
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<Test,Fut>(&mut self, definition_body:impl Str, test:Test)
|
||||
where Test : FnOnce(module::Handle,Handle) -> Fut + 'static,
|
||||
Fut : Future<Output=()> {
|
||||
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");
|
||||
})
|
||||
}
|
||||
}
|
@ -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<notification::Text>,
|
||||
/// Publisher of "graph changed" notifications
|
||||
graph_notifications : notification::Publisher<notification::Graph>,
|
||||
graph_notifications : notification::Publisher<notification::Graphs>,
|
||||
/// 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<DefinitionInfo> {
|
||||
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<notification::Graph> {
|
||||
pub fn subscribe_graph_notifications(&mut self) -> Subscriber<notification::Graphs> {
|
||||
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> {
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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<Message:'static> Debug for Publisher<Message> {
|
||||
|
||||
|
||||
|
||||
|
||||
// =====================================
|
||||
// === 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,
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,8 +5,6 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::controller::FallibleResult;
|
||||
|
||||
use file_manager_client as fmc;
|
||||
use json_rpc::Transport;
|
||||
use parser::Parser;
|
||||
|
@ -5,7 +5,6 @@
|
||||
//! user.
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::controller::FallibleResult;
|
||||
use crate::controller::notification;
|
||||
use crate::executor::global::spawn;
|
||||
|
||||
|
@ -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<dyn Iterator<Item=&'a Ast> + 'a> {
|
||||
match self.ast.rarg.shape() {
|
||||
ast::Shape::Block(_) => self.ast.rarg.iter(),
|
||||
_ => Box::new(EmptyIterator::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
|
@ -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<Crumb>,
|
||||
}
|
||||
|
||||
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<DefinitionInfo> {
|
||||
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<Ast>,
|
||||
source : DefinitionInfo,
|
||||
/// Describes all known nodes in this graph (does not include special pseudo-nodes like graph
|
||||
/// inputs and outputs).
|
||||
pub nodes:Vec<node::NodeInfo>,
|
||||
pub nodes : Vec<node::NodeInfo>,
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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::*;
|
||||
|
@ -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;
|
||||
|
4
gui/src/rust/ide/utils/src/fail.rs
Normal file
4
gui/src/rust/ide/utils/src/fail.rs
Normal file
@ -0,0 +1,4 @@
|
||||
//! General-purpose code related to error handling.
|
||||
|
||||
/// General-purpose `Result` supporting any `Error`-compatible failures.
|
||||
pub type FallibleResult<T> = Result<T,failure::Error>;
|
@ -14,4 +14,5 @@ pub use enso_prelude as prelude;
|
||||
|
||||
pub mod channel;
|
||||
pub mod env;
|
||||
pub mod fail;
|
||||
pub mod test;
|
||||
|
Loading…
Reference in New Issue
Block a user