Skip to content

Commit e5a0b99

Browse files
authored
Enable component instantiations (#315)
1 parent 16d9e5e commit e5a0b99

File tree

6 files changed

+108
-40
lines changed

6 files changed

+108
-40
lines changed

vhdl_lang/src/completion.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub enum CompletionItem<'a> {
4242
/// actual work library (using [CompletionItem::Simple] would complete the actual name
4343
/// of the library, not the string 'work'.
4444
Work,
45-
/// Entity instantiation, i.e.,
45+
/// Entity or component instantiation, i.e.,
4646
/// ```vhdl
4747
/// my_ent: entity work.foo
4848
/// generic map (
@@ -54,8 +54,11 @@ pub enum CompletionItem<'a> {
5454
/// ```
5555
///
5656
/// The second argument is a vector of architectures that are associated
57-
/// to this entity
58-
EntityInstantiation(EntRef<'a>, Vec<EntRef<'a>>),
57+
/// to the entity, if the first argument is an entity.
58+
///
59+
/// For a component instantiation, the first argument is a reference to the
60+
/// component. The second argument will always be empty.
61+
Instantiation(EntRef<'a>, Vec<EntRef<'a>>),
5962
/// Complete an attribute designator (i.e. `'range`, `'stable`, ...)
6063
Attribute(AttributeDesignator),
6164
}

vhdl_lang/src/completion/entity_instantiation.rs

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//
55
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
66
use crate::analysis::DesignRoot;
7+
use crate::completion::region::any_ent_to_completion_item;
78
use crate::named_entity::DesignEnt;
89
use crate::{AnyEntKind, CompletionItem, Design, EntRef, EntityId, HasEntityId};
910
use itertools::Itertools;
@@ -43,19 +44,15 @@ pub(crate) fn get_visible_entities_from_architecture<'a>(
4344
}
4445
entities
4546
.into_iter()
46-
.map(|eid| root.get_ent(eid))
47-
.map(|ent| match ent.kind() {
48-
AnyEntKind::Design(Design::Entity(..)) => {
49-
let architectures = get_architectures_for_entity(ent, root);
50-
CompletionItem::EntityInstantiation(ent, architectures)
51-
}
52-
_ => CompletionItem::Simple(ent),
53-
})
47+
.map(|eid| any_ent_to_completion_item(root.get_ent(eid), root))
5448
.collect_vec()
5549
}
5650

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

114-
assert!(options.contains(&CompletionItem::EntityInstantiation(my_ent, vec![])));
115-
assert!(options.contains(&CompletionItem::EntityInstantiation(my_other_ent, vec![])));
111+
assert!(options.contains(&CompletionItem::Instantiation(my_ent, vec![])));
112+
assert!(options.contains(&CompletionItem::Instantiation(my_other_ent, vec![])));
116113
}
117114

118115
#[test]
@@ -170,7 +167,7 @@ end arch;
170167
.search_reference(code2.source(), code2.s1("my_ent2").start())
171168
.unwrap();
172169

173-
assert!(options.contains(&CompletionItem::EntityInstantiation(my_ent2, vec![])));
170+
assert!(options.contains(&CompletionItem::Instantiation(my_ent2, vec![])));
174171

175172
let ent1 = root
176173
.search_reference(code1.source(), code1.s1("my_ent").start())
@@ -183,8 +180,8 @@ end arch;
183180
.search_reference(code3.source(), code3.s1("my_ent2").start())
184181
.unwrap();
185182

186-
assert!(options.contains(&CompletionItem::EntityInstantiation(my_ent2, vec![])));
187-
assert!(options.contains(&CompletionItem::EntityInstantiation(ent1, vec![])));
183+
assert!(options.contains(&CompletionItem::Instantiation(my_ent2, vec![])));
184+
assert!(options.contains(&CompletionItem::Instantiation(ent1, vec![])));
188185
}
189186

