From 84c7acf1f537ff4ae5c2d6f2b9f3378384ad54f7 Mon Sep 17 00:00:00 2001 From: Adam Obuchowicz Date: Fri, 25 Jun 2021 11:09:41 +0200 Subject: [PATCH 1/9] Namespaces --- src/rust/ide/lib/args/src/lib.rs | 1 + .../lib/enso-protocol/src/project_manager.rs | 92 +++++++---- src/rust/ide/lib/parser/build.rs | 2 +- src/rust/ide/lib/parser/tests/macros.rs | 6 +- src/rust/ide/src/config.rs | 15 +- src/rust/ide/src/controller/graph.rs | 23 +-- src/rust/ide/src/controller/graph/executed.rs | 2 + src/rust/ide/src/controller/ide.rs | 2 +- src/rust/ide/src/controller/ide/desktop.rs | 6 +- src/rust/ide/src/controller/ide/plain.rs | 14 +- src/rust/ide/src/controller/module.rs | 4 +- src/rust/ide/src/controller/project.rs | 22 +-- src/rust/ide/src/controller/searcher.rs | 75 +++++---- .../ide/src/controller/searcher/action.rs | 2 +- src/rust/ide/src/double_representation.rs | 1 + .../ide/src/double_representation/module.rs | 79 ++++----- .../ide/src/double_representation/project.rs | 153 ++++++++++++++++++ src/rust/ide/src/double_representation/tp.rs | 32 ++-- src/rust/ide/src/ide/initializer.rs | 17 +- .../ide/src/model/execution_context/plain.rs | 6 +- src/rust/ide/src/model/module.rs | 16 +- src/rust/ide/src/model/project.rs | 25 ++- .../ide/src/model/project/synchronized.rs | 71 ++++---- .../src/model/suggestion_database/entry.rs | 8 +- src/rust/ide/src/test.rs | 33 ++-- src/rust/ide/tests/language_server.rs | 16 +- 26 files changed, 496 insertions(+), 227 deletions(-) create mode 100644 src/rust/ide/src/double_representation/project.rs diff --git a/src/rust/ide/lib/args/src/lib.rs b/src/rust/ide/lib/args/src/lib.rs index ace721d6ba..7bb626fd0f 100644 --- a/src/rust/ide/lib/args/src/lib.rs +++ b/src/rust/ide/lib/args/src/lib.rs @@ -27,6 +27,7 @@ ensogl::read_args! { project_manager : String, language_server_rpc : String, language_server_data : String, + namespace : String, platform : web::platform::Platform, frame : bool, theme : String, diff --git a/src/rust/ide/lib/enso-protocol/src/project_manager.rs b/src/rust/ide/lib/enso-protocol/src/project_manager.rs index 299657a664..7f1740b5f6 100644 --- a/src/rust/ide/lib/enso-protocol/src/project_manager.rs +++ b/src/rust/ide/lib/enso-protocol/src/project_manager.rs @@ -121,13 +121,18 @@ impl From for String { /// Project information, such as name, its id and last time it was opened. #[derive(Debug,Clone,Serialize,Deserialize,PartialEq)] +#[serde(rename_all="camelCase")] pub struct ProjectMetadata { /// Project's name. - pub name : ProjectName, + pub name:ProjectName, + /// Project's namespace, + pub namespace:String, /// Project's uuid. - pub id : Uuid, + pub id:Uuid, + /// Engine version to use for the project, represented by a semver version string. + pub engine_version:String, /// Last time the project was opened. - pub last_opened : Option + pub last_opened:Option } /// This type specifies what action should be taken if an Engine's component required to complete @@ -167,11 +172,15 @@ pub mod response { #[serde(rename_all="camelCase")] pub struct OpenProject { /// The version of the started language server represented by a semver version string. - pub engine_version : String, + pub engine_version:String, /// Address of the endpoint for JSON-RPC communication. - pub language_server_json_address : IpWithSocket, + pub language_server_json_address:IpWithSocket, /// Address of the endpoint for binary FlatBuffers communication. - pub language_server_binary_address : IpWithSocket, + pub language_server_binary_address:IpWithSocket, + /// The name of the project as it is opened. + pub project_name:ProjectName, + /// The namespace of the project. + pub project_namespace:String, } } @@ -218,6 +227,8 @@ mod mock_client_tests { engine_version : "0.2.1".to_owned(), language_server_json_address : language_server_address.clone(), language_server_binary_address : language_server_address, + project_name : ProjectName::new("Test"), + project_namespace : "local".to_owned(), }; let open_result = Ok(expected_open_result.clone()); let missing_component_action = MissingComponentAction::Fail; @@ -255,25 +266,33 @@ mod mock_client_tests { fn list_projects() { let mock_client = MockClient::default(); let project1 = ProjectMetadata { - name : ProjectName::new("project1"), - id : Uuid::default(), - last_opened : Some(DateTime::parse_from_rfc3339("2020-01-07T21:25:26Z").unwrap()) + name : ProjectName::new("project1"), + id : Uuid::default(), + last_opened : Some(DateTime::parse_from_rfc3339("2020-01-07T21:25:26Z").unwrap()), + engine_version : "0.2.21".to_owned(), + namespace : "local".to_owned(), }; let project2 = ProjectMetadata { name : ProjectName::new("project2"), id : Uuid::default(), - last_opened : Some(DateTime::parse_from_rfc3339("2020-02-02T13:15:20Z").unwrap()) + last_opened : Some(DateTime::parse_from_rfc3339("2020-02-02T13:15:20Z").unwrap()), + engine_version : "0.2.22".to_owned(), + namespace : "local".to_owned(), }; let expected_recent_projects = response::ProjectList { projects : vec![project1,project2] }; let sample1 = ProjectMetadata { - name : ProjectName::new("sample1"), - id : Uuid::default(), - last_opened : Some(DateTime::parse_from_rfc3339("2019-11-23T05:30:12Z").unwrap()) + name : ProjectName::new("sample1"), + id : Uuid::default(), + last_opened : Some(DateTime::parse_from_rfc3339("2019-11-23T05:30:12Z").unwrap()), + engine_version : "0.2.21".to_owned(), + namespace : "test".to_owned(), }; let sample2 = ProjectMetadata { - name : ProjectName::new("sample2"), - id : Uuid::default(), - last_opened : Some(DateTime::parse_from_rfc3339("2019-12-25T00:10:58Z").unwrap()) + name : ProjectName::new("sample2"), + id : Uuid::default(), + last_opened : Some(DateTime::parse_from_rfc3339("2019-12-25T00:10:58Z").unwrap()), + engine_version : "0.2.21".to_owned(), + namespace : "test".to_owned(), }; let expected_sample_projects = response::ProjectList { projects : vec![sample1,sample2] }; expect_call!(mock_client.list_projects(count=Some(2)) => @@ -370,8 +389,11 @@ mod remote_client_tests { let engine_version = "0.2.1".to_owned(); let language_server_json_address = IpWithSocket{host:"localhost".to_string(),port:27015}; let language_server_binary_address = IpWithSocket{host:"localhost".to_string(),port:27016}; + let project_name = ProjectName::new("Test"); + let project_namespace = "test_ns".to_owned(); let open_result = response::OpenProject {engine_version - ,language_server_json_address,language_server_binary_address}; + ,language_server_json_address,language_server_binary_address,project_name + ,project_namespace}; let open_result_json = json!({ "engineVersion" : "0.2.1", "languageServerJsonAddress" : { @@ -381,7 +403,9 @@ mod remote_client_tests { "languageServerBinaryAddress" : { "host" : "localhost", "port" : 27016 - } + }, + "projectName" : "Test", + "projectNamespace" : "test_ns", }); let project_name = String::from("HelloWorld"); let project_create_json = json!({ @@ -393,27 +417,35 @@ mod remote_client_tests { let number_of_projects_json = json!({"numberOfProjects":number_of_projects}); let num_projects_json = json!({"numProjects":number_of_projects}); let project1 = ProjectMetadata { - name : ProjectName::new("project1"), - id : Uuid::default(), - last_opened : Some(DateTime::parse_from_rfc3339("2020-01-07T21:25:26Z").unwrap()) + name : ProjectName::new("project1"), + id : Uuid::default(), + last_opened : Some(DateTime::parse_from_rfc3339("2020-01-07T21:25:26Z").unwrap()), + engine_version : "0.2.21".to_owned(), + namespace : "local".to_owned(), }; let project2 = ProjectMetadata { - name : ProjectName::new("project2"), - id : Uuid::default(), - last_opened : Some(DateTime::parse_from_rfc3339("2020-02-02T13:15:20Z").unwrap()) + name : ProjectName::new("project2"), + id : Uuid::default(), + last_opened : Some(DateTime::parse_from_rfc3339("2020-02-02T13:15:20Z").unwrap()), + engine_version : "0.2.22".to_owned(), + namespace : "local".to_owned(), }; let project_list = response::ProjectList { projects : vec![project1,project2] }; let project_list_json = json!({ "projects" : [ { - "id" : "00000000-0000-0000-0000-000000000000", - "last_opened" : "2020-01-07T21:25:26+00:00", - "name" : "project1" + "id" : "00000000-0000-0000-0000-000000000000", + "lastOpened" : "2020-01-07T21:25:26+00:00", + "name" : "project1", + "engineVersion" : "0.2.21", + "namespace" : "local" }, { - "id" : "00000000-0000-0000-0000-000000000000", - "last_opened" : "2020-02-02T13:15:20+00:00", - "name" : "project2" + "id" : "00000000-0000-0000-0000-000000000000", + "lastOpened" : "2020-02-02T13:15:20+00:00", + "name" : "project2", + "engineVersion" : "0.2.22", + "namespace" : "local" } ] }); diff --git a/src/rust/ide/lib/parser/build.rs b/src/rust/ide/lib/parser/build.rs index 67c62af935..48d8872fac 100644 --- a/src/rust/ide/lib/parser/build.rs +++ b/src/rust/ide/lib/parser/build.rs @@ -25,7 +25,7 @@ use std::path::PathBuf; const PARSER_PATH: &str = "./pkg/scala-parser.js"; /// Commit from `enso` repository that will be used to obtain parser from. -const PARSER_COMMIT: &str = "e53ee305d18d070c715205ba4a5758f53b4ef23b"; +const PARSER_COMMIT: &str = "44ef29a3b70ccada491ac71e199f323211c997d3"; /// Magic code that needs to be prepended to ScalaJS generated parser due to: /// https://github.com/scala-js/scala-js/issues/3677/ diff --git a/src/rust/ide/lib/parser/tests/macros.rs b/src/rust/ide/lib/parser/tests/macros.rs index 0d0153be24..f164843017 100644 --- a/src/rust/ide/lib/parser/tests/macros.rs +++ b/src/rust/ide/lib/parser/tests/macros.rs @@ -15,7 +15,7 @@ fn import_utilities() { let parser = Parser::new_or_panic(); let expect_import = |code:&str| { let ast = parser.parse_line(code).unwrap(); - assert!(is_ast_import(&ast)); + assert!(is_ast_import(&ast), "Not Ast import: {:?}", ast); let ast_match = ast_as_import_match(&ast).unwrap(); assert_eq!(&ast,ast_match.ast()); assert!(is_match_import(&ast_match)); @@ -29,10 +29,12 @@ fn import_utilities() { expect_import("import"); expect_import("import Foo"); + expect_import("import foo.Foo.Bar"); + expect_import("import foo.Foo.Bar"); expect_import("import Foo.Bar"); expect_import("import Foo.Bar.Baz"); expect_import("from Foo import Bar"); - expect_import("from Foo import all hiding Bar"); + expect_import("from foo.Foo import all hiding Bar"); expect_import("from Base.Data.List import all hiding Cons, Nil"); expect_not_import("type Foo"); diff --git a/src/rust/ide/src/config.rs b/src/rust/ide/src/config.rs index 101893c2e8..f2b0dcd0a1 100644 --- a/src/rust/ide/src/config.rs +++ b/src/rust/ide/src/config.rs @@ -41,6 +41,7 @@ pub enum BackendService { LanguageServer { json_endpoint : String, binary_endpoint : String, + namespace : String, } } @@ -62,15 +63,17 @@ impl BackendService { Ok(Self::ProjectManager {endpoint}) } } else { - match (&args.language_server_rpc,&args.language_server_data) { - (Some(json_endpoint),Some(binary_endpoint)) => { + match (&args.language_server_rpc,&args.language_server_data,&args.namespace) { + (Some(json_endpoint),Some(binary_endpoint),Some(namespace)) => { let json_endpoint = json_endpoint.clone(); let binary_endpoint = binary_endpoint.clone(); - Ok(Self::LanguageServer {json_endpoint,binary_endpoint}) + let namespace = namespace.clone(); + Ok(Self::LanguageServer {json_endpoint,binary_endpoint,namespace}) } - (None,None) => Ok(default()), - (Some(_),None) => Err(MissingOption(args.names().language_server_data()).into()), - (None,Some(_)) => Err(MissingOption(args.names().language_server_rpc()).into()) + (None,None,None) => Ok(default()), + (None,_,_) => Err(MissingOption(args.names().language_server_rpc()).into()), + (_,None,_) => Err(MissingOption(args.names().language_server_data()).into()), + (_,_,None) => Err(MissingOption(args.names().namespace()).into()), } } } diff --git a/src/rust/ide/src/controller/graph.rs b/src/rust/ide/src/controller/graph.rs index 4b74f494a7..4bb18a4ae4 100644 --- a/src/rust/ide/src/controller/graph.rs +++ b/src/rust/ide/src/controller/graph.rs @@ -484,7 +484,7 @@ impl Handle { let root_id = project.content_root_id(); let module_path = model::module::Path::from_method(root_id,&method)?; let module = project.module(module_path).await?; - let definition = module.lookup_method(project.name().as_ref(),&method)?; + let definition = module.lookup_method(project.qualified_name(),&method)?; Self::new(parent,module,project.suggestion_db(),project.parser(),definition) } @@ -909,19 +909,22 @@ pub mod tests { use super::*; use crate::double_representation::identifier::NormalizedName; + use crate::double_representation::project; use crate::executor::test_utils::TestWithLocalPoolExecutor; use crate::model::module::Position; + use crate::model::suggestion_database; + use crate::test::mock::data; use ast::crumbs; use ast::test_utils::expect_shape; - use data::text::Index; - use data::text::TextChange; + use enso_data::text::Index; + use enso_data::text::TextChange; use enso_protocol::language_server::MethodPointer; use parser::Parser; use utils::test::ExpectTuple; use wasm_bindgen_test::wasm_bindgen_test; - use crate::model::suggestion_database; + /// Returns information about all the connections between graph's nodes. /// @@ -935,7 +938,7 @@ pub mod tests { pub struct MockData { pub module_path : model::module::Path, pub graph_id : Id, - pub project_name : String, + pub project_name : project::QualifiedName, pub code : String, pub suggestions : HashMap, } @@ -945,10 +948,10 @@ pub mod tests { /// node. pub fn new() -> Self { MockData { - module_path : crate::test::mock::data::module_path(), - graph_id : crate::test::mock::data::graph_id(), - project_name : crate::test::mock::data::PROJECT_NAME.to_owned(), - code : crate::test::mock::data::CODE.to_owned(), + module_path : data::module_path(), + graph_id : data::graph_id(), + project_name : data::project_qualified_name(), + code : data::CODE.to_owned(), suggestions : default(), } } @@ -984,7 +987,7 @@ pub mod tests { } pub fn method(&self) -> MethodPointer { - self.module_path.method_pointer(&self.project_name,self.graph_id.to_string()) + self.module_path.method_pointer(self.project_name.clone(),self.graph_id.to_string()) } pub fn suggestion_db(&self) -> Rc { diff --git a/src/rust/ide/src/controller/graph/executed.rs b/src/rust/ide/src/controller/graph/executed.rs index 9206165c62..22e9be36ba 100644 --- a/src/rust/ide/src/controller/graph/executed.rs +++ b/src/rust/ide/src/controller/graph/executed.rs @@ -338,7 +338,9 @@ pub mod tests { let method = self.graph.method(); let mut project = model::project::MockAPI::new(); let ctx = Rc::new(self.ctx.create()); + let proj_name = test::mock::data::project_qualified_name(); model::project::test::expect_name(&mut project,test::mock::data::PROJECT_NAME); + model::project::test::expect_qualified_name(&mut project,&proj_name); model::project::test::expect_parser(&mut project,&parser); model::project::test::expect_module(&mut project,module); model::project::test::expect_execution_ctx(&mut project,ctx); diff --git a/src/rust/ide/src/controller/ide.rs b/src/rust/ide/src/controller/ide.rs index 419cd62839..80f68ca42b 100644 --- a/src/rust/ide/src/controller/ide.rs +++ b/src/rust/ide/src/controller/ide.rs @@ -118,7 +118,7 @@ pub trait ManagingProjectAPI { fn list_projects(&self) -> BoxFuture>>; /// Open the project with given id and name. - fn open_project(&self, id:Uuid, name:ProjectName) -> BoxFuture; + fn open_project(&self, id:Uuid) -> BoxFuture; } diff --git a/src/rust/ide/src/controller/ide/desktop.rs b/src/rust/ide/src/controller/ide/desktop.rs index bd499f50d0..5ca1cc66f5 100644 --- a/src/rust/ide/src/controller/ide/desktop.rs +++ b/src/rust/ide/src/controller/ide/desktop.rs @@ -105,7 +105,7 @@ impl ManagingProjectAPI for Handle { let create_result = self.project_manager.create_project(&name,&version,&action).await?; let new_project_id = create_result.project_id; let project_mgr = self.project_manager.clone_ref(); - let new_project = Project::new_opened(&self.logger,project_mgr,new_project_id,name); + let new_project = Project::new_opened(&self.logger,project_mgr,new_project_id); self.current_project.set(new_project.await?); executor::global::spawn(self.notifications.publish(Notification::NewProjectCreated)); Ok(()) @@ -119,11 +119,11 @@ impl ManagingProjectAPI for Handle { }.boxed_local() } - fn open_project(&self, id: Uuid, name: ProjectName) -> BoxFuture { + fn open_project(&self, id: Uuid) -> BoxFuture { async move { let logger = &self.logger; let project_mgr = self.project_manager.clone_ref(); - let new_project = model::project::Synchronized::new_opened(logger,project_mgr,id,name); + let new_project = model::project::Synchronized::new_opened(logger,project_mgr,id); self.current_project.set(new_project.await?); executor::global::spawn(self.notifications.publish(Notification::ProjectOpened)); Ok(()) diff --git a/src/rust/ide/src/controller/ide/plain.rs b/src/rust/ide/src/controller/ide/plain.rs index 66ee8a093f..718c094772 100644 --- a/src/rust/ide/src/controller/ide/plain.rs +++ b/src/rust/ide/src/controller/ide/plain.rs @@ -3,9 +3,12 @@ //! See [`crate::controller::ide`] for more detailed description of IDE Controller API. use crate::prelude::*; + use crate::controller::ide::ManagingProjectAPI; use crate::controller::ide::Notification; use crate::controller::ide::StatusNotificationPublisher; +use crate::double_representation::project; +use crate::model::project::synchronized::Properties; use enso_protocol::project_manager::ProjectName; use parser::Parser; @@ -51,17 +54,22 @@ impl Handle { /// Create IDE Controller from Language Server endpoints, describing the opened project. pub async fn from_ls_endpoints - ( project_name : ProjectName + ( namespace : String + , project_name : ProjectName , version : semver::Version , json_endpoint : String , binary_endpoint : String ) -> FallibleResult { let logger = Logger::new("controller::ide::Plain"); + let properties = Properties { //TODO [ao]: this should be not the default; instead project model should not need the id. // See https://github.com/enso-org/ide/issues/1572 - let project_id = default(); + id : default(), + name : project::QualifiedName::from_segments(namespace,project_name)?, + engine_version : version + }; let project = model::project::Synchronized::new_connected - (&logger,None,json_endpoint,binary_endpoint,version,project_id,project_name).await?; + (&logger,None,json_endpoint,binary_endpoint,properties).await?; let status_notifications = default(); let parser = Parser::new_or_panic(); Ok(Self{logger,status_notifications,parser,project}) diff --git a/src/rust/ide/src/controller/module.rs b/src/rust/ide/src/controller/module.rs index 221e4de61c..c814daf787 100644 --- a/src/rust/ide/src/controller/module.rs +++ b/src/rust/ide/src/controller/module.rs @@ -2,9 +2,9 @@ use crate::prelude::*; -use crate::double_representation::identifier::ReferentName; use crate::double_representation::text::apply_code_change_to_id_map; use crate::double_representation::module; +use crate::double_representation::project; use crate::model::module::Path; use ast; @@ -111,7 +111,7 @@ impl Handle { } /// Get the module's qualified name. - pub fn qualified_name(&self, project_name:ReferentName) -> module::QualifiedName { + pub fn qualified_name(&self, project_name:project::QualifiedName) -> module::QualifiedName { module::QualifiedName::new(project_name,self.model.id()) } diff --git a/src/rust/ide/src/controller/project.rs b/src/rust/ide/src/controller/project.rs index 66cd1add76..ea1c633e5b 100644 --- a/src/rust/ide/src/controller/project.rs +++ b/src/rust/ide/src/controller/project.rs @@ -4,6 +4,7 @@ use crate::prelude::*; use crate::controller::graph::executed::Notification as GraphNotification; use crate::controller::ide::StatusNotificationPublisher; +use crate::double_representation::project; use enso_frp::web::platform; use enso_frp::web::platform::Platform; @@ -52,7 +53,8 @@ pub fn default_main_module_code() -> String { /// Method pointer that described the main method, i.e. the method that project view wants to open /// and which presence is currently required. -pub fn main_method_ptr(project_name:impl Str, module_path:&model::module::Path) -> MethodPointer { +pub fn main_method_ptr +(project_name:project::QualifiedName, module_path:&model::module::Path) -> MethodPointer { module_path.method_pointer(project_name,MAIN_DEFINITION_NAME) } @@ -125,9 +127,9 @@ impl Project { // TODO [mwu] This solution to recreate missing main file should be considered provisional // until proper decision is made. See: https://github.com/enso-org/enso/issues/1050 self.recreate_if_missing(&file_path,default_main_method_code()).await?; - let method = main_method_ptr(project.name(),&module_path); + let method = main_method_ptr(project.qualified_name(),&module_path); let module = self.model.module(module_path).await?; - Self::add_main_if_missing(project.name().as_ref(),&module,&method,&parser)?; + Self::add_main_if_missing(project.qualified_name(),&module,&method,&parser)?; // Here, we should be relatively certain (except race conditions in case of multiple // clients that we currently do not support) that main module exists and contains main @@ -164,7 +166,7 @@ impl Project { /// /// The lookup will be done using the given `main_ptr` value. pub fn add_main_if_missing - (project_name:&str, module:&model::Module, main_ptr:&MethodPointer, parser:&Parser) + (project_name:project::QualifiedName, module:&model::Module, main_ptr:&MethodPointer, parser:&Parser) -> FallibleResult { if module.lookup_method(project_name,main_ptr).is_err() { let mut info = module.info(); @@ -192,7 +194,7 @@ impl Project { fn display_warning_on_unsupported_engine_version(&self) -> FallibleResult { let requirements = semver::VersionReq::parse(ENGINE_VERSION_SUPPORTED)?; let version = self.model.engine_version(); - if !requirements.matches(version) { + if !requirements.matches(&version) { let message = format!("Unsupported Engine version. Please update engine_version in {} \ to {}.",package_yaml_path(&self.model.name()),ENGINE_VERSION_FOR_NEW_PROJECTS); self.status_notifications.publish_event(message); @@ -226,22 +228,22 @@ mod tests { let parser = parser::Parser::new_or_panic(); let mut data = crate::test::mock::Unified::new(); let module_name = data.module_path.module_name(); - let main_ptr = main_method_ptr(&data.project_name,&data.module_path); + let main_ptr = main_method_ptr(data.project_name.clone(),&data.module_path); // Check that module without main gets it after the call. let empty_module_code = ""; data.set_code(empty_module_code); let urm = data.undo_redo_manager(); let module = data.module(urm.clone_ref()); - assert!(module.lookup_method(&data.project_name,&main_ptr).is_err()); - Project::add_main_if_missing(&data.project_name, &module, &main_ptr, &parser).unwrap(); - assert!(module.lookup_method(&data.project_name,&main_ptr).is_ok()); + assert!(module.lookup_method(data.project_name.clone(),&main_ptr).is_err()); + Project::add_main_if_missing(data.project_name.clone(), &module, &main_ptr, &parser).unwrap(); + assert!(module.lookup_method(data.project_name.clone(),&main_ptr).is_ok()); // Now check that modules that have main already defined won't get modified. let mut expect_intact = move |code:&str| { data.set_code(code); let module = data.module(urm.clone_ref()); - Project::add_main_if_missing(&data.project_name, &module, &main_ptr, &parser).unwrap(); + Project::add_main_if_missing(data.project_name.clone(), &module, &main_ptr, &parser).unwrap(); assert_eq!(code,module.ast().repr()); }; expect_intact("main = 5"); diff --git a/src/rust/ide/src/controller/searcher.rs b/src/rust/ide/src/controller/searcher.rs index 2d26a8100b..b91c3d2402 100644 --- a/src/rust/ide/src/controller/searcher.rs +++ b/src/rust/ide/src/controller/searcher.rs @@ -10,6 +10,7 @@ use crate::double_representation::graph::GraphInfo; use crate::double_representation::graph::LocationHint; use crate::double_representation::module::QualifiedName; use crate::double_representation::node::NodeInfo; +use crate::double_representation::project; use crate::double_representation::tp; use crate::model::module::MethodId; use crate::model::module::NodeMetadata; @@ -416,7 +417,7 @@ impl Data { /// Additionally searcher should restore information about intended method, so we will be able /// to suggest arguments. fn new_with_edited_node - ( project_name : &str + ( project_name : project::QualifiedName , graph : &controller::Graph , database : &model::SuggestionDatabase , edited_node_id : ast::Id @@ -501,7 +502,7 @@ impl Searcher { let logger = Logger::sub(parent,"Searcher Controller"); let database = project.suggestion_db(); let data = if let Mode::EditNode{node_id} = mode { - Data::new_with_edited_node(&*project.name(),&graph.graph(),&*database,node_id)? + Data::new_with_edited_node(project.qualified_name(),&graph.graph(),&*database,node_id)? } else { default() }; @@ -587,6 +588,7 @@ impl Searcher { let id = self.data.borrow().input.next_completion_id(); let picked_completion = FragmentAddedByPickingSuggestion {id,picked_suggestion}; let code_to_insert = self.code_to_insert(&picked_completion).code; + debug!(self.logger, "Code to insert: \"{code_to_insert}\""); let added_ast = self.ide.parser().parse_line(&code_to_insert)?; let pattern_offset = self.data.borrow().input.pattern_offset; let new_expression = match self.data.borrow_mut().input.expression.take() { @@ -655,8 +657,8 @@ impl Searcher { let result = match action { action::ProjectManagement::CreateNewProject => manage_projects.create_new_project(), - action::ProjectManagement::OpenProject {id,name} => - manage_projects.open_project(*id,name.to_string().into()), + action::ProjectManagement::OpenProject {id,..} => + manage_projects.open_project(*id), }; if let Err(err) = result.await { error!(logger, "Error when creating new project: {err}"); @@ -997,7 +999,8 @@ impl Searcher { } fn module_qualified_name(&self) -> QualifiedName { - self.graph.graph().module.path().qualified_module_name(&*self.ide.current_project().name()) + let project_name = self.ide.current_project().qualified_name(); + self.graph.graph().module.path().qualified_module_name(project_name) } /// Get the user action basing of current input (see `UserAction` docs). @@ -1128,7 +1131,7 @@ pub mod test { use crate::model::suggestion_database::entry::Scope; use crate::test::mock::data::MAIN_FINISH; use crate::test::mock::data::MODULE_NAME; - use crate::test::mock::data::PROJECT_NAME; + use crate::test::mock::data::project_qualified_name; use enso_protocol::language_server::types::test::value_update_with_type; use json_rpc::expect_call; @@ -1208,23 +1211,25 @@ pub mod test { let mut client = language_server::MockClient::default(); client.require_all_calls(); client_setup(&mut data,&mut client); - let end_of_code = TextLocation::at_document_end(&data.graph.module.code); - let code_range = TextLocation::at_document_begin()..=end_of_code; - let graph = data.graph.controller(); - let node = &graph.graph().nodes().unwrap()[0]; - let this = ThisNode::new(vec![node.info.id()],&graph.graph()); - let this = data.selected_node.and_option(this); - let logger = Logger::new("Searcher");// new_empty - let database = Rc::new(SuggestionDatabase::new_empty(&logger)); - let mut ide = controller::ide::MockAPI::new(); - let mut project = model::project::MockAPI::new(); - let project_name = ImString::new(&data.graph.graph.project_name); - project.expect_name().returning_st(move || project_name.clone_ref()); + let end_of_code = TextLocation::at_document_end(&data.graph.module.code); + let code_range = TextLocation::at_document_begin()..=end_of_code; + let graph = data.graph.controller(); + let node = &graph.graph().nodes().unwrap()[0]; + let this = ThisNode::new(vec![node.info.id()],&graph.graph()); + let this = data.selected_node.and_option(this); + let logger = Logger::new("Searcher");// new_empty + let database = Rc::new(SuggestionDatabase::new_empty(&logger)); + let mut ide = controller::ide::MockAPI::new(); + let mut project = model::project::MockAPI::new(); + let project_qname = project_qualified_name(); + let project_name = project_qname.project.clone(); + project.expect_qualified_name().returning_st(move || project_qname.clone()); + project.expect_name().returning_st(move || project_name.clone()); let project = Rc::new(project); ide.expect_parser().return_const(Parser::new_or_panic()); ide.expect_current_project().returning_st(move || project.clone_ref()); ide.expect_manage_projects().returning_st(move || Err(ProjectOperationsNotSupported.into())); - let module_name = QualifiedName::from_segments(PROJECT_NAME, &[MODULE_NAME]).unwrap(); + let module_name = QualifiedName::from_segments(data.graph.graph.project_name.clone(), &[MODULE_NAME]).unwrap(); let searcher = Searcher { graph,logger,database, ide : Rc::new(ide), @@ -1274,8 +1279,8 @@ pub mod test { ..entry1.clone() }; let entry4 = model::suggestion_database::Entry { - self_type : Some("Test.Test".to_owned().try_into().unwrap()), - module : "Test.Test".to_owned().try_into().unwrap(), + self_type : Some("test.Test.Test".to_owned().try_into().unwrap()), + module : "test.Test.Test".to_owned().try_into().unwrap(), arguments : vec![ Argument { repr_type : "Any".to_string(), @@ -1316,7 +1321,7 @@ pub mod test { }; let entry10 = model::suggestion_database::Entry { name : "testFunction3".to_string(), - module : "Test.Other".to_owned().try_into().unwrap(), + module : "test.Test.Other".to_owned().try_into().unwrap(), scope : Scope::Everywhere, ..entry9.clone() }; @@ -1439,7 +1444,7 @@ pub mod test { #[wasm_bindgen_test] fn non_picked_function_arg_suggestions() { let mut fixture = Fixture::new_custom(|data,client| { - data.graph.module.code.insert_str(0,"import Test.Test\n\n"); + data.graph.module.code.insert_str(0,"import test.Test.Test\n\n"); data.code_location.line += 2; data.expect_completion(client,None,Some("String"),&[1]); data.expect_completion(client,None,Some("Number"),&[]); @@ -1468,7 +1473,7 @@ pub mod test { let requested_types:Rc>>> = default(); let requested_types2 = requested_types.clone(); let mut fixture = Fixture::new_custom(move |data,client| { - data.graph.module.code.insert_str(0,"import Test.Test\n\n"); + data.graph.module.code.insert_str(0,"import test.Test.Test\n\n"); data.code_location.line += 2; for _ in 0..EXPECTED_REQUESTS { let requested_types = requested_types2.clone(); @@ -1793,12 +1798,12 @@ pub mod test { searcher.mode = Immutable(Mode::NewNode {position}); searcher.commit_node().unwrap(); - let expected_code = "import Test.Test\nmain = \n 2 + 2\n operator1 = Test.testMethod1"; + let expected_code = "import test.Test.Test\nmain = \n 2 + 2\n operator1 = Test.testMethod1"; assert_eq!(module.ast().repr(), expected_code); let (node1,node2) = searcher.graph.graph().nodes().unwrap().expect_tuple(); let expected_intended_method = Some(MethodId { - module : "Test.Test".to_string().try_into().unwrap(), - defined_on_type : "Test.Test".to_string().try_into().unwrap(), + module : "test.Test.Test".to_string().try_into().unwrap(), + defined_on_type : "test.Test.Test".to_string().try_into().unwrap(), name : "testMethod1".to_string(), }); assert_eq!(node2.metadata.unwrap().intended_method, expected_intended_method); @@ -1806,7 +1811,7 @@ pub mod test { // Edit existing node. searcher.mode = Immutable(Mode::EditNode {node_id:node1.info.id()}); searcher.commit_node().unwrap(); - let expected_code = "import Test.Test\nmain = \n Test.testMethod1\n operator1 = Test.testMethod1"; + let expected_code = "import test.Test.Test\nmain = \n Test.testMethod1\n operator1 = Test.testMethod1"; let (node1,_) = searcher.graph.graph().nodes().unwrap().expect_tuple(); assert_eq!(node1.metadata.unwrap().intended_method, expected_intended_method); assert_eq!(module.ast().repr(), expected_code); @@ -1822,21 +1827,21 @@ pub mod test { let database = searcher.database; // Node had not intended method. - let searcher_data = Data::new_with_edited_node(PROJECT_NAME,&graph,&database,node_id).unwrap(); + let searcher_data = Data::new_with_edited_node(project_qualified_name(),&graph,&database,node_id).unwrap(); assert_eq!(searcher_data.input.repr(), node.info.expression().repr()); assert!(searcher_data.fragments_added_by_picking.is_empty()); assert!(searcher_data.actions.is_loading()); // Node had intended method, but it's outdated. let intended_method = MethodId { - module : "Test.Test".to_string().try_into().unwrap(), - defined_on_type : "Test.Test".to_string().try_into().unwrap(), + module : "test.Test.Test".to_string().try_into().unwrap(), + defined_on_type : "test.Test.Test".to_string().try_into().unwrap(), name : "testMethod1".to_string() }; graph.module.with_node_metadata(node_id, Box::new(|md| { md.intended_method = Some(intended_method); })).unwrap(); - let searcher_data = Data::new_with_edited_node(PROJECT_NAME,&graph,&database,node_id).unwrap(); + let searcher_data = Data::new_with_edited_node(project_qualified_name(),&graph,&database,node_id).unwrap(); assert_eq!(searcher_data.input.repr(), node.info.expression().repr()); assert!(searcher_data.fragments_added_by_picking.is_empty()); assert!(searcher_data.actions.is_loading()); @@ -1844,7 +1849,7 @@ pub mod test { // Node had up-to-date intended method. graph.set_expression(node_id,"Test.testMethod1 12").unwrap(); // We set metadata in previous section. - let searcher_data = Data::new_with_edited_node(PROJECT_NAME,&graph,&database,node_id).unwrap(); + let searcher_data = Data::new_with_edited_node(project_qualified_name(),&graph,&database,node_id).unwrap(); assert_eq!(searcher_data.input.repr(), "Test.testMethod1 12"); assert!(searcher_data.actions.is_loading()); let (initial_fragment,) = searcher_data.fragments_added_by_picking.expect_tuple(); @@ -1903,10 +1908,10 @@ pub mod test { let example = model::suggestion_database::example::Example { name : "Test Example".to_owned(), code : "[1,2,3,4,5]".to_owned(), - imports : vec!["Base.Network.Http".to_owned()], + imports : vec!["std.Base.Network.Http".to_owned()], documentation : "Lorem ipsum".to_owned() }; - let expected_code = "import Base.Network.Http\n\ + let expected_code = "import std.Base.Network.Http\n\ test_example1 = [1,2,3,4,5]\n\ntest_example2 = [1,2,3,4,5]\n\n\ main = \n 2 + 2\n here.test_example1\n here.test_example2"; let example = Rc::new(example); diff --git a/src/rust/ide/src/controller/searcher/action.rs b/src/rust/ide/src/controller/searcher/action.rs index 0970c4d25c..9d9e9048e6 100644 --- a/src/rust/ide/src/controller/searcher/action.rs +++ b/src/rust/ide/src/controller/searcher/action.rs @@ -46,7 +46,7 @@ impl Display for Action { let should_put_project_name = self_type.name == constants::PROJECTS_MAIN_MODULE && self_type.module_segments.is_empty(); let self_type_name = if should_put_project_name { - self_type.project_name.as_ref() + self_type.project_name.project.as_ref() } else { &self_type.name }; write!(f,"{}.{}",self_type_name,completion.name) } else { diff --git a/src/rust/ide/src/double_representation.rs b/src/rust/ide/src/double_representation.rs index fb4b0d6f52..012751a981 100644 --- a/src/rust/ide/src/double_representation.rs +++ b/src/rust/ide/src/double_representation.rs @@ -8,6 +8,7 @@ pub mod graph; pub mod identifier; pub mod module; pub mod node; +pub mod project; pub mod refactorings; pub mod text; pub mod tp; diff --git a/src/rust/ide/src/double_representation/module.rs b/src/rust/ide/src/double_representation/module.rs index 72edc390c1..8a87948d0b 100644 --- a/src/rust/ide/src/double_representation/module.rs +++ b/src/rust/ide/src/double_representation/module.rs @@ -10,6 +10,7 @@ use crate::double_representation::identifier; use crate::double_representation::identifier::Identifier; use crate::double_representation::identifier::LocatedName; use crate::double_representation::identifier::ReferentName; +use crate::double_representation::project; use crate::double_representation::tp; use ast::crumbs::ChildAst; @@ -147,15 +148,15 @@ impl Id { #[serde(into="String")] #[serde(try_from="String")] pub struct QualifiedName { - /// The first segment in the full qualified name. - pub project_name : ReferentName, + /// The first segment in the full qualified name. May be a project name or a keyword like + pub project_name : project::QualifiedName, /// The module id: all segments in full qualified name but the first (which is a project name). pub id : Id } impl QualifiedName { /// Build a module's qualified name from the project name and module's path. - pub fn new(project_name:ReferentName, id:Id) -> QualifiedName { + pub fn new(project_name:project::QualifiedName, id:Id) -> QualifiedName { QualifiedName {project_name,id} } @@ -163,7 +164,7 @@ impl QualifiedName { /// /// It is special, as its name consists only from the project name, unlike other modules' /// qualified names. - pub fn new_main(project_name:ReferentName) -> QualifiedName { + pub fn new_main(project_name:project::QualifiedName) -> QualifiedName { Self::new(project_name, Id::new(std::iter::empty())) } @@ -174,9 +175,9 @@ impl QualifiedName { use ast::opr::predefined::ACCESS; let text = text.as_ref(); - let segments = text.split(ACCESS); - if let [ref project_name,ref id_segments @ ..] = *segments.collect_vec().as_slice() { - let project_name = ReferentName::new(*project_name)?; + let segments = text.split(ACCESS).collect_vec(); + if let [namespace_name,project_name,id_segments @ ..] = segments.as_slice() { + let project_name = project::QualifiedName::from_segments(*namespace_name,*project_name)?; let id = Id::try_new(id_segments)?; Ok(Self::new(project_name,id)) } else { @@ -187,13 +188,14 @@ impl QualifiedName { /// Build a module's full qualified name from its name segments and the project name. /// /// ``` - /// use ide::model::module::QualifiedName; + /// # use enso_prelude::*; + /// # use ide::model::module::QualifiedName; /// - /// let name = QualifiedName::from_segments("Project",&["Main"]).unwrap(); - /// assert_eq!(name.to_string(), "Project.Main"); + /// let name = QualifiedName::from_segments("local.Project".try_into().unwrap(),&["Main"]).unwrap(); + /// assert_eq!(name.to_string(), "local.Project.Main"); /// ``` pub fn from_segments - (project_name:impl Into, module_segments:impl IntoIterator>) + (project_name:project::QualifiedName, module_segments:impl IntoIterator>) -> FallibleResult { let project_name = std::iter::once(project_name.into()); let module_segments = module_segments.into_iter(); @@ -214,20 +216,24 @@ impl QualifiedName { pub fn from_all_segments (segments:impl IntoIterator>) -> FallibleResult { let mut iter = segments.into_iter(); + let namespace = iter.next().map(|name| name.as_ref().to_owned()); let project_name = iter.next().map(|name| name.as_ref().to_owned()); - let project_name = project_name.ok_or(InvalidQualifiedName::NoModuleSegment)?; - Self::from_segments(project_name,iter) + let typed_project_name = match (namespace,project_name) { + (Some(ns),Some(name)) => project::QualifiedName::from_segments(ns,name), + _ => Err(InvalidQualifiedName::NoModuleSegment.into()), + }; + Self::from_segments(typed_project_name?,iter) } /// Get the module's name. It is also the module's typename. pub fn name(&self) -> ReferentName { - if self.id.segments.is_empty() { self.project_name.clone() } + if self.id.segments.is_empty() { self.project_name.project.clone() } else { self.id.name() } } /// Get the name of project owning this module. pub fn project_name(&self) -> &ReferentName { - &self.project_name + &self.project_name.project } /// Get the module's identifier. @@ -236,8 +242,8 @@ impl QualifiedName { } /// Get all segments of the fully qualified name. - pub fn segments(&self) -> impl Iterator { - std::iter::once(&self.project_name).chain(self.id.segments.iter()) + pub fn segments(&self) -> impl Iterator { + self.project_name.segments().chain(self.id.segments.iter().map(|seg| seg.as_ref())) } /// Remove the main module segment from the qualified name. @@ -247,17 +253,17 @@ impl QualifiedName { /// /// ``` /// # use ide::model::module::QualifiedName; - /// let mut name_with_main = QualifiedName::from_text("Project.Main").unwrap(); - /// let mut name_without_main = QualifiedName::from_text("Project.Foo.Bar").unwrap(); - /// let mut main_but_not_project_main = QualifiedName::from_text("Project.Foo.Main").unwrap(); + /// let mut name_with_main = QualifiedName::from_text("ns.Proj.Main").unwrap(); + /// let mut name_without_main = QualifiedName::from_text("ns.Proj.Foo.Bar").unwrap(); + /// let mut main_but_not_project_main = QualifiedName::from_text("ns.Proj.Foo.Main").unwrap(); /// /// name_with_main .remove_main_module_segment(); /// name_without_main .remove_main_module_segment(); /// main_but_not_project_main .remove_main_module_segment(); /// - /// assert_eq!(name_with_main .to_string(), "Project"); - /// assert_eq!(name_without_main .to_string(), "Project.Foo.Bar"); - /// assert_eq!(main_but_not_project_main.to_string(), "Project.Foo.Main"); + /// assert_eq!(name_with_main .to_string(), "ns.Proj"); + /// assert_eq!(name_without_main .to_string(), "ns.Proj.Foo.Bar"); + /// assert_eq!(main_but_not_project_main.to_string(), "ns.Proj.Foo.Main"); /// ``` pub fn remove_main_module_segment(&mut self) { if self.id.segments == [PROJECTS_MAIN_MODULE] { @@ -306,9 +312,8 @@ impl From for String { impl From<&QualifiedName> for String { fn from(name:&QualifiedName) -> Self { - let project_name = std::iter::once(&name.project_name); - let segments = name.id.segments.iter(); - project_name.chain(segments).join(ast::opr::predefined::ACCESS) + let segments = name.id.segments.iter().map(|rn| rn.as_ref()); + name.project_name.segments().chain(segments).join(ast::opr::predefined::ACCESS) } } @@ -488,6 +493,8 @@ impl Info { /// Add a new import if the module is not already imported. pub fn add_module_import (&mut self, here:&QualifiedName, parser:&parser::Parser, to_add:&QualifiedName) { + let imports = self.iter_imports().collect_vec(); + DEBUG!("add_module_import: {to_add} in {imports:?}"); let is_here = to_add == here; let import = ImportInfo::from_qualified_name(&to_add); let already_imported = self.iter_imports().any(|imp| imp == import); @@ -732,14 +739,14 @@ mod tests { #[test] fn qualified_name_validation() { - assert!(QualifiedName::try_from("project.Name").is_err()); - assert!(QualifiedName::try_from("Project.name").is_err()); - assert!(QualifiedName::try_from("Project.").is_err()); + assert!(QualifiedName::try_from("namespace.project.Name").is_err()); + assert!(QualifiedName::try_from("namespace.Project.name").is_err()); + assert!(QualifiedName::try_from("namespace.").is_err()); assert!(QualifiedName::try_from(".Name").is_err()); assert!(QualifiedName::try_from(".").is_err()); assert!(QualifiedName::try_from("").is_err()); - assert!(QualifiedName::try_from("Project.Name").is_ok()); - assert!(QualifiedName::try_from("Project.Name.Sub").is_ok()); + assert!(QualifiedName::try_from("namespace.Project.Name").is_ok()); + assert!(QualifiedName::try_from("namespace.Project.Name.Sub").is_ok()); } #[wasm_bindgen_test] @@ -795,7 +802,7 @@ mod tests { #[wasm_bindgen_test] fn implicit_method_resolution() { let parser = parser::Parser::new_or_panic(); - let module_name = QualifiedName::from_segments("ProjectName",&["Main"]).unwrap(); + let module_name = QualifiedName::from_all_segments(&["local","ProjectName","Main"]).unwrap(); let expect_find = |method:&MethodPointer, code,expected:&definition::Id| { let module = parser.parse_module(code,default()).unwrap(); let result = lookup_method(&module_name,&module,method); @@ -817,8 +824,8 @@ mod tests { // === Lookup the Main (local module type) extension method === let ptr = MethodPointer { - defined_on_type : "ProjectName.Main".into(), - module : "ProjectName.Main".into(), + defined_on_type : "local.ProjectName.Main".into(), + module : "local.ProjectName.Main".into(), name : "foo".into(), }; @@ -840,8 +847,8 @@ mod tests { // === Lookup the Int (non-local type) extension method === let ptr = MethodPointer { - defined_on_type : "Base.Main.Number".into(), - module : "ProjectName.Main".into(), + defined_on_type : "std.Base.Main.Number".into(), + module : "local.ProjectName.Main".into(), name : "foo".into(), }; diff --git a/src/rust/ide/src/double_representation/project.rs b/src/rust/ide/src/double_representation/project.rs new file mode 100644 index 0000000000..e4545b0799 --- /dev/null +++ b/src/rust/ide/src/double_representation/project.rs @@ -0,0 +1,153 @@ +//! A set of structures describing Project for double representation. + +use crate::prelude::*; + +use crate::double_representation::identifier::ReferentName; + +use serde::Deserialize; +use serde::Serialize; + + + +// ============== +// === Errors === +// ============== + +#[allow(missing_docs)] +#[derive(Clone,Debug,Fail)] +pub enum InvalidQualifiedName { + #[fail(display="The qualified name is empty.")] + EmptyName{source:String}, + #[fail(display="No namespace in project qualified name.")] + NoNamespace{source:String}, + #[fail(display="Invalid namespace in project qualified name.")] + InvalidNamespace{source:String}, + #[fail(display="Too many segments in project qualified name.")] + TooManySegments{source:String}, +} + + +// ===================== +// === QualifiedName === +// ===================== + +/// The project qualified name has a form of `.`. It serves as +/// a prefix for qualified names of other entities (modules, types, etc.). +#[derive(Clone,Debug,Deserialize,Eq,Hash,Ord,PartialEq,PartialOrd,Serialize)] +#[serde(into="String")] +#[serde(try_from="String")] +pub struct QualifiedName { + /// The name of project's namespace. + pub namespace:String, + /// The actual project name. + pub project:ReferentName, +} + +impl QualifiedName { + /// Create qualified name from typed components. + pub fn new(namespace:String, project:ReferentName) -> Self { + Self{namespace,project} + } + + /// Create qualified name from string segments. May fail if the segments are invalid. + pub fn from_segments + (namespace:impl Into, project:impl Into) -> FallibleResult { + let namespace = namespace.into(); + if namespace.is_empty() { + let source = format!("{}.{}",namespace,project.into()); + Err(InvalidQualifiedName::InvalidNamespace {source}.into()) + } else { + let project = ReferentName::new(project.into())?; + Ok(Self {namespace,project}) + } + } + + /// Create from a text representation. May fail if the text is not valid Qualified. + pub fn from_text(text:impl Into) -> FallibleResult { + let source:String = text.into(); + let all_segments = source.split('.').collect_vec(); + match all_segments.as_slice() { + [namespace,project] => Self::from_segments(*namespace,*project), + [] => Err(InvalidQualifiedName::EmptyName {source}.into()), + [_] => Err(InvalidQualifiedName::NoNamespace {source}.into()), + _ => Err(InvalidQualifiedName::TooManySegments {source}.into()), + } + } + + /// The iterator over name's segments: the namespace and project name. + pub fn segments(&self) -> impl Iterator { + std::iter::once(self.namespace.as_ref()).chain(std::iter::once(self.project.as_ref())) + } +} + + +// === Conversions === + +impl TryFrom<&str> for QualifiedName { + type Error = failure::Error; + + fn try_from(text:&str) -> Result { + Self::from_text(text) + } +} + +impl TryFrom for QualifiedName { + type Error = failure::Error; + + fn try_from(text:String) -> Result { + Self::from_text(text) + } +} + +impl From for String { + fn from(name:QualifiedName) -> Self { + String::from(&name) + } +} + +impl From<&QualifiedName> for String { + fn from(name:&QualifiedName) -> Self { + name.to_string() + } +} + +impl Display for QualifiedName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f,"{}.{}",self.namespace,self.project) + } +} + + + +// ============= +// === Tests === +// ============= + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn qualified_name_from_string() { + fn valid_case(text:&str, namespace:&str, project:&str) { + let qualified_name = QualifiedName::from_text(text).unwrap(); + assert_eq!(qualified_name.namespace, namespace); + assert_eq!(qualified_name.project , project ); + } + + fn invalid_case(text:&str) { + assert!(QualifiedName::from_text(text).is_err()); + } + + valid_case("ns.Project", "ns", "Project"); + valid_case("n.Proj" , "n" , "Proj" ); + + invalid_case("namespace"); + invalid_case("Project"); + invalid_case("namespace.project"); + invalid_case("namespace.Project.Main"); + invalid_case(".Project"); + invalid_case("namespace."); + invalid_case("."); + } +} diff --git a/src/rust/ide/src/double_representation/tp.rs b/src/rust/ide/src/double_representation/tp.rs index 897b30ca96..a4a5907bf3 100644 --- a/src/rust/ide/src/double_representation/tp.rs +++ b/src/rust/ide/src/double_representation/tp.rs @@ -4,6 +4,7 @@ use crate::prelude::*; use crate::double_representation::identifier::ReferentName; use crate::double_representation::module; +use crate::double_representation::project; use serde::Deserialize; use serde::Serialize; @@ -19,6 +20,8 @@ use serde::Serialize; pub enum InvalidQualifiedName { #[fail(display="The qualified name is empty.")] EmptyName{source:String}, + #[fail(display="The qualified name has no namespace.")] + NoNamespaceName{source:String}, #[fail(display="No module in type qualified name.")] NoModuleName{source:String}, } @@ -42,7 +45,7 @@ pub enum InvalidQualifiedName { #[serde(try_from="String")] pub struct QualifiedName { /// The first segment in the full qualified name. - pub project_name : ReferentName, + pub project_name : project::QualifiedName, /// All segments between the project name (the first) and the entity name (the last). pub module_segments : Vec, /// The last segment in the full qualified name. @@ -69,11 +72,13 @@ impl QualifiedName { /// Create from a text representation. May fail if the text is not valid Qualified name of any /// type. pub fn from_text(text:impl Str) -> FallibleResult { + use InvalidQualifiedName::*; let text:String = text.into(); let mut all_segments = text.split('.'); - let project_name_str = all_segments.next().ok_or_else(|| InvalidQualifiedName::EmptyName{source:text.clone()})?; - let project_name = ReferentName::new(project_name_str)?; - let name_str = all_segments.next_back().ok_or_else(||InvalidQualifiedName::NoModuleName{source:text.clone()})?; + let namespace = all_segments.next().ok_or_else(|| EmptyName{source:text.clone()})?; + let project_name = all_segments.next().ok_or_else(|| NoNamespaceName{source:text.clone()})?; + let project_name = project::QualifiedName::from_segments(namespace,project_name)?; + let name_str = all_segments.next_back().ok_or_else(||NoModuleName{source:text.clone()})?; let name = name_str.to_owned(); let mut module_segments = Vec::new(); for segment in all_segments { @@ -121,7 +126,7 @@ impl From for String { impl From<&QualifiedName> for String { fn from(name:&QualifiedName) -> Self { - let project_name = std::iter::once(name.project_name.as_ref()); + let project_name = name.project_name.segments(); let segments = name.module_segments.iter().map(AsRef::::as_ref); let name = std::iter::once(name.name.as_ref()); project_name.chain(segments).chain(name).join(".") @@ -143,13 +148,15 @@ impl Display for QualifiedName { #[cfg(test)] mod test { - // use super::*; + use super::*; use crate::double_representation::tp::QualifiedName; + #[test] fn qualified_name_from_string() { - let valid_case = |text:&str, project_name:&str, segments:Vec<&str>, name:&str| { + let valid_case = |text:&str, ns_name:&str, project_name:&str, segments:Vec<&str>, name:&str| { + let project_name = project::QualifiedName::from_segments(ns_name,project_name).unwrap(); let result = QualifiedName::from_text(text).unwrap(); assert_eq!(result.project_name , project_name); assert_eq!(result.module_segments , segments ); @@ -160,12 +167,13 @@ mod test { assert!(QualifiedName::from_text(text).is_err()); }; - valid_case("Project.Main.Test.foo" , "Project", vec!["Main", "Test"], "foo"); - valid_case("Project.Main.Bar" , "Project", vec!["Main"] , "Bar"); - valid_case("Project.Baz" , "Project", vec![] , "Baz"); + valid_case("local.Project.Main.Test.foo" , "local" , "Project", vec!["Main", "Test"], "foo"); + valid_case("local.Project.Main.Bar" , "local" , "Project", vec!["Main"] , "Bar"); + valid_case("local.Project.Baz" , "local" , "Project", vec![] , "Baz"); - invalid_case("Project"); - invalid_case("Project.module.foo"); + invalid_case("local"); + invalid_case("local.Project"); + invalid_case("local.Project.module.foo"); invalid_case("..."); invalid_case(""); } diff --git a/src/rust/ide/src/ide/initializer.rs b/src/rust/ide/src/ide/initializer.rs index 45d6283140..aeb8e9d620 100644 --- a/src/rust/ide/src/ide/initializer.rs +++ b/src/rust/ide/src/ide/initializer.rs @@ -116,15 +116,16 @@ impl Initializer { (project_manager,project_name).await?; Ok(Rc::new(controller)) } - LanguageServer {json_endpoint,binary_endpoint} => { + LanguageServer {json_endpoint,binary_endpoint,namespace} => { let json_endpoint = json_endpoint.clone(); let binary_endpoint = binary_endpoint.clone(); + let namespace = namespace.clone(); let project_name = self.config.project_name.clone(); // TODO[ao]: we should think how to handle engine's versions in cloud. // https://github.com/enso-org/ide/issues/1195 let version = semver::Version::parse(ENGINE_VERSION_FOR_NEW_PROJECTS)?; let controller = controller::ide::Plain::from_ls_endpoints - (project_name,version,json_endpoint,binary_endpoint).await?; + (namespace,project_name,version,json_endpoint,binary_endpoint).await?; Ok(Rc::new(controller)) } } @@ -179,9 +180,7 @@ impl WithProjectManager { let project_id = self.get_project_or_create_new().await?; let logger = &self.logger; let project_manager = self.project_manager; - let project_name = self.project_name; - model::project::Synchronized::new_opened(logger,project_manager,project_id,project_name) - .await + model::project::Synchronized::new_opened(logger,project_manager,project_id).await } /// Creates a new project and returns its id, so the newly connected project can be opened. @@ -249,9 +248,11 @@ mod test { let mock_client = project_manager::MockClient::default(); let project_name = ProjectName::new("TestProject"); let project = project_manager::ProjectMetadata { - name : project_name.clone(), - id : uuid::Uuid::new_v4(), - last_opened : default(), + name : project_name.clone(), + id : uuid::Uuid::new_v4(), + last_opened : default(), + engine_version : "127.0.01".to_owned(), + namespace : "local".to_owned(), }; let expected_id = project.id; let projects = vec![project]; diff --git a/src/rust/ide/src/model/execution_context/plain.rs b/src/rust/ide/src/model/execution_context/plain.rs index 35c396caa6..d6b6242ba2 100644 --- a/src/rust/ide/src/model/execution_context/plain.rs +++ b/src/rust/ide/src/model/execution_context/plain.rs @@ -215,6 +215,7 @@ pub mod test { use super::*; use crate::double_representation::definition::DefinitionName; + use crate::double_representation::project; #[derive(Clone,Derivative)] #[derivative(Debug)] @@ -222,6 +223,7 @@ pub mod test { pub module_path : model::module::Path, pub context_id : model::execution_context::Id, pub root_definition : DefinitionName, + pub namespace : String, pub project_name : String, } @@ -237,12 +239,14 @@ pub mod test { context_id : model::execution_context::Id::new_v4(), module_path : crate::test::mock::data::module_path(), root_definition : crate::test::mock::data::definition_name(), + namespace : crate::test::mock::data::NAMESPACE_NAME.to_owned(), project_name : crate::test::mock::data::PROJECT_NAME.to_owned(), } } pub fn module_qualified_name(&self) -> model::module::QualifiedName { - self.module_path.qualified_module_name(&self.project_name) + let project_name = project::QualifiedName::from_segments(&self.namespace,&self.project_name); + self.module_path.qualified_module_name(project_name.unwrap()) } pub fn definition_id(&self) -> model::execution_context::DefinitionId { diff --git a/src/rust/ide/src/model/module.rs b/src/rust/ide/src/model/module.rs index bfe4ce9329..00f5c3f18a 100644 --- a/src/rust/ide/src/model/module.rs +++ b/src/rust/ide/src/model/module.rs @@ -14,6 +14,7 @@ use crate::constants::SOURCE_DIRECTORY; use crate::controller::FilePath; use crate::double_representation::identifier::ReferentName; use crate::double_representation::definition::DefinitionInfo; +use crate::double_representation::project; use data::text::TextChange; use data::text::TextLocation; @@ -192,7 +193,8 @@ impl Path { /// Obtain a pointer to a method of the module (i.e. extending the module's atom). /// /// Note that this cannot be used for a method extending other atom than this module. - pub fn method_pointer(&self, project_name:impl Str, method_name:impl Str) -> MethodPointer { + pub fn method_pointer + (&self, project_name:project::QualifiedName, method_name:impl Str) -> MethodPointer { let module = String::from(self.qualified_module_name(project_name)); let defined_on_type = module.clone(); let name = method_name.into(); @@ -208,10 +210,10 @@ impl Path { /// /// let path = Path::from_name_segments(default(),&["Main"]).unwrap(); /// assert_eq!(path.to_string(),"//00000000-0000-0000-0000-000000000000/src/Main.enso"); - /// let name = path.qualified_module_name("Project"); - /// assert_eq!(name.to_string(),"Project.Main"); + /// let name = path.qualified_module_name("local.Project".try_into().unwrap()); + /// assert_eq!(name.to_string(),"local.Project.Main"); /// ``` - pub fn qualified_module_name(&self, project_name:impl Str) -> QualifiedName { + pub fn qualified_module_name(&self, project_name:project::QualifiedName) -> QualifiedName { let non_src_directories = &self.file_path.segments[1..self.file_path.segments.len()-1]; let non_src_directories = non_src_directories.iter().map(|dirname| dirname.as_str()); let module_name = self.module_name(); @@ -516,7 +518,7 @@ pub trait API:Debug+model::undo_redo::Aware { /// The module is assumed to be in the file identified by the `method.file` (for the purpose of /// desugaring implicit extensions methods for modules). fn lookup_method - (&self, project_name:&str, method:&MethodPointer) + (&self, project_name:project::QualifiedName, method:&MethodPointer) -> FallibleResult { let name = self.path().qualified_module_name(project_name); let ast = self.ast(); @@ -613,12 +615,14 @@ pub mod test { #[test] fn module_qualified_name() { + let namespace = "n"; let project_name = "P"; + let project_name = project::QualifiedName::from_segments(namespace,project_name).unwrap(); let root_id = default(); let file_path = FilePath::new(root_id, &["src", "Foo", "Bar.enso"]); let module_path = Path::from_file_path(file_path).unwrap(); let qualified = module_path.qualified_module_name(project_name); - assert_eq!(qualified.to_string(), "P.Foo.Bar"); + assert_eq!(qualified.to_string(), "n.P.Foo.Bar"); } #[wasm_bindgen_test] diff --git a/src/rust/ide/src/model/project.rs b/src/rust/ide/src/model/project.rs index ced5e50231..98bbaf544e 100644 --- a/src/rust/ide/src/model/project.rs +++ b/src/rust/ide/src/model/project.rs @@ -9,6 +9,9 @@ pub mod synchronized; use crate::prelude::*; +use crate::double_representation::project::QualifiedName; +use crate::double_representation::identifier::ReferentName; + use enso_protocol::binary; use enso_protocol::language_server; use flo_stream::Subscriber; @@ -27,7 +30,10 @@ use uuid::Uuid; pub trait API:Debug { /// Project's name // TODO [mwu] This should return Rc. - fn name(&self) -> ImString; + fn name(&self) -> ReferentName; + + /// Project's qualified name + fn qualified_name(&self) -> QualifiedName; /// Get Language Server JSON-RPC Connection for this project. fn json_rpc(&self) -> Rc; @@ -36,7 +42,7 @@ pub trait API:Debug { fn binary_rpc(&self) -> Rc; /// Get the engine's version of the project. - fn engine_version(&self) -> &semver::Version; + fn engine_version(&self) -> semver::Version; /// Get the instance of parser that is set up for this project. fn parser(&self) -> Parser; @@ -71,7 +77,7 @@ pub trait API:Debug { /// Generates full module's qualified name that includes the leading project name segment. fn qualified_module_name (&self, path:&model::module::Path) -> crate::model::module::QualifiedName { - path.qualified_module_name(self.name().deref()) + path.qualified_module_name(self.qualified_name()) } /// Get qualified name of the project's `Main` module. @@ -79,7 +85,7 @@ pub trait API:Debug { /// This module is special, as it needs to be referred by the project name itself. fn main_module(&self) -> FallibleResult { let main = std::iter::once(controller::project::INITIAL_MODULE_NAME); - model::module::QualifiedName::from_segments(self.name(),main) + model::module::QualifiedName::from_segments(self.qualified_name(),main) // TODO [mwu] The code below likely should be preferred but does not work // because language server does not support using project name @@ -194,7 +200,14 @@ pub mod test { /// Sets up name expectation on the mock project, returning a given name. pub fn expect_name(project:&mut MockAPI, name:impl Into) { - let name = ImString::new(name); - project.expect_name().returning_st(move || name.clone_ref()); + let name = ReferentName::new(name.into()).unwrap(); + project.expect_name().returning_st(move || name.clone()); + } + + /// Sets up name expectation on the mock project, returning a given name. + pub fn expect_qualified_name + (project:&mut MockAPI, name:&QualifiedName) { + let name = name.clone(); + project.expect_qualified_name().returning_st(move || name.clone()); } } diff --git a/src/rust/ide/src/model/project/synchronized.rs b/src/rust/ide/src/model/project/synchronized.rs index 289295a588..a18a538776 100644 --- a/src/rust/ide/src/model/project/synchronized.rs +++ b/src/rust/ide/src/model/project/synchronized.rs @@ -2,6 +2,8 @@ use crate::prelude::*; +use crate::double_representation::identifier::ReferentName; +use crate::double_representation::project::QualifiedName; use crate::model::execution_context::VisualizationUpdateData; use crate::model::execution_context; use crate::model::module; @@ -108,9 +110,9 @@ pub struct UnsupportedEngineVersion { } impl UnsupportedEngineVersion { - fn error_wrapper - (project_name:String, engine_version:semver::Version) - -> impl Fn(failure::Error) -> failure::Error { + fn error_wrapper(properties:&Properties) -> impl Fn(failure::Error) -> failure::Error { + let engine_version = properties.engine_version.clone(); + let project_name = properties.name.project.as_str().to_owned(); move |root_cause| { let requirements = semver::VersionReq::parse(controller::project::ENGINE_VERSION_SUPPORTED); match requirements { @@ -143,7 +145,7 @@ impl Display for UnsupportedEngineVersion { pub struct Properties { /// ID of the project, as used by the Project Manager service. pub id : Uuid, - pub name : CloneRefCell, + pub name : QualifiedName, pub engine_version : semver::Version, } @@ -155,7 +157,7 @@ pub struct Properties { #[derive(Derivative)] #[derivative(Debug)] pub struct Project { - pub properties : Rc, + pub properties : Rc>, #[derivative(Debug = "ignore")] pub project_manager : Option>, pub language_server_rpc : Rc, @@ -177,13 +179,11 @@ impl Project { , project_manager : Option> , language_server_rpc : Rc , language_server_bin : Rc - , engine_version : semver::Version - , id : Uuid - , name : impl Str + , properties : Properties ) -> FallibleResult { - let wrap = UnsupportedEngineVersion::error_wrapper(name.as_ref().to_owned(),engine_version.clone()); + let wrap = UnsupportedEngineVersion::error_wrapper(&properties); let logger = Logger::sub(parent,"Project Controller"); - info!(logger,"Creating a model of project {name.as_ref()}"); + info!(logger,"Creating a model of project {properties.name}"); let binary_protocol_events = language_server_bin.event_stream(); let json_rpc_events = language_server_rpc.events(); let embedded_visualizations = default(); @@ -191,15 +191,13 @@ impl Project { let module_registry = default(); let execution_contexts = default(); let visualization = controller::Visualization::new(language_server,embedded_visualizations); - let name = CloneRefCell::new(ImString::new(name.into())); let parser = Parser::new_or_panic(); let language_server = &*language_server_rpc; let suggestion_db = SuggestionDatabase::create_synchronized(language_server); let suggestion_db = Rc::new(suggestion_db.await.map_err(&wrap)?); let notifications = notification::Publisher::default(); let urm = Rc::new(model::undo_redo::Manager::new(&logger)); - let engine_version = engine_version.clone(); - let properties = Rc::new(Properties {id,name,engine_version}); + let properties = Rc::new(RefCell::new(properties)); let ret = Project {properties,project_manager,language_server_rpc,language_server_bin,module_registry @@ -221,11 +219,9 @@ impl Project { , project_manager : Option> , language_server_rpc : String , language_server_bin : String - , engine_version : semver::Version - , id : Uuid - , name : impl Str + , properties : Properties ) -> FallibleResult { - let wrap = UnsupportedEngineVersion::error_wrapper(name.as_ref().to_owned(),engine_version.clone()); + let wrap = UnsupportedEngineVersion::error_wrapper(&properties); let client_id = Uuid::new_v4(); let json_ws = WebSocket::new_opened(&parent,&language_server_rpc).await?; let binary_ws = WebSocket::new_opened(&parent,&language_server_bin).await?; @@ -240,7 +236,7 @@ impl Project { let language_server_rpc = Rc::new(connection_json); let language_server_bin = Rc::new(connection_binary); let model = Self::new(parent,project_manager,language_server_rpc - ,language_server_bin,engine_version,id,name).await?; + ,language_server_bin,properties).await?; Ok(Rc::new(model)) } @@ -250,15 +246,20 @@ impl Project { ( parent : &Logger , project_manager : Rc , id : Uuid - , name : impl Str ) -> FallibleResult { let action = MissingComponentAction::Install; let opened = project_manager.open_project(&id,&action).await?; - let version = semver::Version::parse(&opened.engine_version)?; + let namespace = opened.project_namespace; + let name = opened.project_name; let project_manager = Some(project_manager); let json_endpoint = opened.language_server_json_address.to_string(); let binary_endpoint = opened.language_server_binary_address.to_string(); - Self::new_connected(parent,project_manager,json_endpoint,binary_endpoint,version,id,name).await + let properties = Properties { + id, + name : QualifiedName::from_segments(namespace,name)?, + engine_version : semver::Version::parse(&opened.engine_version)?, + }; + Self::new_connected(parent,project_manager,json_endpoint,binary_endpoint,properties).await } /// Returns a handling function capable of processing updates from the binary protocol. @@ -385,8 +386,12 @@ impl Project { } impl model::project::API for Project { - fn name(&self) -> ImString { - self.properties.name.get() + fn name(&self) -> ReferentName { + self.properties.borrow().name.project.clone() + } + + fn qualified_name(&self) -> QualifiedName { + self.properties.borrow().name.clone() } fn json_rpc(&self) -> Rc { @@ -397,7 +402,9 @@ impl model::project::API for Project { self.language_server_bin.clone_ref() } - fn engine_version(&self) -> &semver::Version { &self.properties.engine_version } + fn engine_version(&self) -> semver::Version { + self.properties.borrow().engine_version.clone() + } fn parser(&self) -> Parser { self.parser.clone_ref() @@ -435,9 +442,11 @@ impl model::project::API for Project { fn rename_project(&self, name:String) -> BoxFuture { async move { + let referent_name = name.as_str().try_into()?; let project_manager = self.project_manager.as_ref().ok_or(ProjectManagerUnavailable)?; - project_manager.rename_project(&self.properties.id, &name).await?; - self.properties.name.set(name.into()); + let project_id = self.properties.borrow().id; + project_manager.rename_project(&project_id,&name).await?; + self.properties.borrow_mut().name.project = referent_name; Ok(()) }.boxed_local() } @@ -465,7 +474,6 @@ impl model::project::API for Project { mod test { use super::*; - use crate::constants::DEFAULT_PROJECT_NAME; use crate::executor::test_utils::TestWithLocalPoolExecutor; use enso_protocol::types::Sha3_224; @@ -516,10 +524,13 @@ mod test { let binary_connection = Rc::new(binary::Connection::new_mock(binary_client)); let project_manager = Rc::new(project_manager); let logger = Logger::new("Fixture"); - let id = Uuid::new_v4(); - let engine_version = semver::Version::new(0,2,1); + let properties = Properties { + id : Uuid::new_v4(), + name : crate::test::mock::data::project_qualified_name(), + engine_version : semver::Version::new(0,2,1), + }; let project_fut = Project::new(logger,Some(project_manager), - json_connection,binary_connection,engine_version,id,DEFAULT_PROJECT_NAME).boxed_local(); + json_connection,binary_connection,properties).boxed_local(); let project = test.expect_completion(project_fut).unwrap(); Fixture {test,project,binary_events_sender,json_events_sender} } diff --git a/src/rust/ide/src/model/suggestion_database/entry.rs b/src/rust/ide/src/model/suggestion_database/entry.rs index 9528f3a8b5..1325edb4c7 100644 --- a/src/rust/ide/src/model/suggestion_database/entry.rs +++ b/src/rust/ide/src/model/suggestion_database/entry.rs @@ -150,7 +150,7 @@ impl Entry { None } } else { - // No this expression unless we have been requested to add one. + // No "this" expression unless we have been requested to add one. None }; @@ -439,8 +439,8 @@ mod test { #[test] fn code_from_entry() { - let main_module = module::QualifiedName::from_text("Project.Main").unwrap(); - let another_module = module::QualifiedName::from_text("Project.Another_Module").unwrap(); + let main_module = module::QualifiedName::from_text("local.Project.Main").unwrap(); + let another_module = module::QualifiedName::from_text("local.Project.Another_Module").unwrap(); let atom = Entry { name : "Atom".to_owned(), kind : Kind::Atom, @@ -454,7 +454,7 @@ mod test { let method = Entry { name : "method".to_string(), kind : Kind::Method, - self_type : Some("Base.Main.Number".to_string().try_into().unwrap()), + self_type : Some("std.Base.Main.Number".to_string().try_into().unwrap()), ..atom.clone() }; let module_method = Entry { diff --git a/src/rust/ide/src/test.rs b/src/rust/ide/src/test.rs index b91dfd0a54..13e209500a 100644 --- a/src/rust/ide/src/test.rs +++ b/src/rust/ide/src/test.rs @@ -4,6 +4,7 @@ use crate::prelude::*; use crate::double_representation::module; +use crate::double_representation::project; use crate::model::suggestion_database; use crate::model::undo_redo; use crate::executor::test_utils::TestWithLocalPoolExecutor; @@ -34,11 +35,12 @@ pub mod mock { use uuid::Uuid; pub const ROOT_ID : Uuid = Uuid::from_u128(100); - pub const PROJECT_NAME : &str = "MockProject"; + pub const NAMESPACE_NAME : &str = "mock_namespace"; + pub const PROJECT_NAME : &str = "Mock_Project"; pub const MODULE_NAME : &str = "Mock_Module"; pub const CODE : &str = "main = \n 2 + 2"; pub const DEFINITION_NAME : &str = "main"; - pub const TYPE_NAME : &str = "MockProject.Mock_Module.Mock_Type"; + pub const TYPE_NAME : &str = "mock_namespace.MockProject.Mock_Module.Mock_Type"; pub const MAIN_FINISH : Position = Position {line:1, character:9}; pub const CONTEXT_ID : Uuid = Uuid::from_u128(0xFE); @@ -46,8 +48,12 @@ pub mod mock { crate::model::module::Path::from_name_segments(ROOT_ID, &[MODULE_NAME]).unwrap() } + pub fn project_qualified_name() -> project::QualifiedName { + project::QualifiedName::from_segments(NAMESPACE_NAME,PROJECT_NAME).unwrap() + } + pub fn module_qualified_name() -> module::QualifiedName { - module_path().qualified_module_name(PROJECT_NAME) + module_path().qualified_module_name(project_qualified_name()) } pub fn definition_name() -> crate::double_representation::definition::DefinitionName { @@ -89,10 +95,11 @@ pub mod mock { } pub fn suggestion_entry_foo() -> suggestion_database::Entry { + let project_name = project::QualifiedName::from_segments("std","Base").unwrap(); suggestion_database::Entry { name : "foo".to_owned(), - module : module::QualifiedName::from_segments("Std",&["Base"]).unwrap(), - self_type : Some("Std.Base".to_owned().try_into().unwrap()), + module : module::QualifiedName::from_segments(project_name,&["Main"]).unwrap(), + self_type : Some("std.Base.Main".to_owned().try_into().unwrap()), arguments : vec![foo_method_parameter(),foo_method_parameter2()], return_type : "Any".to_owned(), kind : suggestion_database::entry::Kind::Method, @@ -102,10 +109,11 @@ pub mod mock { } pub fn suggestion_entry_bar() -> suggestion_database::Entry { + let project_name = project::QualifiedName::from_segments("std","Base").unwrap(); suggestion_database::Entry { name : "bar".to_owned(), - module : module::QualifiedName::from_segments("Std",&["Other"]).unwrap(), - self_type : Some("Std.Other".to_owned().try_into().unwrap()), + module : module::QualifiedName::from_segments(project_name,&["Other"]).unwrap(), + self_type : Some("std.Base.Other".to_owned().try_into().unwrap()), arguments : vec![bar_method_parameter()], return_type : "Any".to_owned(), kind : suggestion_database::entry::Kind::Method, @@ -121,7 +129,7 @@ pub mod mock { #[derive(Clone,Debug)] pub struct Unified { pub logger : Logger, - pub project_name : String, + pub project_name : project::QualifiedName, pub module_path : model::module::Path, pub suggestions : HashMap, pub context_id : model::execution_context::Id, @@ -156,7 +164,7 @@ pub mod mock { let logger = Logger::new("UnifiedMock"); Unified { suggestions, - project_name : PROJECT_NAME.to_owned(), + project_name : project_qualified_name(), module_path : module_path(), code : CODE.to_owned(), id_map : default(), @@ -184,7 +192,7 @@ pub mod mock { } pub fn module_qualified_name(&self) -> module::QualifiedName { - self.module_path.qualified_module_name(&self.project_name) + self.module_path.qualified_module_name(self.project_name.clone()) } pub fn definition_id(&self) -> double_representation::definition::Id { @@ -205,7 +213,7 @@ pub mod mock { -> crate::controller::Graph { let parser = self.parser.clone_ref(); let method = self.method_pointer(); - let definition = module.lookup_method(&self.project_name,&method).expect("Lookup failed."); + let definition = module.lookup_method(self.project_name.clone(),&method).expect("Lookup failed."); crate::controller::Graph::new(logger,module,db,parser,definition).expect("Graph could not be created") } @@ -224,7 +232,8 @@ pub mod mock { , binary_client : binary::MockClient ) -> model::Project { let mut project = model::project::MockAPI::new(); - model::project::test::expect_name(&mut project,&self.project_name); + model::project::test::expect_name(&mut project,&self.project_name.project); + model::project::test::expect_qualified_name(&mut project,&self.project_name); model::project::test::expect_parser(&mut project,&self.parser); model::project::test::expect_module(&mut project,module); model::project::test::expect_execution_ctx(&mut project,execution_context); diff --git a/src/rust/ide/tests/language_server.rs b/src/rust/ide/tests/language_server.rs index eb513c746e..f0b19bd465 100644 --- a/src/rust/ide/tests/language_server.rs +++ b/src/rust/ide/tests/language_server.rs @@ -14,7 +14,6 @@ use ide::prelude::*; use enso_protocol::language_server::*; use enso_protocol::types::*; -use ide::double_representation::identifier::ReferentName; use ide::model::module; use ide::model::execution_context::Visualization; use ide::transport::web::WebSocket; @@ -63,12 +62,13 @@ wasm_bindgen_test_configure!(run_in_browser); //#[wasm_bindgen_test::wasm_bindgen_test(async)] #[allow(dead_code)] async fn ls_text_protocol_test() { - let _guard = ide::initializer::setup_global_executor(); - let ide = setup_ide().await; - let project = ide.current_project(); - let client = project.json_rpc(); - let root_id = project.content_root_id(); - let project_name = ReferentName::new(project.name()).unwrap(); + + let _guard = ide::initializer::setup_global_executor(); + let ide = setup_ide().await; + let project = ide.current_project(); + let client = project.json_rpc(); + let root_id = project.content_root_id(); + let project_name = project.qualified_name(); // Initialize files. let file = Path::new(root_id,&["src","Main.enso"]); @@ -333,7 +333,7 @@ async fn binary_visualization_updates_test_hlp() { let logger = Logger::new("Test"); let module_path = ide::initial_module_path(&project).unwrap(); - let method = module_path.method_pointer(project.name(),MAIN_DEFINITION_NAME); + let method = module_path.method_pointer(project.qualified_name(),MAIN_DEFINITION_NAME); let module_qualified_name = project.qualified_module_name(&module_path); let module = project.module(module_path).await.unwrap(); info!(logger,"Got module: {module:?}"); From cb76f55bb2bd96580d9e3e96403e0bc3255ec356 Mon Sep 17 00:00:00 2001 From: Adam Obuchowicz Date: Mon, 5 Jul 2021 14:31:36 +0200 Subject: [PATCH 2/9] Update with the newest Engines API --- .../src/language_server/connection.rs | 10 +-- .../src/language_server/types.rs | 67 +++++++++++++------ src/rust/ide/src/controller/visualization.rs | 2 +- src/rust/ide/src/model/project.rs | 4 +- .../ide/src/model/project/synchronized.rs | 4 +- 5 files changed, 52 insertions(+), 35 deletions(-) diff --git a/src/rust/ide/lib/enso-protocol/src/language_server/connection.rs b/src/rust/ide/lib/enso-protocol/src/language_server/connection.rs index 251d51de3b..0d98a6750f 100644 --- a/src/rust/ide/lib/enso-protocol/src/language_server/connection.rs +++ b/src/rust/ide/lib/enso-protocol/src/language_server/connection.rs @@ -5,7 +5,6 @@ use crate::prelude::*; use crate::language_server::API; use crate::language_server::MockClient; use crate::language_server::types::ContentRoot; -use crate::language_server::types::ContentRootType; use uuid::Uuid; use utils::fail::FallibleResult; @@ -59,8 +58,7 @@ impl Connection { } fn extract_project_root(content_roots:&mut Vec) -> FallibleResult { - use ContentRootType::*; - let opt_index = content_roots.iter().position(|cr| cr.content_root_type == Project); + let opt_index = content_roots.iter().position(|cr| matches!(cr, ContentRoot::Project {..})); let index = opt_index.ok_or(MissingContentRoots)?; Ok(content_roots.drain(index..=index).next().unwrap()) } @@ -70,11 +68,7 @@ impl Connection { Connection { client : Box::new(client), client_id : default(), - project_root : ContentRoot { - id : default(), - content_root_type : ContentRootType::Project, - name : "Project".to_owned() - }, + project_root : ContentRoot::Project {id:default()}, content_roots : default(), } } diff --git a/src/rust/ide/lib/enso-protocol/src/language_server/types.rs b/src/rust/ide/lib/enso-protocol/src/language_server/types.rs index 8d60030a7b..8a67e02669 100644 --- a/src/rust/ide/lib/enso-protocol/src/language_server/types.rs +++ b/src/rust/ide/lib/enso-protocol/src/language_server/types.rs @@ -390,32 +390,55 @@ impl FileSystemObject { // === Content Roots === // ===================== -/// The type of the annotated content root. -#[derive(Clone,Copy,Debug,Deserialize,Eq,Hash,PartialEq,Serialize)] -pub enum ContentRootType { - /// The project home. - Project, - /// System root `/` on unix systems, or drive root on Windows. In Windows’ case, there may be - /// multiple [`Root`] entries corresponding to the various drives. - Root, - /// The user’s home directory. - Home, - /// An Enso library location. - Library, - /// A content root that has been added by the IDE (unused for now). - Custom -} - /// A content root represents a location on a real file-system that has been virtualized for use in /// the Cloud. #[allow(missing_docs)] #[derive(Clone,Debug,Deserialize,Eq,Hash,PartialEq,Serialize)] -#[serde(rename_all="camelCase")] -pub struct ContentRoot { - pub id:Uuid, - #[serde(rename="type")] - pub content_root_type : ContentRootType, - pub name : String, +#[serde(tag="type")] +pub enum ContentRoot { + /// Points to the project home. + #[serde(rename_all="camelCase")] + Project { + id : Uuid, + }, + /// This content root points to the system root (`/`) on unix systems, or to a drive root on + /// Windows. In Windows' case, there may be multiple `Root` entries corresponding to the various + /// drives. + #[serde(rename_all="camelCase")] + FileSystemRoot { + id : Uuid, + path : String, + }, + /// The user's home directory + #[serde(rename_all="camelCase")] + Home { + id : Uuid, + }, + /// An Enso library location. + #[serde(rename_all="camelCase")] + Library { + id : Uuid, + namespace : String, + name : String, + version : String, + }, + /// A content root that has been added by the IDE. + #[serde(rename_all="camelCase")] + Custom { + id : Uuid, + } +} + +impl ContentRoot { + pub fn id(&self) -> Uuid { + match self { + ContentRoot::Project {id } => *id, + ContentRoot::FileSystemRoot {id,..} => *id, + ContentRoot::Home {id } => *id, + ContentRoot::Library {id,..} => *id, + ContentRoot::Custom {id } => *id, + } + } } diff --git a/src/rust/ide/src/controller/visualization.rs b/src/rust/ide/src/controller/visualization.rs index 57550acbc8..523275c60f 100644 --- a/src/rust/ide/src/controller/visualization.rs +++ b/src/rust/ide/src/controller/visualization.rs @@ -105,7 +105,7 @@ impl Handle { async fn list_project_specific_visualizations (&self) -> FallibleResult> { - let root_id = self.language_server_rpc.project_root().id; + let root_id = self.language_server_rpc.project_root().id(); let path = language_server::Path::new(root_id,&[VISUALIZATION_DIRECTORY]); let folder = self.language_server_rpc.file_exists(&path).await?; let file_list = if folder.exists { diff --git a/src/rust/ide/src/model/project.rs b/src/rust/ide/src/model/project.rs index 98bbaf544e..cbeacd3be6 100644 --- a/src/rust/ide/src/model/project.rs +++ b/src/rust/ide/src/model/project.rs @@ -70,8 +70,8 @@ pub trait API:Debug { fn rename_project<'a>(&'a self, name:String) -> BoxFuture<'a,FallibleResult<()>>; /// Returns the primary content root id for this project. - fn content_root_id(&self) -> Uuid { - self.json_rpc().project_root().id + fn project_content_root_id(&self) -> Uuid { + self.json_rpc().project_root().id() } /// Generates full module's qualified name that includes the leading project name segment. diff --git a/src/rust/ide/src/model/project/synchronized.rs b/src/rust/ide/src/model/project/synchronized.rs index a18a538776..8ff66b3144 100644 --- a/src/rust/ide/src/model/project/synchronized.rs +++ b/src/rust/ide/src/model/project/synchronized.rs @@ -451,8 +451,8 @@ impl model::project::API for Project { }.boxed_local() } - fn content_root_id(&self) -> Uuid { - self.language_server_rpc.project_root().id + fn project_content_root_id(&self) -> Uuid { + self.language_server_rpc.project_root().id() } fn subscribe(&self) -> Subscriber { From 6a828578025aef56f43b4e968ecb2822a57598f6 Mon Sep 17 00:00:00 2001 From: Adam Obuchowicz Date: Mon, 12 Jul 2021 19:03:15 +0200 Subject: [PATCH 3/9] Fixes after cherry-picking --- src/rust/ensogl/lib/components/src/selector/shape.rs | 1 - .../ide/lib/enso-protocol/src/language_server/tests.rs | 7 +------ .../ide/lib/enso-protocol/src/language_server/types.rs | 1 + src/rust/ide/src/controller/graph.rs | 2 +- src/rust/ide/src/controller/project.rs | 4 ++-- src/rust/ide/src/controller/upload.rs | 4 ++-- src/rust/ide/src/ide.rs | 2 +- src/rust/ide/src/model/project.rs | 2 +- src/rust/ide/tests/language_server.rs | 6 +++--- 9 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/rust/ensogl/lib/components/src/selector/shape.rs b/src/rust/ensogl/lib/components/src/selector/shape.rs index 2e28c0bab7..488e95ce91 100644 --- a/src/rust/ensogl/lib/components/src/selector/shape.rs +++ b/src/rust/ensogl/lib/components/src/selector/shape.rs @@ -246,7 +246,6 @@ mod tests { use enso_frp::io::mouse::Button; use enso_frp::stream::EventEmitter; use enso_frp::stream::ValueProvider; - use float_eq::assert_float_eq; #[test] fn test_shape_is_dragged() { diff --git a/src/rust/ide/lib/enso-protocol/src/language_server/tests.rs b/src/rust/ide/lib/enso-protocol/src/language_server/tests.rs index a4a21e15ca..c713246246 100644 --- a/src/rust/ide/lib/enso-protocol/src/language_server/tests.rs +++ b/src/rust/ide/lib/enso-protocol/src/language_server/tests.rs @@ -261,11 +261,7 @@ fn test_file_requests() { #[test] fn test_protocol_connection() { let init_protocol_connection_response = response::InitProtocolConnection { - content_roots: vec![ContentRoot { - id : default(), - content_root_type : ContentRootType::Project, - name : "Project".to_owned() - }] + content_roots: vec![ContentRoot::Project {id:default()}] }; test_request( |client| client.init_protocol_connection(&uuid::Uuid::default()), @@ -277,7 +273,6 @@ fn test_protocol_connection() { "contentRoots" : [{ "id" : "00000000-0000-0000-0000-000000000000", "type" : "Project", - "name" : "Project", }] }), init_protocol_connection_response diff --git a/src/rust/ide/lib/enso-protocol/src/language_server/types.rs b/src/rust/ide/lib/enso-protocol/src/language_server/types.rs index 8a67e02669..219ecfc6d1 100644 --- a/src/rust/ide/lib/enso-protocol/src/language_server/types.rs +++ b/src/rust/ide/lib/enso-protocol/src/language_server/types.rs @@ -430,6 +430,7 @@ pub enum ContentRoot { } impl ContentRoot { + /// The content root's id. pub fn id(&self) -> Uuid { match self { ContentRoot::Project {id } => *id, diff --git a/src/rust/ide/src/controller/graph.rs b/src/rust/ide/src/controller/graph.rs index 4bb18a4ae4..1189e172b4 100644 --- a/src/rust/ide/src/controller/graph.rs +++ b/src/rust/ide/src/controller/graph.rs @@ -481,7 +481,7 @@ impl Handle { (parent:impl AnyLogger, project:&model::Project, method:&language_server::MethodPointer) -> FallibleResult { let method = method.clone(); - let root_id = project.content_root_id(); + let root_id = project.project_content_root_id(); let module_path = model::module::Path::from_method(root_id,&method)?; let module = project.module(module_path).await?; let definition = module.lookup_method(project.qualified_name(),&method)?; diff --git a/src/rust/ide/src/controller/project.rs b/src/rust/ide/src/controller/project.rs index ea1c633e5b..040a9235dd 100644 --- a/src/rust/ide/src/controller/project.rs +++ b/src/rust/ide/src/controller/project.rs @@ -23,12 +23,12 @@ pub const COMPILING_STDLIB_LABEL:&str = "Compiling standard library. It can take /// The requirements for Engine's version, in format understandable by /// [`semver::VersionReq::parse`]. -pub const ENGINE_VERSION_SUPPORTED : &str = "^0.2.12"; +pub const ENGINE_VERSION_SUPPORTED : &str = "^0.2.13"; /// The Engine version used in projects created in IDE. // Usually it is a good idea to synchronize this version with the bundled Engine version in // src/js/lib/project-manager/src/build.ts. See also https://github.com/enso-org/ide/issues/1359 -pub const ENGINE_VERSION_FOR_NEW_PROJECTS : &str = "0.2.12"; +pub const ENGINE_VERSION_FOR_NEW_PROJECTS : &str = "0.2.13"; /// The name of the module initially opened in the project view. /// diff --git a/src/rust/ide/src/controller/upload.rs b/src/rust/ide/src/controller/upload.rs index 4aa9a24d96..3914114ce6 100644 --- a/src/rust/ide/src/controller/upload.rs +++ b/src/rust/ide/src/controller/upload.rs @@ -283,7 +283,7 @@ impl NodeFromDroppedFileHandler { if !self.data_directory_exists().await? { let to_create = FileSystemObject::Directory { name : DATA_DIR_NAME.to_owned(), - path : Path::new_root(self.project.content_root_id()) + path : Path::new_root(self.project.project_content_root_id()) }; self.project.json_rpc().create_file(&to_create).await? } @@ -311,7 +311,7 @@ impl NodeFromDroppedFileHandler { } fn data_path(&self) -> Path { - Path::new(self.project.content_root_id(),&[DATA_DIR_NAME]) + Path::new(self.project.project_content_root_id(),&[DATA_DIR_NAME]) } } diff --git a/src/rust/ide/src/ide.rs b/src/rust/ide/src/ide.rs index 9538f106f1..f8345d3e88 100644 --- a/src/rust/ide/src/ide.rs +++ b/src/rust/ide/src/ide.rs @@ -88,5 +88,5 @@ impl Ide { /// The Path of the module initially opened after opening project in IDE. pub fn initial_module_path(project:&model::Project) -> FallibleResult { - model::module::Path::from_name_segments(project.content_root_id(),&[INITIAL_MODULE_NAME]) + model::module::Path::from_name_segments(project.project_content_root_id(),&[INITIAL_MODULE_NAME]) } diff --git a/src/rust/ide/src/model/project.rs b/src/rust/ide/src/model/project.rs index cbeacd3be6..feb20f7a4a 100644 --- a/src/rust/ide/src/model/project.rs +++ b/src/rust/ide/src/model/project.rs @@ -180,7 +180,7 @@ pub mod test { /// Sets up root id expectation on the mock project, returning a given id. pub fn expect_root_id(project:&mut MockAPI, root_id:Uuid) { - project.expect_content_root_id().return_const(root_id); + project.expect_project_content_root_id().return_const(root_id); } /// Sets up suggestion database expectation on the mock project, returning a given database. diff --git a/src/rust/ide/tests/language_server.rs b/src/rust/ide/tests/language_server.rs index f0b19bd465..98a53e5968 100644 --- a/src/rust/ide/tests/language_server.rs +++ b/src/rust/ide/tests/language_server.rs @@ -67,7 +67,7 @@ async fn ls_text_protocol_test() { let ide = setup_ide().await; let project = ide.current_project(); let client = project.json_rpc(); - let root_id = project.content_root_id(); + let root_id = project.project_content_root_id(); let project_name = project.qualified_name(); // Initialize files. @@ -240,7 +240,7 @@ async fn file_events() { let client_id = uuid::Uuid::default(); let session = client.init_protocol_connection(&client_id).await; let session = session.expect("Couldn't initialize session."); - let root_id = session.content_roots[0].id; + let root_id = session.content_roots[0].id(); let path = Path{root_id,segments:vec!["test.txt".into()]}; let file = client.file_exists(&path).await; @@ -298,7 +298,7 @@ async fn file_operations_test() { let project = ide.current_project(); info!(logger,"Got project: {project:?}"); // Edit file using the text protocol - let path = Path::new(project.json_rpc().project_root().id, &["test_file.txt"]); + let path = Path::new(project.json_rpc().project_root().id(), &["test_file.txt"]); let contents = "Hello, 世界!".to_string(); let written = project.json_rpc().write_file(&path,&contents).await.unwrap(); info!(logger,"Written: {written:?}"); From 6b6c5eeb912e193b49d58936b5e9e22a6e2be982 Mon Sep 17 00:00:00 2001 From: Adam Obuchowicz Date: Mon, 12 Jul 2021 19:05:25 +0200 Subject: [PATCH 4/9] Bump version in build.ts and add changelog note --- CHANGELOG.md | 15 +++++++++++++++ src/js/lib/project-manager/src/build.ts | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f52f4ae60..8b89358c09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Next Release +This update contains major performance improvements and exposes new privacy user +settings. We will work towards stabilizing it in the next weeks in order to make +these updates be shipped in a stable release before the end of the year. + +
![New Features](/docs/assets/tags/new_features.svg) + +#### Enso Compiler + +- [Updated Enso engine to version 0.2.13][xxxx]. If you're interested in the + enhancements and fixes made to the Enso compiler, you can find out more + details in + [the engine release notes](https://github.com/enso-org/enso/blob/main/RELEASES.md). + +[xxxx]: https://github.com/enso-org/ide/pull/xxxx + # Enso 2.0.0-alpha.8 (2021-06-09)
![New Features](/docs/assets/tags/new_features.svg) diff --git a/src/js/lib/project-manager/src/build.ts b/src/js/lib/project-manager/src/build.ts index 351ec42b21..c6c4553be8 100644 --- a/src/js/lib/project-manager/src/build.ts +++ b/src/js/lib/project-manager/src/build.ts @@ -44,7 +44,7 @@ async function get_project_manager_url(): Promise { // This constant MUST be synchronized with `ENGINE` constant in src/js/lib/client/tasks/signArchives.js. // Also it is usually a good idea to synchronize it with `ENGINE_VERSION_FOR_NEW_PROJECTS` in // src/rust/ide/src/controller/project.rs. See also https://github.com/enso-org/ide/issues/1359 - const version = '0.2.12' + const version = '0.2.13' let base_url: string = 'https://github.com/enso-org/' base_url += 'enso/releases/download/' base_url += `enso-${version}/enso-project-manager-${version}` From 58fe0bc5e33b49cd7b4a8274a9d923e18cb4a3b9 Mon Sep 17 00:00:00 2001 From: Adam Obuchowicz Date: Mon, 12 Jul 2021 19:52:29 +0200 Subject: [PATCH 5/9] Fixes --- CHANGELOG.md | 4 ++-- src/rust/ide/lib/parser/tests/parsing.rs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b89358c09..b9b72b3cac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,12 +8,12 @@ these updates be shipped in a stable release before the end of the year. #### Enso Compiler -- [Updated Enso engine to version 0.2.13][xxxx]. If you're interested in the +- [Updated Enso engine to version 0.2.13][1710]. If you're interested in the enhancements and fixes made to the Enso compiler, you can find out more details in [the engine release notes](https://github.com/enso-org/enso/blob/main/RELEASES.md). -[xxxx]: https://github.com/enso-org/ide/pull/xxxx +[1710]: https://github.com/enso-org/ide/pull/1710 # Enso 2.0.0-alpha.8 (2021-06-09) diff --git a/src/rust/ide/lib/parser/tests/parsing.rs b/src/rust/ide/lib/parser/tests/parsing.rs index eae6376c18..94aa77ca09 100644 --- a/src/rust/ide/lib/parser/tests/parsing.rs +++ b/src/rust/ide/lib/parser/tests/parsing.rs @@ -395,7 +395,6 @@ impl Fixture { let macro_usages = vec! [ "[]", "[1,2,3]" , "{x}" - , "unsafe x", "private x" , "polyglot java import com.example.MyClass" , "foo -> bar" , "()" From dbb8605bf6ca497ae093f1a9015912bbbac318e5 Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Tue, 13 Jul 2021 15:16:44 +0200 Subject: [PATCH 6/9] Bump engine version in signArchives.js. --- src/js/lib/client/tasks/signArchives.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/lib/client/tasks/signArchives.js b/src/js/lib/client/tasks/signArchives.js index c9ce600ccc..9eb60e1a66 100644 --- a/src/js/lib/client/tasks/signArchives.js +++ b/src/js/lib/client/tasks/signArchives.js @@ -22,7 +22,7 @@ const resRoot = path.join(contentRoot, 'Resources') // TODO: Refactor this once we have a better wau to get the used engine version. // See the tracking issue for more information https://github.com/enso-org/ide/issues/1359 -const ENGINE = '0.2.12' +const ENGINE = '0.2.13' const ID = '"Developer ID Application: New Byte Order Sp. z o. o. (NM77WTZJFQ)"' // Placeholder name for temporary archives. const tmpArchive = 'temporary_archive.zip' From e81a819daa9ae76bfe1e374953fa4ab8481eb628 Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Thu, 15 Jul 2021 09:45:42 +0200 Subject: [PATCH 7/9] fix: Add visualisation import to projects to ensure the library gets preloaded and does not delay showing visualisations. --- src/rust/ide/src/controller/project.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/rust/ide/src/controller/project.rs b/src/rust/ide/src/controller/project.rs index 040a9235dd..ab512fffe5 100644 --- a/src/rust/ide/src/controller/project.rs +++ b/src/rust/ide/src/controller/project.rs @@ -4,7 +4,9 @@ use crate::prelude::*; use crate::controller::graph::executed::Notification as GraphNotification; use crate::controller::ide::StatusNotificationPublisher; +use crate::double_representation::module::ImportInfo; use crate::double_representation::project; +use crate::model::module::QualifiedName; use enso_frp::web::platform; use enso_frp::web::platform::Platform; @@ -23,12 +25,12 @@ pub const COMPILING_STDLIB_LABEL:&str = "Compiling standard library. It can take /// The requirements for Engine's version, in format understandable by /// [`semver::VersionReq::parse`]. -pub const ENGINE_VERSION_SUPPORTED : &str = "^0.2.13"; +pub const ENGINE_VERSION_SUPPORTED : &str = "^0.2.14-SNAPSHOT.2021-07-14.1"; /// The Engine version used in projects created in IDE. // Usually it is a good idea to synchronize this version with the bundled Engine version in // src/js/lib/project-manager/src/build.ts. See also https://github.com/enso-org/ide/issues/1359 -pub const ENGINE_VERSION_FOR_NEW_PROJECTS : &str = "0.2.13"; +pub const ENGINE_VERSION_FOR_NEW_PROJECTS : &str = "0.2.14-SNAPSHOT.2021-07-14.1"; /// The name of the module initially opened in the project view. /// @@ -128,9 +130,13 @@ impl Project { // until proper decision is made. See: https://github.com/enso-org/enso/issues/1050 self.recreate_if_missing(&file_path,default_main_method_code()).await?; let method = main_method_ptr(project.qualified_name(),&module_path); - let module = self.model.module(module_path).await?; + let module = self.model.module(module_path.clone()).await?; Self::add_main_if_missing(project.qualified_name(),&module,&method,&parser)?; + let mut info = module.info(); + info.add_module_import(&project.qualified_module_name(&module_path), &project.parser(), &QualifiedName::from_text("Standard.Visualization").unwrap()); + module.update_ast(info.ast)?; + // Here, we should be relatively certain (except race conditions in case of multiple // clients that we currently do not support) that main module exists and contains main // method. Thus, we should be able to successfully create a graph controller for it. @@ -140,6 +146,8 @@ impl Project { self.notify_about_compiling_process(&main_graph); self.display_warning_on_unsupported_engine_version()?; + + Ok(InitializationResult {main_module_text,main_graph}) } } From 0ee7b5202009717d6faf2385dd264d6d512c5e47 Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Thu, 15 Jul 2021 23:50:58 +0200 Subject: [PATCH 8/9] feat: Upgrade engine to 0.2.14, disable engine version check for projects. --- CHANGELOG.md | 2 +- src/js/lib/client/tasks/signArchives.js | 2 +- src/js/lib/project-manager/src/build.ts | 2 +- src/rust/ide/src/controller/project.rs | 20 +++++++++++--------- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9b72b3cac..57b76382dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ these updates be shipped in a stable release before the end of the year. #### Enso Compiler -- [Updated Enso engine to version 0.2.13][1710]. If you're interested in the +- [Updated Enso engine to version 0.2.14][1710]. If you're interested in the enhancements and fixes made to the Enso compiler, you can find out more details in [the engine release notes](https://github.com/enso-org/enso/blob/main/RELEASES.md). diff --git a/src/js/lib/client/tasks/signArchives.js b/src/js/lib/client/tasks/signArchives.js index 9eb60e1a66..8e3c6277aa 100644 --- a/src/js/lib/client/tasks/signArchives.js +++ b/src/js/lib/client/tasks/signArchives.js @@ -22,7 +22,7 @@ const resRoot = path.join(contentRoot, 'Resources') // TODO: Refactor this once we have a better wau to get the used engine version. // See the tracking issue for more information https://github.com/enso-org/ide/issues/1359 -const ENGINE = '0.2.13' +const ENGINE = '0.2.14' const ID = '"Developer ID Application: New Byte Order Sp. z o. o. (NM77WTZJFQ)"' // Placeholder name for temporary archives. const tmpArchive = 'temporary_archive.zip' diff --git a/src/js/lib/project-manager/src/build.ts b/src/js/lib/project-manager/src/build.ts index c6c4553be8..826894105c 100644 --- a/src/js/lib/project-manager/src/build.ts +++ b/src/js/lib/project-manager/src/build.ts @@ -44,7 +44,7 @@ async function get_project_manager_url(): Promise { // This constant MUST be synchronized with `ENGINE` constant in src/js/lib/client/tasks/signArchives.js. // Also it is usually a good idea to synchronize it with `ENGINE_VERSION_FOR_NEW_PROJECTS` in // src/rust/ide/src/controller/project.rs. See also https://github.com/enso-org/ide/issues/1359 - const version = '0.2.13' + const version = '0.2.14' let base_url: string = 'https://github.com/enso-org/' base_url += 'enso/releases/download/' base_url += `enso-${version}/enso-project-manager-${version}` diff --git a/src/rust/ide/src/controller/project.rs b/src/rust/ide/src/controller/project.rs index ab512fffe5..a35e8e3231 100644 --- a/src/rust/ide/src/controller/project.rs +++ b/src/rust/ide/src/controller/project.rs @@ -25,12 +25,12 @@ pub const COMPILING_STDLIB_LABEL:&str = "Compiling standard library. It can take /// The requirements for Engine's version, in format understandable by /// [`semver::VersionReq::parse`]. -pub const ENGINE_VERSION_SUPPORTED : &str = "^0.2.14-SNAPSHOT.2021-07-14.1"; +pub const ENGINE_VERSION_SUPPORTED : &str = "^0.2.141"; /// The Engine version used in projects created in IDE. // Usually it is a good idea to synchronize this version with the bundled Engine version in // src/js/lib/project-manager/src/build.ts. See also https://github.com/enso-org/ide/issues/1359 -pub const ENGINE_VERSION_FOR_NEW_PROJECTS : &str = "0.2.14-SNAPSHOT.2021-07-14.1"; +pub const ENGINE_VERSION_FOR_NEW_PROJECTS : &str = "0.2.14"; /// The name of the module initially opened in the project view. /// @@ -200,13 +200,15 @@ impl Project { } fn display_warning_on_unsupported_engine_version(&self) -> FallibleResult { - let requirements = semver::VersionReq::parse(ENGINE_VERSION_SUPPORTED)?; - let version = self.model.engine_version(); - if !requirements.matches(&version) { - let message = format!("Unsupported Engine version. Please update engine_version in {} \ - to {}.",package_yaml_path(&self.model.name()),ENGINE_VERSION_FOR_NEW_PROJECTS); - self.status_notifications.publish_event(message); - } + // FIXME[MM]: Disabled as it needs updating to the new edition system. + // See https://github.com/enso-org/ide/issues/1713 for more information. + // let requirements = semver::VersionReq::parse(ENGINE_VERSION_SUPPORTED)?; + // let version = self.model.engine_version(); + // if !requirements.matches(&version) { + // let message = format!("Unsupported Engine version. Please update engine_version in {} \ + // to {}.",package_yaml_path(&self.model.name()),ENGINE_VERSION_FOR_NEW_PROJECTS); + // self.status_notifications.publish_event(message); + // } Ok(()) } } From 108e483938b0d35f9a35458f677e31eb79b3fab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20W=2E=20Urba=C5=84czyk?= Date: Mon, 19 Jul 2021 16:15:50 +0200 Subject: [PATCH 9/9] API update to support the newly added reexport field. --- .../src/language_server/types.rs | 23 ++++- src/rust/ide/src/controller/project.rs | 1 - src/rust/ide/src/controller/searcher.rs | 2 + src/rust/ide/src/ide/integration/project.rs | 1 + src/rust/ide/src/lib.rs | 1 + src/rust/ide/src/model/suggestion_database.rs | 32 ++++--- .../src/model/suggestion_database/entry.rs | 83 ++++++++++++++++--- src/rust/ide/src/test.rs | 6 +- 8 files changed, 119 insertions(+), 30 deletions(-) diff --git a/src/rust/ide/lib/enso-protocol/src/language_server/types.rs b/src/rust/ide/lib/enso-protocol/src/language_server/types.rs index 219ecfc6d1..f7cfb3e73a 100644 --- a/src/rust/ide/lib/enso-protocol/src/language_server/types.rs +++ b/src/rust/ide/lib/enso-protocol/src/language_server/types.rs @@ -820,6 +820,15 @@ pub enum SuggestionEntryType {Atom,Method,Function,Local} #[serde(tag="type")] #[serde(rename_all="camelCase")] pub enum SuggestionEntry { + #[serde(rename_all="camelCase")] + Module { + /// The fully qualified module name. + module : String, + /// The documentation string. + documentation : Option, + /// The module name re-exporting this module. + reexport : Option, + }, #[serde(rename_all="camelCase")] Atom { external_id : Option, @@ -828,6 +837,8 @@ pub enum SuggestionEntry { arguments : Vec, return_type : String, documentation : Option, + /// The module name re-exporting this atom. + reexport : Option, }, #[serde(rename_all="camelCase")] Method { @@ -838,6 +849,8 @@ pub enum SuggestionEntry { self_type : String, return_type : String, documentation : Option, + /// The module name re-exporting this method. + reexport : Option, }, #[serde(rename_all="camelCase")] Function { @@ -862,10 +875,11 @@ impl SuggestionEntry { /// Get name of the suggested entity. pub fn name(&self) -> &String { match self { - Self::Atom {name,..} => name, - Self::Function {name,..} => name, - Self::Local {name,..} => name, - Self::Method {name,..} => name, + Self::Module {module,..} => module, + Self::Atom {name,..} => name, + Self::Function {name,..} => name, + Self::Local {name,..} => name, + Self::Method {name,..} => name, } } } @@ -994,6 +1008,7 @@ pub struct SuggestionsDatabaseModification { pub return_type : Option>, pub documentation : Option>, pub scope : Option>, + pub reexport : Option>, } /// Notification about change in the suggestions database. diff --git a/src/rust/ide/src/controller/project.rs b/src/rust/ide/src/controller/project.rs index a35e8e3231..d000cf4148 100644 --- a/src/rust/ide/src/controller/project.rs +++ b/src/rust/ide/src/controller/project.rs @@ -4,7 +4,6 @@ use crate::prelude::*; use crate::controller::graph::executed::Notification as GraphNotification; use crate::controller::ide::StatusNotificationPublisher; -use crate::double_representation::module::ImportInfo; use crate::double_representation::project; use crate::model::module::QualifiedName; diff --git a/src/rust/ide/src/controller/searcher.rs b/src/rust/ide/src/controller/searcher.rs index b91c3d2402..1fae5a4170 100644 --- a/src/rust/ide/src/controller/searcher.rs +++ b/src/rust/ide/src/controller/searcher.rs @@ -1023,6 +1023,7 @@ impl Searcher { documentation : None, self_type : Some(tp::QualifiedName::from_text(ENSO_PROJECT_SPECIAL_MODULE)?), scope : model::suggestion_database::entry::Scope::Everywhere, + reexport : None, }; actions.extend(std::iter::once(Action::Suggestion(Rc::new(entry)))); } @@ -1249,6 +1250,7 @@ pub mod test { documentation : default(), self_type : None, scope : Scope::InModule {range:code_range}, + reexport : None, }; let entry2 = model::suggestion_database::Entry { name : "TestVar1".to_string(), diff --git a/src/rust/ide/src/ide/integration/project.rs b/src/rust/ide/src/ide/integration/project.rs index 0e78d2ec6f..d9e2600c63 100644 --- a/src/rust/ide/src/ide/integration/project.rs +++ b/src/rust/ide/src/ide/integration/project.rs @@ -1469,6 +1469,7 @@ struct DataProviderForView { impl DataProviderForView { fn doc_placeholder_for(suggestion:&controller::searcher::action::Suggestion) -> String { let title = match suggestion.kind { + suggestion_database::entry::Kind::Module => "Module", suggestion_database::entry::Kind::Atom => "Atom", suggestion_database::entry::Kind::Function => "Function", suggestion_database::entry::Kind::Local => "Local variable", diff --git a/src/rust/ide/src/lib.rs b/src/rust/ide/src/lib.rs index e1c28fb49f..33707d83ec 100644 --- a/src/rust/ide/src/lib.rs +++ b/src/rust/ide/src/lib.rs @@ -14,6 +14,7 @@ #![feature(result_cloned)] #![feature(result_into_ok_or_err)] #![feature(map_try_insert)] +#![feature(assert_matches)] #![recursion_limit="256"] #![warn(missing_docs)] #![warn(trivial_casts)] diff --git a/src/rust/ide/src/model/suggestion_database.rs b/src/rust/ide/src/model/suggestion_database.rs index 32f2bf3cfd..ca8854e09e 100644 --- a/src/rust/ide/src/model/suggestion_database.rs +++ b/src/rust/ide/src/model/suggestion_database.rs @@ -270,6 +270,7 @@ mod test { return_type : "TestAtom".to_string(), documentation : None, external_id : None, + reexport : None, }; let db_entry = SuggestionsDatabaseEntry {id:12, suggestion:entry}; let response = language_server::response::GetSuggestionDatabase { @@ -294,6 +295,7 @@ mod test { return_type : "TestAtom".to_owned(), documentation : None, external_id : None, + reexport : None, }; let entry2 = language_server::types::SuggestionEntry::Atom { name : "Entry2".to_owned(), @@ -302,6 +304,7 @@ mod test { return_type : "TestAtom".to_owned(), documentation : None, external_id : None, + reexport : None, }; let new_entry2 = language_server::types::SuggestionEntry::Atom { name : "NewEntry2".to_owned(), @@ -310,6 +313,7 @@ mod test { return_type : "TestAtom".to_owned(), documentation : None, external_id : None, + reexport : None, }; let arg1 = SuggestionEntryArgument { name : "Argument1".to_owned(), @@ -381,6 +385,7 @@ mod test { assert_eq!(db.version.get(), 3); // Empty modify + let entry1_reexporter = QualifiedName::from_text("local.TestProject.Reexporter").unwrap(); let modify_update = entry::Update::Modify { id : 1, external_id : None, @@ -390,7 +395,8 @@ mod test { self_type : None, return_type : None, documentation : None, - scope : None + scope : None, + reexport : Some(FieldUpdate::set(entry1_reexporter.to_string())), }), }; let update = SuggestionDatabaseUpdatesEvent { @@ -404,13 +410,14 @@ mod test { assert_eq!(db.lookup(1).unwrap().arguments , vec![]); assert_eq!(db.lookup(1).unwrap().return_type , "TestAtom"); assert_eq!(db.lookup(1).unwrap().documentation, None); - assert!(matches!(db.lookup(1).unwrap().scope, Scope::Everywhere)); + assert_matches!(db.lookup(1).unwrap().scope , Scope::Everywhere); + assert_eq!(db.lookup(1).unwrap().reexport , Some(entry1_reexporter)); assert_eq!(db.version.get(), 4); // Modify with some invalid fields let modify_update = entry::Update::Modify { - id : 1, - external_id : None, + id : 1, + external_id : None, modification : Box::new(SuggestionsDatabaseModification { // Invalid: the entry does not have any arguments. arguments:vec![SuggestionArgumentUpdate::Remove {index:0}], @@ -423,8 +430,9 @@ mod test { start : Position {line:4, character:10}, end : Position {line:8, character:12} })), - module:None, - self_type:None, + module : None, + self_type : None, + reexport : None, }), }; let update = SuggestionDatabaseUpdatesEvent { @@ -462,6 +470,7 @@ mod test { })), self_type : None, module : None, + reexport : None, }), }; let update = SuggestionDatabaseUpdatesEvent { @@ -494,11 +503,13 @@ mod test { external_id : None, modification : Box::new(SuggestionsDatabaseModification { arguments : vec![SuggestionArgumentUpdate::Add {index:2, argument:new_argument}], + module : None, return_type : None, + self_type : None, documentation : None, scope : None, - self_type : None, - module : None, + reexport : None, + }), }; let update = SuggestionDatabaseUpdatesEvent { @@ -519,11 +530,12 @@ mod test { external_id : None, modification : Box::new(SuggestionsDatabaseModification { arguments : vec![SuggestionArgumentUpdate::Remove {index:2}], + module : None, return_type : None, + self_type : None, documentation : None, scope : None, - self_type : None, - module : None, + reexport : None, }), }; let update = SuggestionDatabaseUpdatesEvent { diff --git a/src/rust/ide/src/model/suggestion_database/entry.rs b/src/rust/ide/src/model/suggestion_database/entry.rs index 1325edb4c7..cf809aa7fb 100644 --- a/src/rust/ide/src/model/suggestion_database/entry.rs +++ b/src/rust/ide/src/model/suggestion_database/entry.rs @@ -57,7 +57,7 @@ pub struct MissingThisOnMethod(pub String); #[derive(Copy,Clone,Debug,Eq,PartialEq)] #[allow(missing_docs)] pub enum Kind { - Atom,Function,Local,Method + Module,Atom,Function,Local,Method } /// Describes the visibility range of some entry (i.e. identifier available as suggestion). @@ -106,6 +106,8 @@ pub struct Entry { pub self_type : Option, /// A scope where this suggestion is visible. pub scope : Scope, + /// A module that reexports this symbol. + pub reexport : Option, } impl Entry { @@ -233,19 +235,35 @@ impl Entry { -> FallibleResult { use language_server::types::SuggestionEntry::*; let this = match entry { - Atom {name,module,arguments,return_type,documentation,..} => Self { + Module {module,documentation,reexport} => Self { + documentation, + name : model::module::QualifiedName::from_text(&module)?.name().into(), + reexport : match reexport { + Some(reexport) => Some(reexport.try_into()?), + None => None, + }, + kind : Kind::Module, + arguments : vec![], + return_type : module.clone(), + scope : Scope::Everywhere, + module : module.try_into()?, + self_type : None, + }, + Atom {name,module,arguments,return_type,documentation,reexport,..} => Self { name,arguments,return_type,documentation, module : module.try_into()?, self_type : None, kind : Kind::Atom, scope : Scope::Everywhere, + reexport : reexport.map(TryInto::try_into).transpose()?, }, - Method {name,module,arguments,self_type,return_type,documentation,..} => Self { + Method {name,module,arguments,self_type,return_type,documentation,reexport,..} => Self { name,arguments,return_type,documentation, module : module.try_into()?, self_type : Some(self_type.try_into()?), kind : Kind::Method, scope : Scope::Everywhere, + reexport : reexport.map(TryInto::try_into).transpose()?, }, Function {name,module,arguments,return_type,scope,..} => Self { name,arguments,return_type, @@ -254,6 +272,7 @@ impl Entry { documentation : default(), kind : Kind::Function, scope : Scope::InModule {range:scope.into()}, + reexport : None, }, Local {name,module,return_type,scope,..} => Self { name,return_type, @@ -263,6 +282,7 @@ impl Entry { documentation : default(), kind : Kind::Local, scope : Scope::InModule {range:scope.into()}, + reexport : None, }, }; Ok(this) @@ -271,20 +291,25 @@ impl Entry { /// Apply modification to the entry. pub fn apply_modifications (&mut self, modification:SuggestionsDatabaseModification) -> Vec { - let m = modification; - let module = m.module.map(|f| f.try_map(module::QualifiedName::from_text)).transpose(); - let self_type = m.self_type.map(|f| f.try_map(tp::QualifiedName::from_text)).transpose(); + // Make sure that we do not miss any field. + let SuggestionsDatabaseModification { + arguments,module,self_type,return_type,documentation,scope,reexport + } = modification; + let module = module.map(|f| f.try_map(module::QualifiedName::from_text)).transpose(); + let self_type = self_type.map(|f| f.try_map(tp::QualifiedName::from_text)).transpose(); + let reexport = reexport.map(|f| f.try_map(module::QualifiedName::from_text)).transpose(); let other_update_results = - [ Entry::apply_field_update ("return_type" , &mut self.return_type , m.return_type ) - , Entry::apply_opt_field_update("documentation", &mut self.documentation, m.documentation) + [ Entry::apply_field_update ("return_type" , &mut self.return_type , return_type ) + , Entry::apply_opt_field_update("documentation", &mut self.documentation, documentation) , module.and_then (|m| Entry::apply_field_update ("module" , &mut self.module , m)) , self_type.and_then(|s| Entry::apply_opt_field_update("self_type", &mut self.self_type, s)) - , self.apply_scope_update(m.scope) + , reexport.and_then (|s| Entry::apply_opt_field_update("reexport" , &mut self.reexport , s)) + , self.apply_scope_update(scope) ]; let other_update_results = SmallVec::from_buf(other_update_results).into_iter(); let other_update_errors = other_update_results.filter_map(|res| res.err()); - let arg_update_errors = m.arguments.into_iter().flat_map(|arg| self.apply_arg_update(arg)); - arg_update_errors.chain(other_update_errors).collect_vec() + let arg_update_errors = arguments.into_iter().flat_map(|arg| self.apply_arg_update(arg)); + arg_update_errors.chain(other_update_errors).collect() } fn apply_arg_update(&mut self, update:language_server::types::SuggestionArgumentUpdate) @@ -426,6 +451,7 @@ mod test { use super::*; + fn expect ( entry : &Entry , current_module : Option<&module::QualifiedName> @@ -441,6 +467,23 @@ mod test { fn code_from_entry() { let main_module = module::QualifiedName::from_text("local.Project.Main").unwrap(); let another_module = module::QualifiedName::from_text("local.Project.Another_Module").unwrap(); + let main_module_entry = Entry { + name : main_module.name().to_string(), + kind : Kind::Module, + module : main_module.clone(), + arguments : vec![], + return_type : main_module.name().to_string(), + documentation : None, + self_type : None, + scope : Scope::Everywhere, + reexport : None, + }; + let another_module_entry = Entry { + name : another_module.name().to_string(), + module : another_module.clone(), + return_type : another_module.name().to_string(), + ..main_module_entry.clone() + }; let atom = Entry { name : "Atom".to_owned(), kind : Kind::Atom, @@ -450,6 +493,7 @@ mod test { documentation : None, self_type : None, scope : Scope::Everywhere, + reexport : None, }; let method = Entry { name : "method".to_string(), @@ -479,14 +523,26 @@ mod test { ..module_method.clone() }; + expect(&main_module_entry, None, true, "Main", &[&main_module]); + expect(&main_module_entry, None, false, "Main", &[&main_module]); + expect(&main_module_entry, Some(&main_module), true, "Main", &[]); + expect(&main_module_entry, Some(&main_module), false, "Main", &[]); + expect(&main_module_entry, Some(&another_module), true, "Main", &[&main_module]); + expect(&main_module_entry, Some(&another_module), false, "Main", &[&main_module]); + + expect(&another_module_entry, None, true, "Another_Module", &[&another_module]); + expect(&another_module_entry, None, false, "Another_Module", &[&another_module]); + expect(&another_module_entry, Some(&main_module), true, "Another_Module", &[&another_module]); + expect(&another_module_entry, Some(&main_module), false, "Another_Module", &[&another_module]); + expect(&another_module_entry, Some(&another_module), true, "Another_Module", &[]); + expect(&another_module_entry, Some(&another_module), false, "Another_Module", &[]); + expect(&atom, None, true, "Atom", &[&main_module]); expect(&atom, None, false, "Atom", &[&main_module]); expect(&atom, Some(&main_module), true, "Atom", &[]); expect(&atom, Some(&main_module), false, "Atom", &[]); expect(&atom, Some(&another_module), true, "Atom", &[&main_module]); expect(&atom, Some(&another_module), false, "Atom", &[&main_module]); - expect(&atom, Some(&another_module), true, "Atom", &[&main_module]); - expect(&atom, Some(&another_module), false, "Atom", &[&main_module]); expect(&method, None, true, "method", &[&main_module]); expect(&method, None, false, "method", &[&main_module]); @@ -538,6 +594,7 @@ mod test { documentation : None, self_type : None, scope : Scope::Everywhere, + reexport : None, }; let method = Entry { name : "method".to_string(), diff --git a/src/rust/ide/src/test.rs b/src/rust/ide/src/test.rs index 13e209500a..a728262f6f 100644 --- a/src/rust/ide/src/test.rs +++ b/src/rust/ide/src/test.rs @@ -104,7 +104,8 @@ pub mod mock { return_type : "Any".to_owned(), kind : suggestion_database::entry::Kind::Method, scope : suggestion_database::entry::Scope::Everywhere, - documentation : None + documentation : None, + reexport : None, } } @@ -118,7 +119,8 @@ pub mod mock { return_type : "Any".to_owned(), kind : suggestion_database::entry::Kind::Method, scope : suggestion_database::entry::Scope::Everywhere, - documentation : None + documentation : None, + reexport : None, } } }