Skip to content

Commit

Permalink
Enable component instantiations (#315)
Browse files Browse the repository at this point in the history
  • Loading branch information
Schottkyc137 committed Jun 30, 2024
1 parent 16d9e5e commit e5a0b99
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 40 deletions.
9 changes: 6 additions & 3 deletions vhdl_lang/src/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub enum CompletionItem<'a> {
/// actual work library (using [CompletionItem::Simple] would complete the actual name
/// of the library, not the string 'work'.
Work,
/// Entity instantiation, i.e.,
/// Entity or component instantiation, i.e.,
/// ```vhdl
/// my_ent: entity work.foo
/// generic map (
Expand All @@ -54,8 +54,11 @@ pub enum CompletionItem<'a> {
/// ```
///
/// The second argument is a vector of architectures that are associated
/// to this entity
EntityInstantiation(EntRef<'a>, Vec<EntRef<'a>>),
/// to the entity, if the first argument is an entity.
///
/// For a component instantiation, the first argument is a reference to the
/// component. The second argument will always be empty.
Instantiation(EntRef<'a>, Vec<EntRef<'a>>),
/// Complete an attribute designator (i.e. `'range`, `'stable`, ...)
Attribute(AttributeDesignator),
}
Expand Down
69 changes: 52 additions & 17 deletions vhdl_lang/src/completion/entity_instantiation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//
// Copyright (c) 2024, Olof Kraigher [email protected]
use crate::analysis::DesignRoot;
use crate::completion::region::any_ent_to_completion_item;
use crate::named_entity::DesignEnt;
use crate::{AnyEntKind, CompletionItem, Design, EntRef, EntityId, HasEntityId};
use itertools::Itertools;
Expand Down Expand Up @@ -43,19 +44,15 @@ pub(crate) fn get_visible_entities_from_architecture<'a>(
}
entities
.into_iter()
.map(|eid| root.get_ent(eid))
.map(|ent| match ent.kind() {
AnyEntKind::Design(Design::Entity(..)) => {
let architectures = get_architectures_for_entity(ent, root);
CompletionItem::EntityInstantiation(ent, architectures)
}
_ => CompletionItem::Simple(ent),
})
.map(|eid| any_ent_to_completion_item(root.get_ent(eid), root))
.collect_vec()
}

/// Returns a vec populated with all architectures that belong to a given entity
fn get_architectures_for_entity<'a>(ent: EntRef<'a>, root: &'a DesignRoot) -> Vec<EntRef<'a>> {
pub(crate) fn get_architectures_for_entity<'a>(
ent: EntRef<'a>,
root: &'a DesignRoot,
) -> Vec<EntRef<'a>> {
let Some(lib_symbol) = ent.library_name() else {
return vec![];
};
Expand Down Expand Up @@ -111,8 +108,8 @@ end arch;
.search_reference(code.source(), code.s1("my_other_ent").start())
.unwrap();

assert!(options.contains(&CompletionItem::EntityInstantiation(my_ent, vec![])));
assert!(options.contains(&CompletionItem::EntityInstantiation(my_other_ent, vec![])));
assert!(options.contains(&CompletionItem::Instantiation(my_ent, vec![])));
assert!(options.contains(&CompletionItem::Instantiation(my_other_ent, vec![])));
}

#[test]
Expand Down Expand Up @@ -170,7 +167,7 @@ end arch;
.search_reference(code2.source(), code2.s1("my_ent2").start())
.unwrap();

assert!(options.contains(&CompletionItem::EntityInstantiation(my_ent2, vec![])));
assert!(options.contains(&CompletionItem::Instantiation(my_ent2, vec![])));

let ent1 = root
.search_reference(code1.source(), code1.s1("my_ent").start())
Expand All @@ -183,8 +180,8 @@ end arch;
.search_reference(code3.source(), code3.s1("my_ent2").start())
.unwrap();

