Skip to content

Commit f91dd49

Browse files
authored
Add signal and port declarations to the list of completion items (#293)
1 parent 3d1db16 commit f91dd49

File tree

2 files changed

+138
-35
lines changed

2 files changed

+138
-35
lines changed

vhdl_lang/src/completion.rs

Lines changed: 136 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use crate::named_entity::{self, AsUnique, DesignEnt, HasEntityId, NamedEntities,
1414
use crate::syntax::Kind::*;
1515
use crate::syntax::{Kind, Symbols, Token, TokenAccess, Tokenizer, Value};
1616
use crate::{AnyEntKind, Design, EntRef, EntityId, HasTokenSpan, Overloaded, Position, Source};
17-
use itertools::Itertools;
1817
use std::collections::HashSet;
1918
use std::default::Default;
2019
use std::iter::once;
@@ -350,12 +349,46 @@ impl<'a> Searcher for CompletionSearcher<'a> {
350349
fn search_decl(&mut self, ctx: &dyn TokenAccess, decl: FoundDeclaration) -> SearchState {
351350
match decl {
352351
FoundDeclaration::Architecture(body) => {
352+
// ensure we are in the concurrent region
353+
if !ctx
354+
.get_span(body.begin_token, body.get_end_token())
355+
.contains(self.cursor)
356+
{
357+
return Finished(NotFound);
358+
}
359+
// Add architecture declarations to the list of completed names
360+
self.completions.extend(
361+
body.decl
362+
.iter()
363+
.filter_map(|decl| decl.ent_id())
364+
.map(|eid| self.root.get_ent(eid))
365+
.filter(|ent| {
366+
matches!(
367+
ent.kind(),
368+
AnyEntKind::Object(_) | AnyEntKind::ObjectAlias { .. }
369+
)
370+
})
371+
.map(CompletionItem::Simple),
372+
);
373+
353374
let Some(eid) = body.entity_name.reference.get() else {
354375
return Finished(NotFound);
355376
};
356377
let Some(ent) = DesignEnt::from_any(self.root.get_ent(eid)) else {
357378
return Finished(NotFound);
358379
};
380+
// Add ports and generics to the list of completed items
381+
if let Design::Entity(_, region) = ent.kind() {
382+
self.completions
383+
.extend(region.entities.values().filter_map(|ent| {
384+
if let NamedEntities::Single(ent) = ent {
385+
Some(CompletionItem::Simple(ent))
386+
} else {
387+
None
388+
}
389+
}))
390+
}
391+
// Early-exit for when we are inside a statement.
359392
for statement in &body.statements {
360393
let pos = &statement.statement.pos;
361394

@@ -368,45 +401,45 @@ impl<'a> Searcher for CompletionSearcher<'a> {
368401
return Finished(NotFound);
369402
}
370403
}
371-
if ctx
372-
.get_span(body.begin_token, body.get_end_token())
373-
.contains(self.cursor)
374-
{
375-
self.completions = self
376-
.root
404+
self.completions.extend(
405+
self.root
377406
.get_visible_entities_from_entity(ent)
378-
.map(|eid| {
379-
let ent = self.root.get_ent(eid);
380-
let architectures = get_architectures_for_entity(ent, self.root);
381-
CompletionItem::EntityInstantiation(
382-
self.root.get_ent(eid),
383-
architectures,
384-
)
385-
})
386-
.collect()
387-
}
407+
.map(|eid| entity_to_completion_item(self.root, eid)),
408+
);
388409
Finished(Found)
389410
}
390411
_ => NotFinished,
391412
}
392413
}
393414
}
394415

416+
fn entity_to_completion_item(root: &DesignRoot, eid: EntityId) -> CompletionItem {
417+
let ent = root.get_ent(eid);
418+
match ent.kind() {
419+
AnyEntKind::Design(Design::Entity(..)) => {
420+
let architectures = get_architectures_for_entity(ent, root);
421+
CompletionItem::EntityInstantiation(ent, architectures)
422+
}
423+
_ => CompletionItem::Simple(ent),
424+
}
425+
}
426+
395427
/// Returns a vec populated with all architectures that belong to the given entity
396428
fn get_architectures_for_entity<'a>(ent: EntRef<'a>, root: &'a DesignRoot) -> Vec<EntRef<'a>> {
397-
if let Some(design) = DesignEnt::from_any(ent) {
398-
root.public_symbols()
399-
.filter(|sym| match sym.kind() {
400-
AnyEntKind::Design(Design::Architecture(arch_design)) => {
401-
arch_design.id() == design.id()
402-
}
403-
_ => false,
404-
})
405-
.sorted_by_key(|a| a.decl_pos())
406-
.collect_vec()
407-
} else {
408-
vec![]
409-
}
429+
let Some(lib_symbol) = ent.library_name() else {
430+
return vec![];
431+
};
432+
let Some(lib) = root.get_lib(lib_symbol) else {
433+
return vec![];
434+
};
435+
let Some(sym) = ent.designator().as_identifier() else {
436+
return vec![];
437+
};
438+
lib.secondary_units(sym)
439+
.filter_map(|locked_unit| locked_unit.unit.get())
440+
.filter_map(|read_guard| read_guard.ent_id())
441+
.map(|eid| root.get_ent(eid))
442+
.collect()
410443
}
411444

412445
impl DesignRoot {
@@ -644,6 +677,20 @@ mod test {
644677
.search_reference(code.source(), code.s1("dout").start())
645678
.unwrap();
646679

680+
let clk_signal = root
681+
.search_reference(
682+
code.source(),
683+
code.s1("signal clk, rst: bit;").s1("clk").start(),
684+
)
685+
.unwrap();
686+
687+
let rst_signal = root
688+
.search_reference(
689+
code.source(),
690+
code.s1("signal clk, rst: bit;").s1("rst").start(),
691+
)
692+
.unwrap();
693+
647694
let cursor = code.s1("port map (").pos().end();
648695
let options = list_completion_options(&root, code.source(), cursor);
649696
assert!(options.contains(&CompletionItem::Formal(rst)));
@@ -655,14 +702,26 @@ mod test {
655702
.pos()
656703
.end();
657704
let options = list_completion_options(&root, code.source(), cursor);
658-
assert_eq!(options.len(), 0);
705+
assert_eq_unordered(
706+
&options,
707+
&[
708+
CompletionItem::Simple(clk_signal),
709+
CompletionItem::Simple(rst_signal),
710+
],
711+
);
659712
let cursor = code
660713
.s1("port map (
661714
clk => c")
662715
.pos()
663716
.end();
664717
let options = list_completion_options(&root, code.source(), cursor);
665-
assert_eq!(options.len(), 0);
718+
assert_eq_unordered(
719+
&options,
720+
&[
721+
CompletionItem::Simple(clk_signal),
722+
CompletionItem::Simple(rst_signal),
723+
],
724+
);
666725
}
667726

668727
#[test]
@@ -874,9 +933,51 @@ end arch;
874933
.search_reference(code1.source(), code1.s1("arch2").start())
875934
.unwrap();
876935

877-
assert_eq!(
878-
options,
879-
vec![CompletionItem::EntityInstantiation(ent, vec![arch1, arch2])]
936+
match &options[..] {
937+
[CompletionItem::EntityInstantiation(got_ent, architectures)] => {
938+
assert_eq!(*got_ent, ent);
939+
assert_eq_unordered(architectures, &[arch1, arch2]);
940+
}
941+
_ => panic!("Expected entity instantiation"),
942+
}
943+
}
944+
945+
#[test]
946+
pub fn completes_signals_and_ports() {
947+
let mut builder = LibraryBuilder::new();
948+
let code = builder.code(
949+
"libA",
950+
"\
951+
entity my_ent is
952+
port (
953+
foo : in bit
954+
);
955+
end my_ent;
956+
957+
architecture arch of my_ent is
958+
signal bar : natural;
959+
type foobaz is array(natural range <>) of bit;
960+
begin
961+
end arch;
962+
",
963+
);
964+
965+
let (root, diag) = builder.get_analyzed_root();
966+
check_no_diagnostics(&diag);
967+
let cursor = code.s1("begin").end();
968+
let options = list_completion_options(&root, code.source(), cursor);
969+
970+
let ent1 = root
971+
.search_reference(code.source(), code.s1("foo").start())
972+
.unwrap();
973+
974+
let ent2 = root
975+
.search_reference(code.source(), code.s1("bar").start())
976+
.unwrap();
977+
978+
assert_eq_unordered(
979+
&options,
980+
&[CompletionItem::Simple(ent1), CompletionItem::Simple(ent2)],
880981
)
881982
}
882983
}

vhdl_lang/src/named_entity/design.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ use crate::SrcPos;
1717

1818
pub enum Design<'a> {
1919
Entity(Visibility<'a>, Region<'a>),
20+
/// A VHDL architecture.
21+
/// The linked `DesignEnt` is the entity that belongs to the architecture.
2022
Architecture(DesignEnt<'a>),
2123
Configuration,
2224
Package(Visibility<'a>, Region<'a>),

0 commit comments

Comments
 (0)