Skip to content
This repository has been archived by the owner on Dec 28, 2021. It is now read-only.

Commit

Permalink
Fix Main modules [ci build]
Browse files Browse the repository at this point in the history
  • Loading branch information
farmaazon committed Mar 1, 2021
1 parent ea0f6c0 commit 583af2d
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 27 deletions.
4 changes: 4 additions & 0 deletions src/rust/ide/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ pub const PROJECT_MANAGER_ENDPOINT:&str = "ws://127.0.0.1:30535";
/// Default project name used by IDE on startup.
pub const DEFAULT_PROJECT_NAME:&str = "Unnamed";

/// The name of the main module in each library. The main module is explicitly imported when the
/// import statement has the library name only.
pub const LIBRARIES_MAIN_MODULE:&str = "Main";

/// Visualization folder where IDE can look for user-defined visualizations per project.
pub const VISUALIZATION_DIRECTORY:&str = "visualization";

Expand Down
2 changes: 1 addition & 1 deletion src/rust/ide/src/controller/searcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,7 @@ impl Searcher {
fn add_required_imports(&self) -> FallibleResult {
let data_borrowed = self.data.borrow();
let fragments = data_borrowed.fragments_added_by_picking.iter();
let imports = fragments.map(|frag| &frag.picked_suggestion.module);
let imports = fragments.map(|frag| frag.picked_suggestion.module.clone().remove_main_module_segment());
let mut module = self.module();
let here = self.module_qualified_name();
for import in imports {
Expand Down
7 changes: 6 additions & 1 deletion src/rust/ide/src/controller/searcher/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ impl Action {
pub fn caption(&self) -> String {
match self {
Self::Suggestion(completion) => if let Some(self_type) = completion.self_type.as_ref() {
format!("{}.{}",self_type.name,completion.name)
let should_put_library_name = self_type.name == constants::LIBRARIES_MAIN_MODULE
&& self_type.module_segments.is_empty();
let self_type_name = if should_put_library_name {
self_type.project_name.as_ref()
} else { &self_type.name };
format!("{}.{}",self_type_name,completion.name)
} else {
completion.name.clone()
}
Expand Down
55 changes: 35 additions & 20 deletions src/rust/ide/src/double_representation/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use crate::prelude::*;

use crate::constants::keywords::HERE;
use crate::constants::LIBRARIES_MAIN_MODULE;
use crate::double_representation::definition;
use crate::double_representation::definition::DefinitionProvider;
use crate::double_representation::identifier;
Expand Down Expand Up @@ -35,11 +36,6 @@ pub struct ImportNotFound(pub ImportInfo);
#[allow(missing_docs)]
pub struct LineIndexOutOfBounds;

/// Happens if an empty segments list is provided as qualified module name.
#[derive(Clone,Copy,Debug,Fail)]
#[fail(display="No name segments were provided.")]
pub struct EmptyName;

#[allow(missing_docs)]
#[derive(Clone,Copy,Debug,Fail)]
pub enum InvalidQualifiedName {
Expand Down Expand Up @@ -75,21 +71,17 @@ pub struct NotDirectChild(ast::Crumbs);
/// Includes segments of module path but *NOT* the project name (see: `QualifiedName`).
#[derive(Clone,Debug,Shrinkwrap,PartialEq,Eq,Hash)]
pub struct Id {
/// The vector is non-empty.
/// The vector can be empty - in that case we point to the module called `Main`.
segments:Vec<ReferentName>
}

impl Id {
/// Construct a module's ID value from a name segments sequence.
///
/// Fails if the given sequence is empty.
pub fn new(segments:impl IntoIterator<Item=ReferentName>) -> Result<Id,EmptyName> {
pub fn new(segments:impl IntoIterator<Item=ReferentName>) -> Id {
let segments = segments.into_iter().collect_vec();
if segments.is_empty() {
Err(EmptyName)
} else {
Ok(Id {segments})
}
Id {segments}
}

/// Construct a module's ID value from a name segments sequence.
Expand All @@ -100,7 +92,7 @@ impl Id {
let names = texts.map(|text| ReferentName::new(text.as_ref()));

let segments:Vec<_> = Result::from_iter(names)?;
Self::new(segments).map_err(Into::into)
Ok(Self::new(segments))
}

/// Get the segments of the module's path. They correspond to the module's file parent
Expand All @@ -109,7 +101,7 @@ impl Id {
/// The names are ordered beginning with the root one. The last one is the direct parent of the
/// target module's file. The module name itself is not included.
pub fn parent_segments(&self) -> &[ReferentName] {
&self.segments[..self.segments.len() - 1]
&self.segments[..self.segments.len().saturating_sub(1)]
}

/// Consume the [`Id`] and returns the inner representation of segments.
Expand All @@ -118,9 +110,10 @@ impl Id {
}

/// Get the name of a module identified by this value.
pub fn name(&self) -> &ReferentName {
// Safe, as the invariant guarantees segments to be non-empty.
self.segments.iter().last().unwrap()
pub fn name(&self) -> ReferentName {
self.segments.iter().last().cloned().unwrap_or_else(
|| ReferentName::new(LIBRARIES_MAIN_MODULE).unwrap()
)
}
}

Expand Down Expand Up @@ -208,8 +201,9 @@ impl QualifiedName {
}

/// Get the module's name. It is also the module's typename.
pub fn name(&self) -> &ReferentName {
self.id.name()
pub fn name(&self) -> ReferentName {
if self.id.segments.is_empty() { self.project_name.clone() }
else { self.id.name() }
}

/// Get the name of project owning this module.
Expand All @@ -226,6 +220,28 @@ impl QualifiedName {
pub fn segments(&self) -> impl Iterator<Item=&ReferentName> {
std::iter::once(&self.project_name).chain(self.id.segments.iter())
}

/// Remove the main module segment from the qualified name.
///
/// The main module does not need to be explicitly mentioned, so the modified structure will
/// still identify the same entity.
///
/// ```
/// # use ide::model::module::QualifiedName;
/// let name_with_main = QualifiedName::from_text("Project.Main").unwrap();
/// let name_without_main = QualifiedName::from_text("Project.Foo.Bar").unwrap();
/// let main_but_not_library_main = QualifiedName::from_text("Project.Foo.Main").unwrap();
///
/// assert_eq!(name_with_main .remove_main_module_segment().to_string(), "Project");
/// assert_eq!(name_without_main .remove_main_module_segment().to_string(), "Project.Foo.Bar");
/// assert_eq!(main_but_not_library_main.remove_main_module_segment().to_string(), "Project.Foo.Main");
/// ```
pub fn remove_main_module_segment(mut self) -> Self {
if self.id.segments.len() == 1 && self.id.segments.last().contains_if(|s| s == &&LIBRARIES_MAIN_MODULE) {
self.id.segments.pop();
}
self
}
}

impl TryFrom<&str> for QualifiedName {
Expand Down Expand Up @@ -694,7 +710,6 @@ mod tests {

#[test]
fn qualified_name_validation() {
assert!(QualifiedName::try_from("ProjectName").is_err());
assert!(QualifiedName::try_from("project.Name").is_err());
assert!(QualifiedName::try_from("Project.name").is_err());
assert!(QualifiedName::try_from("Project.").is_err());
Expand Down
6 changes: 3 additions & 3 deletions src/rust/ide/src/model/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ impl Path {
// We prepend source directory and replace trailing segment with filename.
let src_dir = std::iter::once(SOURCE_DIRECTORY.to_owned());
let dirs = id.parent_segments().iter().map(ToString::to_string);
let filename = std::iter::once(Self::module_filename(id.name()));
let filename = std::iter::once(Self::module_filename(&id.name()));
let segments = src_dir.chain(dirs).chain(filename).collect();
let path = FilePath {root_id,segments};
Path {file_path:Rc::new(path)}
Expand Down Expand Up @@ -140,7 +140,7 @@ impl Path {
(root_id:Uuid, name_segments:impl IntoIterator<Item:AsRef<str>>) -> FallibleResult<Path> {
let segment_results = name_segments.into_iter().map(|s| ReferentName::new(s.as_ref()));
let segments:Vec<_> = Result::from_iter(segment_results)?;
let id = Id::new(segments)?;
let id = Id::new(segments);
Ok(Self::from_id(root_id,&id))
}

Expand All @@ -152,7 +152,7 @@ impl Path {
let parent_segments = dirs.iter().map(ReferentName::new).map(Result::unwrap);
let final_segment = std::iter::once(self.module_name());
let segments = parent_segments.chain(final_segment);
Id::new(segments).unwrap()
Id::new(segments)
} else {
// Class invariant guarantees at least two segments in path.
panic!("Unreachable")
Expand Down
4 changes: 2 additions & 2 deletions src/rust/ide/src/model/suggestion_database/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ impl Entry {
pub fn code_to_insert(&self, current_module:Option<&module::QualifiedName>) -> String {
if self.has_self_type(&self.module) {
let module_var = if current_module.contains(&&self.module) {keywords::HERE.to_owned()}
else {self.module.name().clone().into()};
else {self.module.clone().remove_main_module_segment().name().into()};
format!("{}.{}",module_var,self.name)
} else {
self.name.clone()
Expand Down Expand Up @@ -164,7 +164,7 @@ impl Entry {
pub fn from_ls_entry(entry:language_server::types::SuggestionEntry)
-> FallibleResult<Self> {
use language_server::types::SuggestionEntry::*;
let this = match entry {
let mut this = match entry {
Atom {name,module,arguments,return_type,documentation,..} => Self {
name,arguments,return_type,documentation,
module : module.try_into()?,
Expand Down

0 comments on commit 583af2d

Please sign in to comment.