assert!(options.contains(&CompletionItem::EntityInstantiation(my_ent2, vec![])));
assert!(options.contains(&CompletionItem::EntityInstantiation(ent1, vec![])));
assert!(options.contains(&CompletionItem::Instantiation(my_ent2, vec![])));
assert!(options.contains(&CompletionItem::Instantiation(ent1, vec![])));
}

#[test]
Expand Down Expand Up @@ -238,9 +235,7 @@ end arch;
let applicable_options = options
.into_iter()
.filter_map(|option| match option {
CompletionItem::EntityInstantiation(ent, architectures) => {
Some((ent, architectures))
}
CompletionItem::Instantiation(ent, architectures) => Some((ent, architectures)),
_ => None,
})
.collect_vec();
Expand All @@ -253,4 +248,44 @@ end arch;
_ => panic!("Expected entity instantiation"),
}
}

#[test]
fn component_instantiations() {
let mut builder = LibraryBuilder::new();
let code = builder.code(
"libname",
"\
package foo is
component comp_A is
end component;
end foo;
use work.foo.all;
entity my_ent is
end my_ent;
architecture arch1 of my_ent is
component comp_B is
end component;
component comp_C is
end component;
begin
end arch1;
",
);

let (root, diag) = builder.get_analyzed_root();
check_no_diagnostics(&diag[..]);
let cursor = code.s1("begin").end();
let options = list_completion_options(&root, code.source(), cursor);

for component in ["comp_A", "comp_B", "comp_C"] {
let entity = root
.search_reference(code.source(), code.s1(component).start())
.unwrap();
assert!(options.contains(&CompletionItem::Instantiation(entity, vec![])))
}
}
}
21 changes: 13 additions & 8 deletions vhdl_lang/src/completion/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,40 +106,45 @@ impl<'a> Searcher for CompletionSearcher<'a> {
let Some(ent) = DesignEnt::from_any(self.root.get_ent(ent_id)) else {
return Finished(Found);
};
self.completions.extend(visible_entities_from(ent.kind()));
self.completions
.extend(visible_entities_from(self.root, ent.kind()));
Finished(Found)
}
}

fn visible_entities_from<'a>(design: &'a Design<'a>) -> Vec<CompletionItem<'a>> {
fn visible_entities_from<'a>(
root: &'a DesignRoot,
design: &'a Design<'a>,
) -> Vec<CompletionItem<'a>> {
use Design::*;
match design {
Entity(visibility, region)
| UninstPackage(visibility, region)
| Architecture(visibility, region, _)
| Package(visibility, region)
| PackageBody(visibility, region) => chain(
completion_items_from_region(region),
completion_items_from_visibility(visibility),
completion_items_from_region(root, region),
completion_items_from_visibility(root, visibility),
)
.collect_vec(),
PackageInstance(region) | InterfacePackageInstance(region) | Context(region) => {
completion_items_from_region(region).collect_vec()
completion_items_from_region(root, region).collect_vec()
}
Configuration => vec![],
}
}

fn completion_items_from_visibility<'a>(
root: &'a DesignRoot,
visibility: &'a Visibility<'a>,
) -> impl Iterator<Item = CompletionItem<'a>> {
visibility
.visible()
.unique()
.map(CompletionItem::Simple)
.chain(
visibility
.all_in_region()
.flat_map(|visible_region| completion_items_from_region(visible_region.region())),
visibility.all_in_region().flat_map(|visible_region| {
completion_items_from_region(root, visible_region.region())
}),
)
}
25 changes: 22 additions & 3 deletions vhdl_lang/src/completion/region.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,45 @@
// You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2024, Olof Kraigher [email protected]

use crate::analysis::DesignRoot;
use crate::completion::entity_instantiation::get_architectures_for_entity;
use crate::named_entity::{AsUnique, NamedEntities, Region};
use crate::CompletionItem;
use crate::{AnyEntKind, CompletionItem, Design};
use vhdl_lang::EntRef;

pub(crate) fn completion_items_from_region<'a>(
root: &'a DesignRoot,
region: &'a Region<'a>,
) -> impl Iterator<Item = CompletionItem<'a>> {
region
.entities
.values()
.map(named_entities_to_completion_item)
.map(|entities| named_entities_to_completion_item(root, entities))
}

