mirror of
https://github.com/enso-org/enso.git
synced 2024-12-25 03:43:41 +03:00
Avoid relying on order of updates for hierarchy index (#4094)
[Task](https://www.pivotaltracker.com/story/show/184279344). This PR fixes the hierarchy index of the suggestion database and removes a bunch of warnings from the console: ![image](https://user-images.githubusercontent.com/6566674/215051255-6cb22349-1e72-4717-96c0-66db9bf2020f.png) Now we also correctly display documentation for all types. Previously some methods, namely for the `Text` type, were not present in the documentation. No visual changes to IDE were made.
This commit is contained in:
parent
9002df986e
commit
945aa2b2d7
@ -161,7 +161,15 @@ impl QualifiedNameToIdMap {
|
|||||||
/// displays all Constructors and Methods defined for this type.
|
/// displays all Constructors and Methods defined for this type.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct HierarchyIndex {
|
struct HierarchyIndex {
|
||||||
inner: HashMap<entry::Id, HashSet<entry::Id>>,
|
inner: HashMap<entry::Id, HashSet<entry::Id>>,
|
||||||
|
/// Orphans are entries that are not yet in the index, but are referenced by other entries.
|
||||||
|
/// For example, if we have a Type entry, but we haven't yet received its Module entry, we
|
||||||
|
/// store the Type entry as an orphan of Module entry. When we receive the Module entry, we
|
||||||
|
/// add all orphans to the index. This way we do not rely on the order of updates from the
|
||||||
|
/// Engine.
|
||||||
|
///
|
||||||
|
/// Key is the parent entry name, value is a set of orphan entries.
|
||||||
|
orphans: HashMap<QualifiedName, HashSet<entry::Id>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HierarchyIndex {
|
impl HierarchyIndex {
|
||||||
@ -179,7 +187,7 @@ impl HierarchyIndex {
|
|||||||
if let Some(self_type_id) = qualified_name_to_id_map.get(self_type) {
|
if let Some(self_type_id) = qualified_name_to_id_map.get(self_type) {
|
||||||
self.inner.entry(self_type_id).or_default().insert(id);
|
self.inner.entry(self_type_id).or_default().insert(id);
|
||||||
} else {
|
} else {
|
||||||
warn!("Could not find an id for self type {self_type}.");
|
self.orphans.entry(self_type.clone()).or_default().insert(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if entry.kind == Kind::Type {
|
if entry.kind == Kind::Type {
|
||||||
@ -187,13 +195,17 @@ impl HierarchyIndex {
|
|||||||
if let Some(parent_id) = qualified_name_to_id_map.get(&parent_module) {
|
if let Some(parent_id) = qualified_name_to_id_map.get(&parent_module) {
|
||||||
self.inner.entry(parent_id).or_default().insert(id);
|
self.inner.entry(parent_id).or_default().insert(id);
|
||||||
} else {
|
} else {
|
||||||
warn!("Could not find an id for parent module {parent_module}.");
|
let parent_module_name = parent_module.to_owned();
|
||||||
|
self.orphans.entry(parent_module_name).or_default().insert(id);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let entry_name = &entry.name;
|
let entry_name = &entry.name;
|
||||||
warn!("Could not find a parent module for type {entry_name}.");
|
warn!("Could not find a parent module for type {entry_name}.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(orphans) = self.orphans.remove(&entry.qualified_name()) {
|
||||||
|
self.inner.entry(id).or_default().extend(orphans);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove the entry from the index, as it is removed from the database.
|
/// Remove the entry from the index, as it is removed from the database.
|
||||||
@ -1364,4 +1376,98 @@ pub mod test {
|
|||||||
"Standard.NewModule.Number",
|
"Standard.NewModule.Number",
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test that the hierarchy index does not depend on the order of received updates. For example,
|
||||||
|
/// we can receive a method entry before receiving a self type entry.
|
||||||
|
#[test]
|
||||||
|
fn hierarchy_index_does_not_depend_on_order_of_updates() {
|
||||||
|
// === Add a new method, then add a self type for it ===
|
||||||
|
|
||||||
|
let db = mock::standard_db_mock();
|
||||||
|
let update = SuggestionDatabaseUpdatesEvent {
|
||||||
|
updates: vec![entry::Update::Add {
|
||||||
|
id: 20,
|
||||||
|
suggestion: Box::new(SuggestionEntry::Method {
|
||||||
|
external_id: None,
|
||||||
|
name: "new_method".to_string(),
|
||||||
|
module: "Standard.Base".to_string(),
|
||||||
|
arguments: vec![],
|
||||||
|
self_type: "Standard.Base.NewType".to_string(),
|
||||||
|
return_type: "Standard.Base.Maybe".to_string(),
|
||||||
|
is_static: false,
|
||||||
|
reexport: None,
|
||||||
|
documentation: None,
|
||||||
|
documentation_html: None,
|
||||||
|
documentation_sections: vec![],
|
||||||
|
}),
|
||||||
|
}],
|
||||||
|
current_version: 1,
|
||||||
|
};
|
||||||
|
db.apply_update_event(update);
|
||||||
|
assert_eq!(db.hierarchy_index.borrow().len(), 4);
|
||||||
|
let update = SuggestionDatabaseUpdatesEvent {
|
||||||
|
updates: vec![entry::Update::Add {
|
||||||
|
id: 21,
|
||||||
|
suggestion: Box::new(SuggestionEntry::Type {
|
||||||
|
external_id: None,
|
||||||
|
name: "NewType".to_string(),
|
||||||
|
module: "Standard.Base".to_string(),
|
||||||
|
params: vec![],
|
||||||
|
parent_type: None,
|
||||||
|
reexport: None,
|
||||||
|
documentation: None,
|
||||||
|
documentation_html: None,
|
||||||
|
documentation_sections: vec![],
|
||||||
|
}),
|
||||||
|
}],
|
||||||
|
current_version: 2,
|
||||||
|
};
|
||||||
|
db.apply_update_event(update);
|
||||||
|
assert_eq!(db.hierarchy_index.borrow().len(), 5);
|
||||||
|
let new_type = lookup_id_by_name(&db, "Standard.Base.NewType").unwrap();
|
||||||
|
let new_method = lookup_id_by_name(&db, "Standard.Base.NewType.new_method").unwrap();
|
||||||
|
assert_eq!(db.lookup_hierarchy(new_type).unwrap(), HashSet::from([new_method]));
|
||||||
|
|
||||||
|
|
||||||
|
// === Add a new type, then add a parent module for it ===
|
||||||
|
|
||||||
|
let db = mock::standard_db_mock();
|
||||||
|
let update = SuggestionDatabaseUpdatesEvent {
|
||||||
|
updates: vec![entry::Update::Add {
|
||||||
|
id: 20,
|
||||||
|
suggestion: Box::new(SuggestionEntry::Type {
|
||||||
|
external_id: None,
|
||||||
|
name: "NewType".to_string(),
|
||||||
|
module: "Standard.NewModule".to_string(),
|
||||||
|
params: vec![],
|
||||||
|
reexport: None,
|
||||||
|
documentation: None,
|
||||||
|
documentation_html: None,
|
||||||
|
documentation_sections: vec![],
|
||||||
|
parent_type: None,
|
||||||
|
}),
|
||||||
|
}],
|
||||||
|
current_version: 1,
|
||||||
|
};
|
||||||
|
db.apply_update_event(update);
|
||||||
|
assert_eq!(db.hierarchy_index.borrow().len(), 4);
|
||||||
|
let update = SuggestionDatabaseUpdatesEvent {
|
||||||
|
updates: vec![entry::Update::Add {
|
||||||
|
id: 21,
|
||||||
|
suggestion: Box::new(SuggestionEntry::Module {
|
||||||
|
module: "Standard.NewModule".to_string(),
|
||||||
|
reexport: None,
|
||||||
|
documentation: None,
|
||||||
|
documentation_html: None,
|
||||||
|
documentation_sections: vec![],
|
||||||
|
}),
|
||||||
|
}],
|
||||||
|
current_version: 2,
|
||||||
|
};
|
||||||
|
db.apply_update_event(update);
|
||||||
|
assert_eq!(db.hierarchy_index.borrow().len(), 5);
|
||||||
|
let new_module = lookup_id_by_name(&db, "Standard.NewModule").unwrap();
|
||||||
|
let new_type = lookup_id_by_name(&db, "Standard.NewModule.NewType").unwrap();
|
||||||
|
assert_eq!(db.lookup_hierarchy(new_module).unwrap(), HashSet::from([new_type]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user