mirror of
https://github.com/enso-org/enso.git
synced 2024-12-19 18:51:31 +03:00
Execution Context (https://github.com/enso-org/ide/pull/419)
This PR introduces Executed Graph Controller, which is a Graph Controller with additional info about execution context.
Original commit: 026a2585ae
This commit is contained in:
parent
f8bd0d56e2
commit
7d4529885b
@ -130,14 +130,14 @@ trait API {
|
||||
fn destroy_execution_context(&self, context_id:ContextId) -> ();
|
||||
|
||||
/// Move the execution context to a new location deeper down the stack.
|
||||
#[MethodInput=PushExecutionContextInput,rpc_name="executionContext/push",
|
||||
result=push_execution_context_result,set_result=set_push_execution_context_result]
|
||||
fn push_execution_context(&self, context_id:ContextId, stack_item:StackItem) -> ();
|
||||
#[MethodInput=PushToExecutionContextInput,rpc_name="executionContext/push",
|
||||
result=push_to_execution_context_result,set_result=set_push_to_execution_context_result]
|
||||
fn push_to_execution_context(&self, context_id:ContextId, stack_item:StackItem) -> ();
|
||||
|
||||
/// Move the execution context up the stack.
|
||||
#[MethodInput=PopExecutionContextInput,rpc_name="executionContext/pop",
|
||||
result=pop_execution_context_result,set_result=set_pop_execution_context_result]
|
||||
fn pop_execution_context(&self, context_id:ContextId) -> ();
|
||||
#[MethodInput=PopFromExecutionContextInput,rpc_name="executionContext/pop",
|
||||
result=pop_from_execution_context_result,set_result=set_pop_from_execution_context_result]
|
||||
fn pop_from_execution_context(&self, context_id:ContextId) -> ();
|
||||
|
||||
/// Attach a visualisation, potentially preprocessed by some arbitrary Enso code, to a given
|
||||
/// node in the program.
|
||||
|
@ -52,6 +52,7 @@ pub struct OpenTextFile {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[allow(missing_docs)]
|
||||
pub struct CreateExecutionContext {
|
||||
pub context_id : ContextId,
|
||||
pub can_modify : CapabilityRegistration,
|
||||
pub receives_updates : CapabilityRegistration
|
||||
}
|
||||
|
@ -322,12 +322,13 @@ fn test_execution_context() {
|
||||
let method = "executionContext/receivesUpdates".to_string();
|
||||
let receives_updates = CapabilityRegistration{method,register_options};
|
||||
let create_execution_context_response = response::CreateExecutionContext
|
||||
{can_modify,receives_updates};
|
||||
{context_id,can_modify,receives_updates};
|
||||
test_request(
|
||||
|client| client.create_execution_context(),
|
||||
"executionContext/create",
|
||||
json!({}),
|
||||
json!({
|
||||
"contextId" : "00000000-0000-0000-0000-000000000000",
|
||||
"canModify" : {
|
||||
"method" : "executionContext/canModify",
|
||||
"registerOptions" : {
|
||||
@ -354,7 +355,7 @@ fn test_execution_context() {
|
||||
let local_call = LocalCall {expression_id};
|
||||
let stack_item = StackItem::LocalCall(local_call);
|
||||
test_request(
|
||||
|client| client.push_execution_context(context_id,stack_item),
|
||||
|client| client.push_to_execution_context(context_id,stack_item),
|
||||
"executionContext/push",
|
||||
json!({
|
||||
"contextId" : "00000000-0000-0000-0000-000000000000",
|
||||
@ -367,7 +368,7 @@ fn test_execution_context() {
|
||||
()
|
||||
);
|
||||
test_request(
|
||||
|client| client.pop_execution_context(context_id),
|
||||
|client| client.pop_from_execution_context(context_id),
|
||||
"executionContext/pop",
|
||||
json!({"contextId":"00000000-0000-0000-0000-000000000000"}),
|
||||
unit_json.clone(),
|
||||
|
@ -24,6 +24,7 @@ pub struct Path {
|
||||
pub root_id:Uuid,
|
||||
/// Path's segments.
|
||||
pub segments:Vec<String>,
|
||||
|
||||
}
|
||||
|
||||
impl Display for Path {
|
||||
@ -34,12 +35,24 @@ impl Display for Path {
|
||||
}
|
||||
|
||||
impl Path {
|
||||
/// Returns the file name, i.e. the last segment if exists.
|
||||
pub fn file_name(&self) -> Option<&String> {
|
||||
self.segments.last()
|
||||
}
|
||||
|
||||
/// Returns the file extension, i.e. the part of last path segment after the last dot.
|
||||
/// Returns `None` is there is no segments or no dot in the last segment.
|
||||
pub fn extension(&self) -> Option<&str> {
|
||||
let segment = self.segments.last()?;
|
||||
let last_dot_index = segment.rfind('.')?;
|
||||
Some(&segment[last_dot_index + 1..])
|
||||
let name = self.file_name()?;
|
||||
let last_dot_index = name.rfind('.')?;
|
||||
Some(&name[last_dot_index + 1..])
|
||||
}
|
||||
|
||||
/// Returns the stem of filename, i.e. part of last segment without extension if present.
|
||||
pub fn file_stem(&self) -> Option<&str> {
|
||||
let name = self.file_name()?;
|
||||
let name_length = name.rfind('.').unwrap_or_else(|| name.len());
|
||||
Some(&name[..name_length])
|
||||
}
|
||||
|
||||
/// Constructs a new path from given root ID and segments.
|
||||
|
@ -60,25 +60,25 @@ macro_rules! make_rpc_methods {
|
||||
}
|
||||
|
||||
impl Client {
|
||||
/// Create a new client that will use given transport.
|
||||
pub fn new(transport:impl json_rpc::Transport + 'static) -> Self {
|
||||
let handler = RefCell::new(Handler::new(transport));
|
||||
Self { handler }
|
||||
}
|
||||
/// Create a new client that will use given transport.
|
||||
pub fn new(transport:impl json_rpc::Transport + 'static) -> Self {
|
||||
let handler = RefCell::new(Handler::new(transport));
|
||||
Self { handler }
|
||||
}
|
||||
|
||||
/// Asynchronous event stream with notification and errors.
|
||||
///
|
||||
/// On a repeated call, previous stream is closed.
|
||||
pub fn events(&self) -> impl Stream<Item = Event> {
|
||||
self.handler.borrow_mut().handler_event_stream()
|
||||
}
|
||||
/// Asynchronous event stream with notification and errors.
|
||||
///
|
||||
/// On a repeated call, previous stream is closed.
|
||||
pub fn events(&self) -> impl Stream<Item = Event> {
|
||||
self.handler.borrow_mut().handler_event_stream()
|
||||
}
|
||||
|
||||
/// Returns a future that performs any background, asynchronous work needed
|
||||
/// for this Client to correctly work. Should be continually run while the
|
||||
/// `Client` is used. Will end once `Client` is dropped.
|
||||
pub fn runner(&self) -> impl Future<Output = ()> {
|
||||
self.handler.borrow_mut().runner()
|
||||
}
|
||||
/// Returns a future that performs any background, asynchronous work needed
|
||||
/// for this Client to correctly work. Should be continually run while the
|
||||
/// `Client` is used. Will end once `Client` is dropped.
|
||||
pub fn runner(&self) -> impl Future<Output = ()> {
|
||||
self.handler.borrow_mut().runner()
|
||||
}
|
||||
}
|
||||
|
||||
impl API for Client {
|
||||
@ -112,15 +112,18 @@ macro_rules! make_rpc_methods {
|
||||
/// Mock used for tests.
|
||||
#[derive(Debug,Default)]
|
||||
pub struct MockClient {
|
||||
$($method_result : RefCell<HashMap<($($param_ty),*),Result<$result>>>,)*
|
||||
expect_all_calls : Cell<bool>,
|
||||
$($method_result : RefCell<HashMap<($($param_ty),*),Vec<Result<$result>>>>,)*
|
||||
}
|
||||
|
||||
impl API for MockClient {
|
||||
$(fn $method(&self $(,$param_name:$param_ty)*)
|
||||
-> std::pin::Pin<Box<dyn Future<Output=Result<$result>>>> {
|
||||
let mut result = self.$method_result.borrow_mut();
|
||||
let result = result.remove(&($($param_name),*)).unwrap();
|
||||
Box::pin(async move { result })
|
||||
let mut results = self.$method_result.borrow_mut();
|
||||
let params = ($($param_name),*);
|
||||
let result = results.get_mut(¶ms).and_then(|res| res.pop());
|
||||
let err = format!("Unrecognized call {} with params {:?}",$rpc_name,params);
|
||||
Box::pin(futures::future::ready(result.expect(err.as_str())))
|
||||
})*
|
||||
}
|
||||
|
||||
@ -128,9 +131,30 @@ macro_rules! make_rpc_methods {
|
||||
$(
|
||||
/// Sets `$method`'s result to be returned when it is called.
|
||||
pub fn $set_result(&self $(,$param_name:$param_ty)*, result:Result<$result>) {
|
||||
self.$method_result.borrow_mut().insert(($($param_name),*),result);
|
||||
let mut results = self.$method_result.borrow_mut();
|
||||
let mut entry = results.entry(($($param_name),*));
|
||||
entry.or_default().push(result);
|
||||
}
|
||||
)*
|
||||
|
||||
/// Mark all calls defined by `set_$method_result` as required. If client will be
|
||||
/// dropped without calling the test will fail.
|
||||
pub fn expect_all_calls(&self) {
|
||||
self.expect_all_calls.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MockClient {
|
||||
fn drop(&mut self) {
|
||||
if self.expect_all_calls.get() {
|
||||
$(
|
||||
for (params,results) in self.$method_result.borrow().iter() {
|
||||
assert!(results.is_empty(), "Didn't make expected call {} with \
|
||||
parameters {:?}",$rpc_name,params);
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,17 @@ pub mod module;
|
||||
pub mod project;
|
||||
pub mod text;
|
||||
|
||||
pub use graph::Handle as Graph;
|
||||
pub use module::Handle as Module;
|
||||
pub use project::Handle as Project;
|
||||
pub use text::Handle as Text;
|
||||
pub use graph::Handle as Graph;
|
||||
pub use graph::executed::Handle as ExecutedGraph;
|
||||
pub use module::Handle as Module;
|
||||
pub use project::Handle as Project;
|
||||
pub use text::Handle as Text;
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
// === Path ===
|
||||
// ============
|
||||
|
||||
/// Path to a file on disc, used across all controllers
|
||||
pub type FilePath = enso_protocol::language_server::Path;
|
||||
|
@ -2,6 +2,7 @@
|
||||
//!
|
||||
//! This controller provides access to a specific graph. It lives under a module controller, as
|
||||
//! each graph belongs to some module.
|
||||
pub mod executed;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
@ -23,7 +24,6 @@ use span_tree::SpanTree;
|
||||
use ast::crumbs::InfixCrumb;
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Errors ===
|
||||
// ==============
|
||||
@ -395,18 +395,14 @@ pub struct Handle {
|
||||
impl Handle {
|
||||
|
||||
/// 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:Rc<model::Module>, parser:Parser, id:Id) -> Handle {
|
||||
let id = Rc::new(id);
|
||||
let id = Rc::new(id);
|
||||
let logger = Logger::new(format!("Graph Controller {}", id));
|
||||
Handle {module,parser,id,logger}
|
||||
}
|
||||
|
||||
/// 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:Rc<model::Module>, parser:Parser, id:Id) -> FallibleResult<Handle> {
|
||||
let ret = Self::new_unchecked(module,parser,id);
|
||||
// Get and discard definition info, we are just making sure it can be obtained.
|
||||
@ -751,7 +747,7 @@ mod tests {
|
||||
Fut : Future<Output=()> {
|
||||
let code = code.as_ref();
|
||||
let ls = language_server::Connection::new_mock_rc(default());
|
||||
let path = controller::module::Path::new(default(),&["Main"]);
|
||||
let path = controller::module::Path::from_module_name("Main");
|
||||
let parser = Parser::new_or_panic();
|
||||
let module = controller::Module::new_mock(path,code,default(),ls,parser).unwrap();
|
||||
let graph_id = Id::new_single_crumb(DefinitionName::new_plain(function_name.into()));
|
||||
@ -766,7 +762,7 @@ mod tests {
|
||||
Fut : Future<Output=()> {
|
||||
let code = code.as_ref();
|
||||
let ls = language_server::Connection::new_mock_rc(default());
|
||||
let path = controller::module::Path::new(default(),&["Main"]);
|
||||
let path = controller::module::Path::from_module_name("Main");
|
||||
let parser = Parser::new_or_panic();
|
||||
let module = controller::Module::new_mock(path, code, default(), ls, parser).unwrap();
|
||||
let graph = module.graph_controller(graph_id).unwrap();
|
||||
|
30
gui/src/rust/ide/src/controller/graph/executed.rs
Normal file
30
gui/src/rust/ide/src/controller/graph/executed.rs
Normal file
@ -0,0 +1,30 @@
|
||||
//! A module with Executed Graph Controller.
|
||||
//!
|
||||
//! This controller provides operations on a specific graph with some execution context - these
|
||||
//! operations usually involves retrieving values on nodes: that's are i.e. operations on
|
||||
//! visualisations, retrieving types on ports, etc.
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::model::synchronized::ExecutionContext;
|
||||
|
||||
/// Handle providing executed graph controller interface.
|
||||
#[derive(Clone,CloneRef,Debug)]
|
||||
pub struct Handle {
|
||||
/// A handle to basic graph operations.
|
||||
pub graph : controller::Graph,
|
||||
execution_ctx : Rc<ExecutionContext>,
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
/// Create handle for given graph and execution context.
|
||||
///
|
||||
/// This takes ownership of execution context which will be shared between all copies of this
|
||||
/// handle; when all copies will be dropped, the execution context will be dropped as well
|
||||
/// (and will then removed from LanguageServer).
|
||||
pub fn new(graph:controller::Graph, execution_ctx:ExecutionContext) -> Self {
|
||||
let execution_ctx = Rc::new(execution_ctx);
|
||||
Handle{graph,execution_ctx}
|
||||
}
|
||||
|
||||
//TODO[ao] Here goes the methods requiring ContextId
|
||||
}
|
@ -7,7 +7,9 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::controller::FilePath;
|
||||
use crate::double_representation::text::apply_code_change_to_id_map;
|
||||
use crate::model::synchronized::ExecutionContext;
|
||||
|
||||
use ast;
|
||||
use ast::HasIdMap;
|
||||
@ -15,6 +17,23 @@ use data::text::*;
|
||||
use double_representation as dr;
|
||||
use enso_protocol::language_server;
|
||||
use parser::Parser;
|
||||
use failure::_core::fmt::Formatter;
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Errors ===
|
||||
// ==============
|
||||
|
||||
/// Error returned when module path is invalid, i.e. cannot obtain module name from it.
|
||||
#[derive(Clone,Copy,Debug,Fail)]
|
||||
#[fail(display="Invalid module path.")]
|
||||
pub struct InvalidModulePath {}
|
||||
|
||||
/// Error returned when graph id invalid.
|
||||
#[derive(Clone,Debug,Fail)]
|
||||
#[fail(display="Invalid graph id: {:?}.",_0)]
|
||||
pub struct InvalidGraphId(controller::graph::Id);
|
||||
|
||||
|
||||
|
||||
@ -23,7 +42,55 @@ use parser::Parser;
|
||||
// ============
|
||||
|
||||
/// Path identifying module's file in the Language Server.
|
||||
pub type Path = language_server::Path;
|
||||
#[derive(Clone,Debug,Eq,Hash,PartialEq)]
|
||||
pub struct Path {
|
||||
file_path : FilePath,
|
||||
}
|
||||
|
||||
impl Path {
|
||||
/// Create a path from the file path. Returns None if given path is not a valid module file.
|
||||
pub fn from_file_path(file_path:FilePath) -> Option<Self> {
|
||||
let has_proper_ext = file_path.extension() == Some(constants::LANGUAGE_FILE_EXTENSION);
|
||||
let capitalized_name = file_path.file_name()?.chars().next()?.is_uppercase();
|
||||
let is_module = has_proper_ext && capitalized_name;
|
||||
is_module.and_option_from(|| Some(Path{file_path}))
|
||||
}
|
||||
|
||||
/// Get the file path.
|
||||
pub fn file_path(&self) -> &FilePath {
|
||||
&self.file_path
|
||||
}
|
||||
|
||||
/// Get the module name from path.
|
||||
///
|
||||
/// The module name is a filename without extension.
|
||||
pub fn module_name(&self) -> &str {
|
||||
// The file stem existence should be checked during construction.
|
||||
self.file_path.file_stem().unwrap()
|
||||
}
|
||||
/// Create a module path consisting of a single segment, based on a given module name.
|
||||
#[cfg(test)]
|
||||
pub fn from_module_name(name:impl Str) -> Self {
|
||||
let name:String = name.into();
|
||||
let file_name = format!("{}.{}",name,constants::LANGUAGE_FILE_EXTENSION);
|
||||
let file_path = FilePath::new(default(),&[file_name]);
|
||||
Self::from_file_path(file_path).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<FilePath> for Path {
|
||||
type Error = InvalidModulePath;
|
||||
|
||||
fn try_from(value:FilePath) -> Result<Self, Self::Error> {
|
||||
Path::from_file_path(value).ok_or(InvalidModulePath{})
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Path {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.file_path, f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -59,7 +126,7 @@ impl Handle {
|
||||
/// Load or reload module content from file.
|
||||
pub async fn load_file(&self) -> FallibleResult<()> {
|
||||
self.logger.info(|| "Loading module file");
|
||||
let path = self.path.deref().clone();
|
||||
let path = self.path.file_path().clone();
|
||||
let content = self.language_server.client.read_file(path).await?.contents;
|
||||
self.logger.info(|| "Parsing code");
|
||||
// TODO[ao] We should not fail here when metadata are malformed, but discard them and set
|
||||
@ -73,7 +140,7 @@ impl Handle {
|
||||
|
||||
/// Save the module to file.
|
||||
pub fn save_file(&self) -> impl Future<Output=FallibleResult<()>> {
|
||||
let path = self.path.deref().clone();
|
||||
let path = self.path.file_path().clone();
|
||||
let ls = self.language_server.clone();
|
||||
let content = self.model.source_as_string();
|
||||
async move { Ok(ls.client.write_file(path,content?).await?) }
|
||||
@ -122,6 +189,20 @@ impl Handle {
|
||||
controller::Graph::new(self.model.clone_ref(), self.parser.clone_ref(), id)
|
||||
}
|
||||
|
||||
/// Returns a executed graph controller for graph in this module's subtree identified by id.
|
||||
/// The execution context will be rooted at definition of this graph.
|
||||
///
|
||||
/// This function wont check if the definition under id exists.
|
||||
pub async fn executed_graph_controller_unchecked
|
||||
(&self, id:dr::graph::Id) -> FallibleResult<controller::ExecutedGraph> {
|
||||
let definition_name = id.crumbs.last().cloned().ok_or_else(|| InvalidGraphId(id.clone()))?;
|
||||
let graph = self.graph_controller_unchecked(id);
|
||||
let language_server = self.language_server.clone_ref();
|
||||
let path = self.path.clone_ref();
|
||||
let execution_ctx = ExecutionContext::create(language_server,path,definition_name).await?;
|
||||
Ok(controller::ExecutedGraph::new(graph,execution_ctx))
|
||||
}
|
||||
|
||||
/// Returns a graph controller for graph in this module's subtree identified by `id` without
|
||||
/// checking if the graph exists.
|
||||
pub fn graph_controller_unchecked(&self, id:dr::graph::Id) -> controller::Graph {
|
||||
@ -167,17 +248,28 @@ mod test {
|
||||
use ast::BlockLine;
|
||||
use ast::Ast;
|
||||
use data::text::Span;
|
||||
use enso_protocol::language_server;
|
||||
use parser::Parser;
|
||||
use uuid::Uuid;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
|
||||
#[test]
|
||||
fn module_path_conversion() {
|
||||
let path = FilePath::new(default(), &["src","Main.enso"]);
|
||||
assert!(Path::from_file_path(path).is_some());
|
||||
|
||||
let path = FilePath::new(default(), &["src","Main.txt"]);
|
||||
assert!(Path::from_file_path(path).is_none());
|
||||
|
||||
let path = FilePath::new(default(), &["src","main.txt"]);
|
||||
assert!(Path::from_file_path(path).is_none());
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn update_ast_after_text_change() {
|
||||
TestWithLocalPoolExecutor::set_up().run_task(async {
|
||||
let ls = language_server::Connection::new_mock_rc(default());
|
||||
let parser = Parser::new().unwrap();
|
||||
let location = Path{root_id:default(),segments:vec!["Test".into()]};
|
||||
let location = Path::from_module_name("Test");
|
||||
|
||||
let uuid1 = Uuid::new_v4();
|
||||
let uuid2 = Uuid::new_v4();
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::controller::FilePath;
|
||||
|
||||
use enso_protocol::language_server;
|
||||
use parser::Parser;
|
||||
|
||||
@ -40,9 +42,8 @@ impl Handle {
|
||||
/// Returns a text controller for a given file path.
|
||||
///
|
||||
/// It supports both modules and plain text files.
|
||||
pub async fn text_controller
|
||||
(&self, path:language_server::Path) -> FallibleResult<controller::Text> {
|
||||
if is_path_to_module(&path) {
|
||||
pub async fn text_controller(&self, path:FilePath) -> FallibleResult<controller::Text> {
|
||||
if let Some(path) = controller::module::Path::from_file_path(path.clone()) {
|
||||
trace!(self.logger,"Obtaining controller for module {path}");
|
||||
let module = self.module_controller(path).await?;
|
||||
Ok(controller::Text::new_for_module(module))
|
||||
@ -77,16 +78,16 @@ impl Handle {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the given path looks like it is referring to module file.
|
||||
fn is_path_to_module(path:&language_server::Path) -> bool {
|
||||
path.extension() == Some(constants::LANGUAGE_FILE_EXTENSION)
|
||||
}
|
||||
|
||||
|
||||
// ============
|
||||
// === Test ===
|
||||
// ============
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use crate::controller::text::FilePath;
|
||||
use crate::executor::test_utils::TestWithLocalPoolExecutor;
|
||||
|
||||
use language_server::response;
|
||||
@ -96,28 +97,20 @@ mod test {
|
||||
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
|
||||
#[test]
|
||||
fn is_path_to_module_test() {
|
||||
let path = language_server::Path::new(default(), &["src","Main.enso"]);
|
||||
assert!(is_path_to_module(&path));
|
||||
|
||||
let path = language_server::Path::new(default(), &["src","Main.txt"]);
|
||||
assert_eq!(is_path_to_module(&path), false);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn obtain_module_controller() {
|
||||
let mut test = TestWithLocalPoolExecutor::set_up();
|
||||
test.run_task(async move {
|
||||
let path = ModulePath{root_id:default(),segments:vec!["TestLocation".into()]};
|
||||
let another_path = ModulePath{root_id:default(),segments:vec!["TestLocation2".into()]};
|
||||
let path = ModulePath::from_module_name("TestModule");
|
||||
let another_path = ModulePath::from_module_name("TestModule2");
|
||||
|
||||
let client = language_server::MockClient::default();
|
||||
let contents = "2+2".to_string();
|
||||
client.set_file_read_result(path.clone(),Ok(response::Read{contents}));
|
||||
let contents = "2 + 2".to_string();
|
||||
client.set_file_read_result(another_path.clone(),Ok(response::Read{contents}));
|
||||
let client = language_server::MockClient::default();
|
||||
let contents = "2+2".to_string();
|
||||
let file_path = path.file_path().clone();
|
||||
client.set_file_read_result(file_path,Ok(response::Read{contents}));
|
||||
let file_path = another_path.file_path().clone();
|
||||
let contents = "2 + 2".to_string();
|
||||
client.set_file_read_result(file_path,Ok(response::Read{contents}));
|
||||
let connection = language_server::Connection::new_mock(client);
|
||||
let project = controller::Project::new(connection);
|
||||
let module = project.module_controller(path.clone()).await.unwrap();
|
||||
@ -136,8 +129,8 @@ mod test {
|
||||
let connection = language_server::Connection::new_mock(default());
|
||||
let project_ctrl = controller::Project::new(connection);
|
||||
let root_id = default();
|
||||
let path = FilePath{root_id,segments:vec!["TestPath".into()]};
|
||||
let another_path = FilePath{root_id,segments:vec!["TestPath2".into()]};
|
||||
let path = FilePath::new(root_id,&["TestPath"]);
|
||||
let another_path = FilePath::new(root_id,&["TestPath2"]);
|
||||
|
||||
let text_ctrl = project_ctrl.text_controller(path.clone()).await.unwrap();
|
||||
let another_ctrl = project_ctrl.text_controller(another_path.clone()).await.unwrap();
|
||||
@ -156,7 +149,7 @@ mod test {
|
||||
let mut test = TestWithLocalPoolExecutor::set_up();
|
||||
test.run_task(async move {
|
||||
let file_name = format!("test.{}",constants::LANGUAGE_FILE_EXTENSION);
|
||||
let path = ModulePath{root_id:default(),segments:vec![file_name]};
|
||||
let path = FilePath::new(default(),&[file_name]);
|
||||
let contents = "2 + 2".to_string();
|
||||
|
||||
let client = language_server::MockClient::default();
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::controller::FilePath;
|
||||
use crate::notification;
|
||||
|
||||
use data::text::TextChange;
|
||||
@ -15,15 +16,6 @@ use std::pin::Pin;
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
// === Path ===
|
||||
// ============
|
||||
|
||||
/// Path to a file on disc.
|
||||
pub type FilePath = language_server::Path;
|
||||
|
||||
|
||||
|
||||
// =======================
|
||||
// === Text Controller ===
|
||||
// =======================
|
||||
@ -68,7 +60,7 @@ impl Handle {
|
||||
pub fn file_path(&self) -> &FilePath {
|
||||
match &self.file {
|
||||
FileHandle::PlainText{path,..} => &*path,
|
||||
FileHandle::Module{controller} => &*controller.path,
|
||||
FileHandle::Module{controller} => controller.path.file_path()
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,7 +154,7 @@ mod test {
|
||||
let mut test = TestWithLocalPoolExecutor::set_up();
|
||||
test.run_task(async move {
|
||||
let ls = language_server::Connection::new_mock_rc(default());
|
||||
let path = FilePath{root_id:default(),segments:vec!["test".into()]};
|
||||
let path = controller::module::Path::from_module_name("Test");
|
||||
let parser = Parser::new().unwrap();
|
||||
let module_res = controller::Module::new_mock(path,"main = 2+2",default(),ls,parser);
|
||||
let module = module_res.unwrap();
|
||||
|
@ -13,6 +13,8 @@ use ast::known;
|
||||
use utils::fail::FallibleResult;
|
||||
use crate::double_representation::connection::Connection;
|
||||
|
||||
|
||||
|
||||
/// Graph uses the same `Id` as the definition which introduces the graph.
|
||||
pub type Id = double_representation::definition::Id;
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
//! The module with structures describing models shared between different controllers.
|
||||
|
||||
pub mod execution_context;
|
||||
pub mod module;
|
||||
pub mod synchronized;
|
||||
|
||||
pub use execution_context::ExecutionContext;
|
||||
pub use module::Module;
|
||||
|
93
gui/src/rust/ide/src/model/execution_context.rs
Normal file
93
gui/src/rust/ide/src/model/execution_context.rs
Normal file
@ -0,0 +1,93 @@
|
||||
//! This module consists of all structures describing Execution Context.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::double_representation::definition::DefinitionName;
|
||||
|
||||
use enso_protocol::language_server;
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Errors ===
|
||||
// ==============
|
||||
|
||||
/// Error then trying to pop stack item on ExecutionContext when there only root call remains.
|
||||
#[derive(Clone,Copy,Debug,Fail)]
|
||||
#[fail(display="Tried to pop an entry point")]
|
||||
pub struct PopOnEmptyStack {}
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === StackItem ===
|
||||
// =================
|
||||
|
||||
/// An identifier of called definition in module.
|
||||
pub type DefinitionId = crate::double_representation::definition::Id;
|
||||
/// An identifier of expression.
|
||||
pub type ExpressionId = ast::Id;
|
||||
|
||||
/// A specific function call occurring within another function's definition body.
|
||||
///
|
||||
/// This is a single item in ExecutionContext stack.
|
||||
#[derive(Clone,Debug,Eq,PartialEq)]
|
||||
pub struct LocalCall {
|
||||
/// An expression being a call.
|
||||
pub call : ExpressionId,
|
||||
/// A definition of function called in `call` expression.
|
||||
pub definition : DefinitionId,
|
||||
}
|
||||
|
||||
/// An identifier of ExecutionContext.
|
||||
pub type Id = language_server::ContextId;
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Model ===
|
||||
// =============
|
||||
|
||||
/// Execution Context Model.
|
||||
///
|
||||
/// The execution context consists of the root call (which is a direct call of some function
|
||||
/// definition) and stack of function calls (see `StackItem` definition and docs).
|
||||
///
|
||||
/// It implements internal mutability pattern, so the state may be shared between different
|
||||
/// controllers.
|
||||
#[derive(Debug)]
|
||||
pub struct ExecutionContext {
|
||||
/// A name of definition which is a root call of this context.
|
||||
pub entry_point : DefinitionName,
|
||||
stack : RefCell<Vec<LocalCall>>,
|
||||
//TODO[ao] I think we can put here info about visualisation set as well.
|
||||
}
|
||||
|
||||
impl ExecutionContext {
|
||||
/// Create new execution context
|
||||
pub fn new(entry_point:DefinitionName) -> Self {
|
||||
let stack = default();
|
||||
Self {entry_point,stack}
|
||||
}
|
||||
|
||||
/// Push a new stack item to execution context.
|
||||
pub fn push(&self, stack_item:LocalCall) {
|
||||
self.stack.borrow_mut().push(stack_item);
|
||||
}
|
||||
|
||||
/// Pop the last stack item from this context. It returns error when only root call
|
||||
/// remains.
|
||||
pub fn pop(&self) -> FallibleResult<()> {
|
||||
self.stack.borrow_mut().pop().ok_or(PopOnEmptyStack{})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get an iterator over stack items.
|
||||
///
|
||||
/// Because this struct implements _internal mutability pattern_, the stack can actually change
|
||||
/// during iteration. It should not panic, however might give an unpredictable result.
|
||||
pub fn stack_items<'a>(&'a self) -> impl Iterator<Item=LocalCall> + 'a {
|
||||
let stack_size = self.stack.borrow().len();
|
||||
(0..stack_size).filter_map(move |i| self.stack.borrow().get(i).cloned())
|
||||
}
|
||||
}
|
@ -168,7 +168,7 @@ mod test {
|
||||
let state = Rc::new(model::Module::new(ast.try_into().unwrap(),default()));
|
||||
let registry = Rc::new(Registry::default());
|
||||
let expected = state.clone_ref();
|
||||
let path = ModulePath::new(default(),&["test"]);
|
||||
let path = ModulePath::from_module_name("Test");
|
||||
|
||||
let loader = async move { Ok(state) };
|
||||
let module = registry.get_or_load(path.clone(),loader).await.unwrap();
|
||||
@ -188,7 +188,7 @@ mod test {
|
||||
let state2 = state1.clone_ref();
|
||||
let registry1 = Rc::new(Registry::default());
|
||||
let registry2 = registry1.clone_ref();
|
||||
let path1 = ModulePath::new(default(),&["test"]);
|
||||
let path1 = ModulePath::from_module_name("Test");
|
||||
let path2 = path1.clone();
|
||||
|
||||
let (loaded_send, loaded_recv) = futures::channel::oneshot::channel::<()>();
|
||||
|
6
gui/src/rust/ide/src/model/synchronized.rs
Normal file
6
gui/src/rust/ide/src/model/synchronized.rs
Normal file
@ -0,0 +1,6 @@
|
||||
//! This module contains synchronising wrappers for models whose state are a reflection of
|
||||
//! Language Server state, e.g. modules, execution contexts etc. These wrappers synchronize both
|
||||
//! states by notifying Language Server of every change and listening on LanguageServer.
|
||||
pub mod execution_context;
|
||||
|
||||
pub use execution_context::ExecutionContext;
|
222
gui/src/rust/ide/src/model/synchronized/execution_context.rs
Normal file
222
gui/src/rust/ide/src/model/synchronized/execution_context.rs
Normal file
@ -0,0 +1,222 @@
|
||||
//! A ExecutionContext model which is synchronized with LanguageServer state.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::double_representation::definition::DefinitionName;
|
||||
use crate::model::execution_context::LocalCall;
|
||||
|
||||
use enso_protocol::language_server;
|
||||
use json_rpc::error::RpcError;
|
||||
|
||||
|
||||
|
||||
// ==========================
|
||||
// === Synchronized Model ===
|
||||
// ==========================
|
||||
|
||||
/// An ExecutionContext model synchronized with LanguageServer. It will be automatically removed
|
||||
/// from LS once dropped.
|
||||
#[derive(Debug)]
|
||||
pub struct ExecutionContext {
|
||||
id : model::execution_context::Id,
|
||||
model : model::ExecutionContext,
|
||||
module_path : Rc<controller::module::Path>,
|
||||
language_server : Rc<language_server::Connection>,
|
||||
logger : Logger,
|
||||
}
|
||||
|
||||
impl ExecutionContext {
|
||||
/// Create new ExecutionContext. It will be created in LanguageServer and the ExplicitCall
|
||||
/// stack frame will be pushed.
|
||||
pub async fn create
|
||||
( language_server : Rc<language_server::Connection>
|
||||
, module_path : Rc<controller::module::Path>
|
||||
, root_definition : DefinitionName
|
||||
) -> FallibleResult<Self> {
|
||||
let logger = Logger::new("ExecutionContext");
|
||||
let model = model::ExecutionContext::new(root_definition);
|
||||
trace!(logger,"Creating Execution Context.");
|
||||
let id = language_server.client.create_execution_context().await?.context_id;
|
||||
trace!(logger,"Execution Context created. Id:{id}");
|
||||
let this = Self {id,module_path,model,language_server,logger};
|
||||
this.push_root_frame().await?;
|
||||
trace!(this.logger,"Pushed root frame");
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
fn push_root_frame(&self) -> impl Future<Output=FallibleResult<()>> {
|
||||
let method_pointer = language_server::MethodPointer {
|
||||
file : self.module_path.file_path().clone(),
|
||||
defined_on_type : self.module_path.module_name().to_string(),
|
||||
name : self.model.entry_point.name.item.clone(),
|
||||
};
|
||||
let this_argument_expression = default();
|
||||
let positional_arguments_expressions = default();
|
||||
|
||||
let call = language_server::ExplicitCall {method_pointer,this_argument_expression,
|
||||
positional_arguments_expressions};
|
||||
let frame = language_server::StackItem::ExplicitCall(call);
|
||||
let result = self.language_server.push_to_execution_context(self.id,frame);
|
||||
result.map(|res| res.map_err(|err| err.into()))
|
||||
}
|
||||
|
||||
/// Push a new stack item to execution context.
|
||||
pub fn push(&self, stack_item: LocalCall) -> impl Future<Output=Result<(),RpcError>> {
|
||||
let expression_id = stack_item.call;
|
||||
let call = language_server::LocalCall{expression_id};
|
||||
let frame = language_server::StackItem::LocalCall(call);
|
||||
self.model.push(stack_item);
|
||||
self.language_server.push_to_execution_context(self.id,frame)
|
||||
}
|
||||
|
||||
/// Pop the last stack item from this context. It returns error when only root call
|
||||
/// remains.
|
||||
pub async fn pop(&self) -> FallibleResult<()> {
|
||||
self.model.pop()?;
|
||||
self.language_server.pop_from_execution_context(self.id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a mock which does no call on `language_server` during construction.
|
||||
#[cfg(test)]
|
||||
pub fn new_mock
|
||||
( id : model::execution_context::Id
|
||||
, path : controller::module::Path
|
||||
, model : model::ExecutionContext
|
||||
, language_server : language_server::MockClient
|
||||
) -> Self {
|
||||
let module_path = Rc::new(path);
|
||||
let language_server = language_server::Connection::new_mock_rc(language_server);
|
||||
let logger = Logger::new("ExecuctionContext mock");
|
||||
ExecutionContext {id,model,module_path,language_server,logger}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ExecutionContext {
|
||||
fn drop(&mut self) {
|
||||
let id = self.id;
|
||||
let ls = self.language_server.clone_ref();
|
||||
let logger = self.logger.clone_ref();
|
||||
executor::global::spawn(async move {
|
||||
let result = ls.client.destroy_execution_context(id).await;
|
||||
if result.is_err() {
|
||||
error!(logger,"Error when destroying Execution Context: {result:?}.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Tests ===
|
||||
// =============
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use crate::executor::test_utils::TestWithLocalPoolExecutor;
|
||||
|
||||
use language_server::response;
|
||||
use utils::test::ExpectTuple;
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn creating_context() {
|
||||
let path = Rc::new(controller::module::Path::from_module_name("Test"));
|
||||
let context_id = model::execution_context::Id::new_v4();
|
||||
let root_def = DefinitionName::new_plain("main");
|
||||
let ls_client = language_server::MockClient::default();
|
||||
ls_client.set_create_execution_context_result(Ok(response::CreateExecutionContext {
|
||||
context_id,
|
||||
can_modify : create_capability("executionContext/canModify",context_id),
|
||||
receives_updates : create_capability("executionContext/receivesUpdates",context_id),
|
||||
}));
|
||||
let expected_method = language_server::MethodPointer {
|
||||
file : path.file_path().clone(),
|
||||
defined_on_type : "Test".to_string(),
|
||||
name : "main".to_string(),
|
||||
};
|
||||
let expected_root_frame = language_server::ExplicitCall {
|
||||
method_pointer : expected_method,
|
||||
this_argument_expression : None,
|
||||
positional_arguments_expressions : vec![]
|
||||
};
|
||||
let expected_stack_item = language_server::StackItem::ExplicitCall(expected_root_frame);
|
||||
ls_client.set_push_to_execution_context_result(context_id,expected_stack_item,Ok(()));
|
||||
ls_client.set_destroy_execution_context_result(context_id,Ok(()));
|
||||
ls_client.expect_all_calls();
|
||||
let connection = language_server::Connection::new_mock_rc(ls_client);
|
||||
|
||||
let mut test = TestWithLocalPoolExecutor::set_up();
|
||||
test.run_task(async move {
|
||||
let context = ExecutionContext::create(connection,path.clone(),root_def).await.unwrap();
|
||||
assert_eq!(context_id , context.id);
|
||||
assert_eq!(path , context.module_path);
|
||||
assert_eq!(Vec::<LocalCall>::new(), context.model.stack_items().collect_vec());
|
||||
})
|
||||
}
|
||||
|
||||
fn create_capability
|
||||
(method:impl Str, context_id:model::execution_context::Id)
|
||||
-> language_server::CapabilityRegistration {
|
||||
language_server::CapabilityRegistration {
|
||||
method : method.into(),
|
||||
register_options : language_server::RegisterOptions::ExecutionContextId {context_id},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pushing_stack_item() {
|
||||
let id = model::execution_context::Id::new_v4();
|
||||
let definition = model::execution_context::DefinitionId::new_plain_name("foo");
|
||||
let expression_id = model::execution_context::ExpressionId::new_v4();
|
||||
let path = controller::module::Path::from_module_name("Test");
|
||||
let root_def = DefinitionName::new_plain("main");
|
||||
let model = model::ExecutionContext::new(root_def);
|
||||
let ls = language_server::MockClient::default();
|
||||
let expected_call_frame = language_server::LocalCall{expression_id};
|
||||
let expected_stack_item = language_server::StackItem::LocalCall(expected_call_frame);
|
||||
|
||||
ls.set_push_to_execution_context_result(id,expected_stack_item,Ok(()));
|
||||
ls.set_destroy_execution_context_result(id,Ok(()));
|
||||
let context = ExecutionContext::new_mock(id,path.clone(),model,ls);
|
||||
|
||||
let mut test = TestWithLocalPoolExecutor::set_up();
|
||||
test.run_task(async move {
|
||||
let item = LocalCall {
|
||||
call : expression_id,
|
||||
definition : definition.clone()
|
||||
};
|
||||
context.push(item.clone()).await.unwrap();
|
||||
assert_eq!((item,), context.model.stack_items().expect_tuple());
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn popping_stack_item() {
|
||||
let id = model::execution_context::Id::new_v4();
|
||||
let item = LocalCall {
|
||||
call : model::execution_context::ExpressionId::new_v4(),
|
||||
definition : model::execution_context::DefinitionId::new_plain_name("foo"),
|
||||
};
|
||||
let path = controller::module::Path::from_module_name("Test");
|
||||
let root_def = DefinitionName::new_plain("main");
|
||||
let ls = language_server::MockClient::default();
|
||||
let model = model::ExecutionContext::new(root_def);
|
||||
ls.set_pop_from_execution_context_result(id,Ok(()));
|
||||
ls.set_destroy_execution_context_result(id,Ok(()));
|
||||
model.push(item);
|
||||
let context = ExecutionContext::new_mock(id,path.clone(),model,ls);
|
||||
|
||||
let mut test = TestWithLocalPoolExecutor::set_up();
|
||||
test.run_task(async move {
|
||||
context.pop().await.unwrap();
|
||||
assert_eq!(Vec::<LocalCall>::new(), context.model.stack_items().collect_vec());
|
||||
// Pop on empty stack.
|
||||
assert!(context.pop().await.is_err());
|
||||
})
|
||||
}
|
||||
}
|
@ -91,15 +91,15 @@ impl ViewLayout {
|
||||
( logger : &Logger
|
||||
, kb_actions : &mut keyboard::Actions
|
||||
, application : &Application
|
||||
, text_controller : controller::text::Handle
|
||||
, graph_controller : controller::graph::Handle
|
||||
, text_controller : controller::Text
|
||||
, graph_controller : controller::ExecutedGraph
|
||||
, fonts : &mut FontRegistry
|
||||
) -> Self {
|
||||
let logger = logger.sub("ViewLayout");
|
||||
let world = &application.display;
|
||||
let text_editor = TextEditor::new(&logger,world,text_controller,kb_actions,fonts);
|
||||
let node_editor = NodeEditor::new(&logger,application,graph_controller.clone());
|
||||
let node_searcher = NodeSearcher::new(world,&logger,graph_controller,fonts);
|
||||
let node_editor = NodeEditor::new(&logger,application,graph_controller.clone_ref());
|
||||
let node_searcher = NodeSearcher::new(world,&logger,graph_controller.graph.clone_ref(),fonts);
|
||||
world.add_child(&text_editor.display_object());
|
||||
world.add_child(&node_editor);
|
||||
world.add_child(&node_searcher);
|
||||
|
@ -27,7 +27,7 @@ use weak_table::weak_value_hash_map::Entry::{Occupied, Vacant};
|
||||
pub struct GraphEditorIntegration {
|
||||
pub logger : Logger,
|
||||
pub editor : GraphEditor,
|
||||
pub controller : controller::Graph,
|
||||
pub controller : controller::ExecutedGraph,
|
||||
id_to_node : RefCell<WeakValueHashMap<ast::Id,WeakNode>>,
|
||||
node_to_id : RefCell<WeakKeyHashMap<WeakNode,ast::Id>>,
|
||||
|
||||
@ -35,7 +35,7 @@ pub struct GraphEditorIntegration {
|
||||
|
||||
impl GraphEditorIntegration {
|
||||
/// Constructor. It creates GraphEditor panel and connect it with given controller handle.
|
||||
pub fn new(logger:Logger, app:&Application, controller:controller::Graph) -> Rc<Self> {
|
||||
pub fn new(logger:Logger, app:&Application, controller:controller::ExecutedGraph) -> Rc<Self> {
|
||||
let editor = app.views.new::<GraphEditor>();
|
||||
let id_to_node = default();
|
||||
let node_to_id = default();
|
||||
@ -43,12 +43,15 @@ impl GraphEditorIntegration {
|
||||
Self::setup_controller_event_handling(&this);
|
||||
Self::setup_keyboard_event_handling(&this);
|
||||
Self::setup_mouse_event_handling(&this);
|
||||
if let Err(err) = this.invalidate_graph() {
|
||||
error!(this.logger,"Error while initializing graph display: {err}");
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
/// Reloads whole displayed content to be up to date with module state.
|
||||
pub fn invalidate_graph(&self) -> FallibleResult<()> {
|
||||
let nodes = self.controller.nodes()?;
|
||||
let nodes = self.controller.graph.nodes()?;
|
||||
let ids = nodes.iter().map(|node| node.info.id() ).collect();
|
||||
self.retain_ids(&ids);
|
||||
for (i,node_info) in nodes.iter().enumerate() {
|
||||
@ -71,7 +74,7 @@ impl GraphEditorIntegration {
|
||||
}
|
||||
|
||||
fn setup_controller_event_handling(this:&Rc<Self>) {
|
||||
let stream = this.controller.subscribe();
|
||||
let stream = this.controller.graph.subscribe();
|
||||
let weak = Rc::downgrade(this);
|
||||
let handler = process_stream_with_handle(stream,weak,move |notification,this| {
|
||||
let result = match notification {
|
||||
@ -97,7 +100,7 @@ impl GraphEditorIntegration {
|
||||
this.editor.nodes.selected.for_each(|node_id| {
|
||||
let id = this.node_to_id.borrow().get(&node_id.0).cloned(); // FIXME .0
|
||||
if let Some(id) = id {
|
||||
if let Err(err) = this.controller.remove_node(id) {
|
||||
if let Err(err) = this.controller.graph.remove_node(id) {
|
||||
this.logger.error(|| format!("ERR: {:?}", err));
|
||||
}
|
||||
}
|
||||
@ -119,7 +122,7 @@ impl GraphEditorIntegration {
|
||||
if let Some((node_pos,this)) = node_pos.and_then(|n| this.map(|t| (n,t))) {
|
||||
let id = this.node_to_id.borrow().get(&node_id.0).cloned(); // FIXME .0
|
||||
if let Some(id) = id {
|
||||
this.controller.module.with_node_metadata(id, |md| {
|
||||
this.controller.graph.module.with_node_metadata(id, |md| {
|
||||
md.position = Some(model::module::Position::new(node_pos.x, node_pos.y));
|
||||
})
|
||||
}
|
||||
@ -153,12 +156,12 @@ impl GraphEditorIntegration {
|
||||
pub struct NodeEditor {
|
||||
display_object : display::object::Instance,
|
||||
graph : Rc<GraphEditorIntegration>,
|
||||
controller : controller::graph::Handle,
|
||||
controller : controller::ExecutedGraph,
|
||||
}
|
||||
|
||||
impl NodeEditor {
|
||||
/// Create Node Editor Panel.
|
||||
pub fn new(logger:&Logger, app:&Application, controller:controller::graph::Handle) -> Self {
|
||||
pub fn new(logger:&Logger, app:&Application, controller:controller::ExecutedGraph) -> Self {
|
||||
let logger = logger.sub("NodeEditor");
|
||||
let graph = GraphEditorIntegration::new(logger,app,controller.clone_ref());
|
||||
let display_object = display::object::Instance::new(&graph.logger);
|
||||
|
@ -73,12 +73,13 @@ impl ProjectView {
|
||||
pub async fn new(logger:&Logger, controller:controller::Project)
|
||||
-> FallibleResult<Self> {
|
||||
let root_id = controller.language_server_rpc.content_root();
|
||||
let path = controller::module::Path::new(root_id,&INITIAL_FILE_PATH);
|
||||
let path = controller::FilePath::new(root_id,&INITIAL_FILE_PATH);
|
||||
let text_controller = controller.text_controller(path.clone()).await?;
|
||||
let main_name = DefinitionName::new_plain(MAIN_DEFINITION_NAME);
|
||||
let graph_id = controller::graph::Id::new_single_crumb(main_name);
|
||||
let module_controller = controller.module_controller(path).await?;
|
||||
let graph_controller = module_controller.graph_controller_unchecked(graph_id);
|
||||
let module_controller = controller.module_controller(path.try_into()?).await?;
|
||||
let graph_controller = module_controller.executed_graph_controller_unchecked(graph_id);
|
||||
let graph_controller = graph_controller.await?;
|
||||
let application = Application::new(&web::get_html_element_by_id("root").unwrap());
|
||||
let _world = &application.display;
|
||||
// graph::register_shapes(&world);
|
||||
@ -88,10 +89,10 @@ impl ProjectView {
|
||||
let mut keyboard_actions = keyboard::Actions::new(&keyboard);
|
||||
let resize_callback = None;
|
||||
let mut fonts = FontRegistry::new();
|
||||
let layout = ViewLayout::new
|
||||
(&logger,&mut keyboard_actions,&application,text_controller,graph_controller,&mut fonts);
|
||||
let data = ProjectViewData
|
||||
{application,layout,resize_callback,controller,keyboard,keyboard_bindings,keyboard_actions};
|
||||
let layout = ViewLayout::new(&logger,&mut keyboard_actions,&application,
|
||||
text_controller,graph_controller,&mut fonts);
|
||||
let data = ProjectViewData {application,layout,resize_callback,controller,keyboard,
|
||||
keyboard_bindings,keyboard_actions};
|
||||
Ok(Self::new_from_data(data).init())
|
||||
}
|
||||
|
||||
|
@ -101,10 +101,10 @@ async fn file_operations() {
|
||||
let explicit_call = ExplicitCall
|
||||
{method_pointer,positional_arguments_expressions,this_argument_expression};
|
||||
let stack_item = StackItem::ExplicitCall(explicit_call);
|
||||
let response = client.push_execution_context(execution_context_id,stack_item).await;
|
||||
let response = client.push_to_execution_context(execution_context_id,stack_item).await;
|
||||
response.expect("Couldn't push execution context.");
|
||||
|
||||
let response = client.pop_execution_context(execution_context_id).await;
|
||||
let response = client.pop_from_execution_context(execution_context_id).await;
|
||||
response.expect("Couldn't pop execution context.");
|
||||
|
||||
let visualisation_id = uuid::Uuid::new_v4();
|
||||
|
Loading…
Reference in New Issue
Block a user