fn named_entities_to_completion_item<'a>(
root: &'a DesignRoot,
named_entities: &'a NamedEntities<'a>,
) -> CompletionItem<'a> {
match named_entities {
NamedEntities::Single(ent) => CompletionItem::Simple(ent),
NamedEntities::Single(ent) => any_ent_to_completion_item(ent, root),
NamedEntities::Overloaded(overloaded) => match overloaded.as_unique() {
None => CompletionItem::Overloaded(overloaded.designator().clone(), overloaded.len()),
Some(ent) => CompletionItem::Simple(ent),
},
}
}

pub(crate) fn any_ent_to_completion_item<'a>(
ent: EntRef<'a>,
root: &'a DesignRoot,
) -> CompletionItem<'a> {
match ent.kind() {
AnyEntKind::Design(Design::Entity(..)) | AnyEntKind::Component(_) => {
let architectures = get_architectures_for_entity(ent, root);
CompletionItem::Instantiation(ent, architectures)
}
_ => CompletionItem::Simple(ent),
}
}
22 changes: 14 additions & 8 deletions vhdl_lang/src/completion/selected.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ pub(crate) fn completions_for_selected_name<'b>(
) -> Vec<CompletionItem<'b>> {
use crate::named_entity::AnyEntKind::*;
match ent.kind() {
Object(object) => completions_for_type(object.subtype.type_mark().kind()),
Design(design) => completions_for_design(design),
Object(object) => completions_for_type(root, object.subtype.type_mark().kind()),
Design(design) => completions_for_design(root, design),
Library => ent
.library_name()
.map(|sym| list_primaries_for_lib(root, sym))
Expand All @@ -31,31 +31,37 @@ pub(crate) fn completions_for_selected_name<'b>(
}

/// Returns completions applicable when calling `foo.` where `foo` is amn object of some type.
fn completions_for_type<'a>(typ: &'a named_entity::Type<'a>) -> Vec<CompletionItem<'a>> {
fn completions_for_type<'a>(
root: &'a DesignRoot,
typ: &'a named_entity::Type<'a>,
) -> Vec<CompletionItem<'a>> {
use crate::named_entity::Type::*;
match typ {
Record(record_region) => record_region
.iter()
.map(|item| CompletionItem::Simple(item.ent))
.collect(),
Alias(type_ent) => completions_for_type(type_ent.kind()),
Alias(type_ent) => completions_for_type(root, type_ent.kind()),
Access(subtype) => {
let mut completions = completions_for_type(subtype.type_mark().kind());
let mut completions = completions_for_type(root, subtype.type_mark().kind());
completions.push(CompletionItem::Keyword(All));
completions
}
Protected(region, _) => completion_items_from_region(region).collect(),
Protected(region, _) => completion_items_from_region(root, region).collect(),
_ => vec![],
}
}

/// Returns completions applicable when calling `foo.` where `foo` is some design
/// (i.e. entity or package).
fn completions_for_design<'a>(design: &'a crate::Design<'a>) -> Vec<CompletionItem<'a>> {
fn completions_for_design<'a>(
root: &'a DesignRoot,
design: &'a crate::Design<'a>,
) -> Vec<CompletionItem<'a>> {
use crate::named_entity::Design::*;
match design {
Package(_, region) | PackageInstance(region) | InterfacePackageInstance(region) => {
completion_items_from_region(region)
completion_items_from_region(root, region)
.chain(once(CompletionItem::Keyword(All)))
.collect()
}
Expand Down
2 changes: 1 addition & 1 deletion vhdl_ls/src/vhdl_server/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl VHDLServer {
kind: Some(CompletionItemKind::KEYWORD),
..Default::default()
},
vhdl_lang::CompletionItem::EntityInstantiation(ent, architectures) => {
vhdl_lang::CompletionItem::Instantiation(ent, architectures) => {
let work_name = "work";

let library_names = if let Some(lib_name) = ent.library_name() {
Expand Down

0 comments on commit e5a0b99

Please sign in to comment.