190187
#[test]
@@ -238,9 +235,7 @@ end arch;
238235
let applicable_options = options
239236
.into_iter()
240237
.filter_map(|option| match option {
241-
CompletionItem::EntityInstantiation(ent, architectures) => {
242-
Some((ent, architectures))
243-
}
238+
CompletionItem::Instantiation(ent, architectures) => Some((ent, architectures)),
244239
_ => None,
245240
})
246241
.collect_vec();
@@ -253,4 +248,44 @@ end arch;
253248
_ => panic!("Expected entity instantiation"),
254249
}
255250
}
251+
252+
#[test]
253+
fn component_instantiations() {
254+
let mut builder = LibraryBuilder::new();
255+
let code = builder.code(
256+
"libname",
257+
"\
258+
package foo is
259+
component comp_A is
260+
end component;
261+
end foo;
262+
263+
use work.foo.all;
264+
265+
entity my_ent is
266+
end my_ent;
267+
268+
architecture arch1 of my_ent is
269+
component comp_B is
270+
end component;
271+
272+
component comp_C is
273+
end component;
274+
begin
275+
end arch1;
276+
",
277+
);
278+
279+
let (root, diag) = builder.get_analyzed_root();
280+
check_no_diagnostics(&diag[..]);
281+
let cursor = code.s1("begin").end();
282+
let options = list_completion_options(&root, code.source(), cursor);
283+
284+
for component in ["comp_A", "comp_B", "comp_C"] {
285+
let entity = root
286+
.search_reference(code.source(), code.s1(component).start())
287+
.unwrap();
288+
assert!(options.contains(&CompletionItem::Instantiation(entity, vec![])))
289+
}
290+
}
256291
}

vhdl_lang/src/completion/generic.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,40 +106,45 @@ impl<'a> Searcher for CompletionSearcher<'a> {
106106
let Some(ent) = DesignEnt::from_any(self.root.get_ent(ent_id)) else {
107107
return Finished(Found);
108108
};
109-
self.completions.extend(visible_entities_from(ent.kind()));
109+
self.completions
110+
.extend(visible_entities_from(self.root, ent.kind()));
110111
Finished(Found)
111112
}
112113
}
113114

114-
fn visible_entities_from<'a>(design: &'a Design<'a>) -> Vec<CompletionItem<'a>> {
115+
fn visible_entities_from<'a>(
116+
root: &'a DesignRoot,
117+
design: &'a Design<'a>,
118+
) -> Vec<CompletionItem<'a>> {
115119
use Design::*;
116120
match design {
117121
Entity(visibility, region)
118122
| UninstPackage(visibility, region)
119123
| Architecture(visibility, region, _)
120124
| Package(visibility, region)
121125
| PackageBody(visibility, region) => chain(
122-
completion_items_from_region(region),
123-
completion_items_from_visibility(visibility),
126+
completion_items_from_region(root, region),
127+
completion_items_from_visibility(root, visibility),
124128
)
125129
.collect_vec(),
126130
PackageInstance(region) | InterfacePackageInstance(region) | Context(region) => {
127-
completion_items_from_region(region).collect_vec()
131+
completion_items_from_region(root, region).collect_vec()
128132
}
129133
Configuration => vec![],
130134
}
131135
}
132136

133137
fn completion_items_from_visibility<'a>(
138+
root: &'a DesignRoot,
134139
visibility: &'a Visibility<'a>,
135140
) -> impl Iterator<Item = CompletionItem<'a>> {
136141
visibility
137142
.visible()
138143
.unique()
139144
.map(CompletionItem::Simple)
140145
.chain(
141-
visibility
142-
.all_in_region()
143-
.flat_map(|visible_region| completion_items_from_region(visible_region.region())),
146+
visibility.all_in_region().flat_map(|visible_region| {
147+
completion_items_from_region(root, visible_region.region())
148+
}),
144149
)
145150
}

vhdl_lang/src/completion/region.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,45 @@
33
// You can obtain one at http://mozilla.org/MPL/2.0/.
44
//
55
// Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com
6+
7+
use crate::analysis::DesignRoot;
8+
use crate::completion::entity_instantiation::get_architectures_for_entity;
69
use crate::named_entity::{AsUnique, NamedEntities, Region};
7-
use crate::CompletionItem;
10+
use crate::{AnyEntKind, CompletionItem, Design};
11+
use vhdl_lang::EntRef;
812

913
pub(crate) fn completion_items_from_region<'a>(
14+
root: &'a DesignRoot,
1015
region: &'a Region<'a>,
1116
) -> impl Iterator<Item = CompletionItem<'a>> {
1217
region
1318
.entities
1419
.values()
15-
.map(named_entities_to_completion_item)
20+
.map(|entities| named_entities_to_completion_item(root, entities))
1621
}
1722

