diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1f52f4ae60..57b76382dc 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.
+
+

+
+#### Enso Compiler
+
+- [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).
+
+[1710]: https://github.com/enso-org/ide/pull/1710
+
# Enso 2.0.0-alpha.8 (2021-06-09)

diff --git a/src/js/lib/client/tasks/signArchives.js b/src/js/lib/client/tasks/signArchives.js
index c9ce600ccc..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.12'
+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 351ec42b21..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.12'
+ 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/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/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/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/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 8d60030a7b..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
@@ -390,32 +390,56 @@ 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 {
+ /// The content root's id.
+ 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,
+ }
+ }
}
@@ -796,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,
@@ -804,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 {
@@ -814,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 {
@@ -838,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,
}
}
}
@@ -970,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/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/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"
, "()"
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..1189e172b4 100644
--- a/src/rust/ide/src/controller/graph.rs
+++ b/src/rust/ide/src/controller/graph.rs
@@ -481,10 +481,10 @@ 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.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..d000cf4148 100644
--- a/src/rust/ide/src/controller/project.rs
+++ b/src/rust/ide/src/controller/project.rs
@@ -4,6 +4,8 @@ use crate::prelude::*;
use crate::controller::graph::executed::Notification as GraphNotification;
use crate::controller::ide::StatusNotificationPublisher;
+use crate::double_representation::project;
+use crate::model::module::QualifiedName;
use enso_frp::web::platform;
use enso_frp::web::platform::Platform;
@@ -22,12 +24,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.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.12";
+pub const ENGINE_VERSION_FOR_NEW_PROJECTS : &str = "0.2.14";
/// The name of the module initially opened in the project view.
///
@@ -52,7 +54,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 +128,13 @@ 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 module = self.model.module(module_path).await?;
- Self::add_main_if_missing(project.name().as_ref(),&module,&method,&parser)?;
+ let method = main_method_ptr(project.qualified_name(),&module_path);
+ 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
@@ -138,6 +145,8 @@ impl Project {
self.notify_about_compiling_process(&main_graph);
self.display_warning_on_unsupported_engine_version()?;
+
+
Ok(InitializationResult {main_module_text,main_graph})
}
}
@@ -164,7 +173,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();
@@ -190,13 +199,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(())
}
}
@@ -226,22 +237,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..1fae5a4170 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).
@@ -1020,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))));
}
@@ -1128,7 +1132,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 +1212,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),
@@ -1244,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(),
@@ -1274,8 +1281,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 +1323,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 +1446,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 +1475,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 +1800,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 +1813,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 +1829,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 +1851,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 +1910,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/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/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/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.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/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/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/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..feb20f7a4a 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;
@@ -64,14 +70,14 @@ 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.
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
@@ -174,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.
@@ -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..8ff66b3144 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,15 +442,17 @@ 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()
}
- 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 {
@@ -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.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 9528f3a8b5..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 {
@@ -150,7 +152,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
};
@@ -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>
@@ -439,8 +465,25 @@ 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 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,11 +493,12 @@ mod test {
documentation : None,
self_type : None,
scope : Scope::Everywhere,
+ reexport : None,
};
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 {
@@ -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 b91dfd0a54..a728262f6f 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,28 +95,32 @@ 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,
scope : suggestion_database::entry::Scope::Everywhere,
- documentation : None
+ documentation : None,
+ reexport : None,
}
}
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,
scope : suggestion_database::entry::Scope::Everywhere,
- documentation : None
+ documentation : None,
+ reexport : None,
}
}
}
@@ -121,7 +131,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 +166,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 +194,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 +215,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 +234,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..98a53e5968 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.project_content_root_id();
+ let project_name = project.qualified_name();
// Initialize files.
let file = Path::new(root_id,&["src","Main.enso"]);
@@ -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:?}");
@@ -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:?}");