diff --git a/vhdl_lang/src/completion.rs b/vhdl_lang/src/completion.rs index 2582899d..2106805a 100644 --- a/vhdl_lang/src/completion.rs +++ b/vhdl_lang/src/completion.rs @@ -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 ( @@ -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>), + /// 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>), /// Complete an attribute designator (i.e. `'range`, `'stable`, ...) Attribute(AttributeDesignator), } diff --git a/vhdl_lang/src/completion/entity_instantiation.rs b/vhdl_lang/src/completion/entity_instantiation.rs index 1497de5f..d606adaa 100644 --- a/vhdl_lang/src/completion/entity_instantiation.rs +++ b/vhdl_lang/src/completion/entity_instantiation.rs @@ -4,6 +4,7 @@ // // Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com 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; @@ -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> { +pub(crate) fn get_architectures_for_entity<'a>( + ent: EntRef<'a>, + root: &'a DesignRoot, +) -> Vec> { let Some(lib_symbol) = ent.library_name() else { return vec![]; }; @@ -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] @@ -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()) @@ -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] @@ -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(); @@ -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![]))) + } + } } diff --git a/vhdl_lang/src/completion/generic.rs b/vhdl_lang/src/completion/generic.rs index d2cb9f2e..e3cc21bb 100644 --- a/vhdl_lang/src/completion/generic.rs +++ b/vhdl_lang/src/completion/generic.rs @@ -106,12 +106,16 @@ 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> { +fn visible_entities_from<'a>( + root: &'a DesignRoot, + design: &'a Design<'a>, +) -> Vec> { use Design::*; match design { Entity(visibility, region) @@ -119,18 +123,19 @@ fn visible_entities_from<'a>(design: &'a Design<'a>) -> Vec> | 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> { visibility @@ -138,8 +143,8 @@ fn completion_items_from_visibility<'a>( .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()) + }), ) } diff --git a/vhdl_lang/src/completion/region.rs b/vhdl_lang/src/completion/region.rs index c2627475..0cf82fa4 100644 --- a/vhdl_lang/src/completion/region.rs +++ b/vhdl_lang/src/completion/region.rs @@ -1,28 +1,46 @@ +use vhdl_lang::EntRef; // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this file, // You can obtain one at http://mozilla.org/MPL/2.0/. // // Copyright (c) 2024, Olof Kraigher olof.kraigher@gmail.com +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}; pub(crate) fn completion_items_from_region<'a>( + root: &'a DesignRoot, region: &'a Region<'a>, ) -> impl Iterator> { 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), + } +} diff --git a/vhdl_lang/src/completion/selected.rs b/vhdl_lang/src/completion/selected.rs index bc4d6ae8..63ba4108 100644 --- a/vhdl_lang/src/completion/selected.rs +++ b/vhdl_lang/src/completion/selected.rs @@ -20,8 +20,8 @@ pub(crate) fn completions_for_selected_name<'b>( ) -> Vec> { 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)) @@ -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> { +fn completions_for_type<'a>( + root: &'a DesignRoot, + typ: &'a named_entity::Type<'a>, +) -> Vec> { 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> { +fn completions_for_design<'a>( + root: &'a DesignRoot, + design: &'a crate::Design<'a>, +) -> Vec> { 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() } diff --git a/vhdl_ls/src/vhdl_server/completion.rs b/vhdl_ls/src/vhdl_server/completion.rs index 29ee65a5..36445623 100644 --- a/vhdl_ls/src/vhdl_server/completion.rs +++ b/vhdl_ls/src/vhdl_server/completion.rs @@ -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() {