1823
fn named_entities_to_completion_item<'a>(
24+
root: &'a DesignRoot,
1925
named_entities: &'a NamedEntities<'a>,
2026
) -> CompletionItem<'a> {
2127
match named_entities {
22-
NamedEntities::Single(ent) => CompletionItem::Simple(ent),
28+
NamedEntities::Single(ent) => any_ent_to_completion_item(ent, root),
2329
NamedEntities::Overloaded(overloaded) => match overloaded.as_unique() {
2430
None => CompletionItem::Overloaded(overloaded.designator().clone(), overloaded.len()),
2531
Some(ent) => CompletionItem::Simple(ent),
2632
},
2733
}
2834
}
35+
36+
pub(crate) fn any_ent_to_completion_item<'a>(
37+
ent: EntRef<'a>,
38+
root: &'a DesignRoot,
39+
) -> CompletionItem<'a> {
40+
match ent.kind() {
41+
AnyEntKind::Design(Design::Entity(..)) | AnyEntKind::Component(_) => {
42+
let architectures = get_architectures_for_entity(ent, root);
43+
CompletionItem::Instantiation(ent, architectures)
44+
}
45+
_ => CompletionItem::Simple(ent),
46+
}
47+
}

vhdl_lang/src/completion/selected.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ pub(crate) fn completions_for_selected_name<'b>(
2020
) -> Vec<CompletionItem<'b>> {
2121
use crate::named_entity::AnyEntKind::*;
2222
match ent.kind() {
23-
Object(object) => completions_for_type(object.subtype.type_mark().kind()),
24-
Design(design) => completions_for_design(design),
23+
Object(object) => completions_for_type(root, object.subtype.type_mark().kind()),
24+
Design(design) => completions_for_design(root, design),
2525
Library => ent
2626
.library_name()
2727
.map(|sym| list_primaries_for_lib(root, sym))
@@ -31,31 +31,37 @@ pub(crate) fn completions_for_selected_name<'b>(
3131
}
3232

3333
/// Returns completions applicable when calling `foo.` where `foo` is amn object of some type.
34-
fn completions_for_type<'a>(typ: &'a named_entity::Type<'a>) -> Vec<CompletionItem<'a>> {
34+
fn completions_for_type<'a>(
35+
root: &'a DesignRoot,
36+
typ: &'a named_entity::Type<'a>,
37+
) -> Vec<CompletionItem<'a>> {
3538
use crate::named_entity::Type::*;
3639
match typ {
3740
Record(record_region) => record_region
3841
.iter()
3942
.map(|item| CompletionItem::Simple(item.ent))
4043
.collect(),
41-
Alias(type_ent) => completions_for_type(type_ent.kind()),
44+
Alias(type_ent) => completions_for_type(root, type_ent.kind()),
4245
Access(subtype) => {
43-
let mut completions = completions_for_type(subtype.type_mark().kind());
46+
let mut completions = completions_for_type(root, subtype.type_mark().kind());
4447
completions.push(CompletionItem::Keyword(All));
4548
completions
4649
}
47-
Protected(region, _) => completion_items_from_region(region).collect(),
50+
Protected(region, _) => completion_items_from_region(root, region).collect(),
4851
_ => vec![],
4952
}
5053
}
5154

5255
/// Returns completions applicable when calling `foo.` where `foo` is some design
5356
/// (i.e. entity or package).
54-
fn completions_for_design<'a>(design: &'a crate::Design<'a>) -> Vec<CompletionItem<'a>> {
57+
fn completions_for_design<'a>(
58+
root: &'a DesignRoot,
59+
design: &'a crate::Design<'a>,
60+
) -> Vec<CompletionItem<'a>> {
5561
use crate::named_entity::Design::*;
5662
match design {
5763
Package(_, region) | PackageInstance(region) | InterfacePackageInstance(region) => {
58-
completion_items_from_region(region)
64+
completion_items_from_region(root, region)
5965
.chain(once(CompletionItem::Keyword(All)))
6066
.collect()
6167
}

vhdl_ls/src/vhdl_server/completion.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl VHDLServer {
4646
kind: Some(CompletionItemKind::KEYWORD),
4747
..Default::default()
4848
},
49-
vhdl_lang::CompletionItem::EntityInstantiation(ent, architectures) => {
49+
vhdl_lang::CompletionItem::Instantiation(ent, architectures) => {
5050
let work_name = "work";
5151

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

0 commit comments

Comments
 (0)