mirror of
https://github.com/VHDL-LS/rust_hdl.git
synced 2025-12-23 06:01:10 +00:00
More elaborate completions (#205)
* Implement more complex completion item * Add more completion kinds * Add support for snippets * clippy * remove non rendered text * Add primitive overload resolution * clippy * improve overloaded format * Re-add detail to standard completion item * Make label and insertion text different * fmt * fix tests * Remove items method and make local arena private again * Refactor completion * Add 'all' case * Implement changes in server * remove obsolete change * Refactor to AnyEnt instead of ID * Remove obsolete get_end function * Fix unpredictable text order * Fix: All should use the Completion item kind 'keyword' * Reduce code duplication * rustify PortsOrGenericsExtractor * Remove unused function * Refactor for any keyword and use function instead of text * Conditional snippet support * Don't complete when the last token is not a left par or comma * Also complete with an identifier on the right hand side * Refactor: standalone functions from methods * Remove serde dependency from vhdl_lang * clippy * symmetry in docstring
This commit is contained in:
parent
4732a6ce18
commit
c84e06b410
10 changed files with 591 additions and 182 deletions
|
|
@ -35,6 +35,7 @@ mod completion;
|
|||
mod tests;
|
||||
|
||||
pub use self::root::{DesignRoot, EntHierarchy};
|
||||
pub use completion::CompletionItem;
|
||||
pub use named_entity::{
|
||||
AnyEnt, AnyEntKind, Concurrent, Design, EntRef, EntityId, HasEntityId, Object, Overloaded,
|
||||
Related, Sequential, Type,
|
||||
|
|
|
|||
|
|
@ -1,17 +1,33 @@
|
|||
use crate::analysis::DesignRoot;
|
||||
use crate::analysis::region::{AsUnique, NamedEntities};
|
||||
use crate::analysis::{DesignRoot, HasEntityId};
|
||||
use crate::ast::visitor::{Visitor, VisitorResult};
|
||||
use crate::ast::{
|
||||
AnyDesignUnit, AnyPrimaryUnit, AnySecondaryUnit, ComponentDeclaration, Declaration,
|
||||
AnyDesignUnit, AnyPrimaryUnit, AnySecondaryUnit, ComponentDeclaration, Designator,
|
||||
EntityDeclaration, InstantiationStatement, InterfaceDeclaration, MapAspect, PackageDeclaration,
|
||||
SubprogramDeclaration, UnitKey,
|
||||
};
|
||||
use crate::data::{ContentReader, Symbol};
|
||||
use crate::syntax::Kind::*;
|
||||
use crate::syntax::{Symbols, Token, TokenAccess, Tokenizer, Value};
|
||||
use crate::{EntityId, Position, Source};
|
||||
use itertools::Itertools;
|
||||
use crate::syntax::{Kind, Symbols, Token, TokenAccess, Tokenizer, Value};
|
||||
use crate::AnyEntKind::Design;
|
||||
use crate::{EntRef, EntityId, Position, Source};
|
||||
use std::collections::HashSet;
|
||||
use std::default::Default;
|
||||
use std::iter::once;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum CompletionItem<'a> {
|
||||
/// Simply complete the entities
|
||||
/// e.g., `use std.` should simply list all elements in the std library
|
||||
Simple(EntRef<'a>),
|
||||
/// Formal parameter, e.g., in a port map
|
||||
/// `port map (` might choose to complete `<item> => $1`
|
||||
Formal(EntRef<'a>),
|
||||
/// Multiple overloaded items are applicable.
|
||||
/// The argument is the count of overloaded items in total.
|
||||
Overloaded(Designator, usize),
|
||||
/// Complete a keyword
|
||||
Keyword(Kind),
|
||||
}
|
||||
|
||||
macro_rules! kind {
|
||||
($kind: pat) => {
|
||||
|
|
@ -39,25 +55,45 @@ enum MapAspectKind {
|
|||
/// The entity can be an `Entity`, `Component` or `Package`.
|
||||
/// After walking the AST, the ports or generics are written to the `items` vector.
|
||||
/// The `kind` member chooses whether to select ports or generics.
|
||||
struct PortsOrGenericsExtractor<'a> {
|
||||
struct PortsOrGenericsExtractor {
|
||||
id: EntityId,
|
||||
items: &'a mut Vec<String>,
|
||||
items: Vec<EntityId>,
|
||||
kind: MapAspectKind,
|
||||
}
|
||||
|
||||
impl DesignRoot {
|
||||
fn extract_port_or_generic_names(
|
||||
&self,
|
||||
id: EntityId,
|
||||
items: &mut Vec<String>,
|
||||
kind: MapAspectKind,
|
||||
) {
|
||||
let mut searcher = PortsOrGenericsExtractor { id, items, kind };
|
||||
fn extract_port_or_generic_names(&self, id: EntityId, kind: MapAspectKind) -> Vec<EntityId> {
|
||||
let mut searcher = PortsOrGenericsExtractor::new(id, kind);
|
||||
self.walk(&mut searcher);
|
||||
searcher.items
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visitor for PortsOrGenericsExtractor<'a> {
|
||||
impl PortsOrGenericsExtractor {
|
||||
pub fn new(id: EntityId, kind: MapAspectKind) -> PortsOrGenericsExtractor {
|
||||
PortsOrGenericsExtractor {
|
||||
id,
|
||||
items: vec![],
|
||||
kind,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_map_aspect_items(&mut self, map_aspect: &Vec<InterfaceDeclaration>) {
|
||||
for decl in map_aspect {
|
||||
if let Some(id) = decl.ent_id() {
|
||||
self.items.push(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_optional_map_aspect_items(&mut self, map_aspect: &Option<Vec<InterfaceDeclaration>>) {
|
||||
if let Some(map_aspect) = map_aspect {
|
||||
self.add_map_aspect_items(map_aspect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Visitor for PortsOrGenericsExtractor {
|
||||
fn visit_component_declaration(
|
||||
&mut self,
|
||||
node: &ComponentDeclaration,
|
||||
|
|
@ -67,14 +103,10 @@ impl<'a> Visitor for PortsOrGenericsExtractor<'a> {
|
|||
return VisitorResult::Skip;
|
||||
}
|
||||
if self.kind == MapAspectKind::Port {
|
||||
for port in &node.port_list {
|
||||
self.items.push(port.completable_name())
|
||||
}
|
||||
self.add_map_aspect_items(&node.port_list);
|
||||
}
|
||||
if self.kind == MapAspectKind::Generic {
|
||||
for generic in &node.generic_list {
|
||||
self.items.push(generic.completable_name())
|
||||
}
|
||||
self.add_map_aspect_items(&node.generic_list);
|
||||
}
|
||||
VisitorResult::Stop
|
||||
}
|
||||
|
|
@ -88,18 +120,10 @@ impl<'a> Visitor for PortsOrGenericsExtractor<'a> {
|
|||
return VisitorResult::Skip;
|
||||
}
|
||||
if self.kind == MapAspectKind::Port {
|
||||
if let Some(ports) = &node.port_clause {
|
||||
for port in ports {
|
||||
self.items.push(port.completable_name())
|
||||
}
|
||||
}
|
||||
self.add_optional_map_aspect_items(&node.port_clause);
|
||||
}
|
||||
if self.kind == MapAspectKind::Generic {
|
||||
if let Some(generics) = &node.generic_clause {
|
||||
for generic in generics {
|
||||
self.items.push(generic.completable_name())
|
||||
}
|
||||
}
|
||||
self.add_optional_map_aspect_items(&node.generic_clause);
|
||||
}
|
||||
VisitorResult::Stop
|
||||
}
|
||||
|
|
@ -113,42 +137,34 @@ impl<'a> Visitor for PortsOrGenericsExtractor<'a> {
|
|||
return VisitorResult::Skip;
|
||||
}
|
||||
if self.kind == MapAspectKind::Generic {
|
||||
if let Some(generics) = &node.generic_clause {
|
||||
for generic in generics {
|
||||
self.items.push(generic.completable_name())
|
||||
}
|
||||
}
|
||||
self.add_optional_map_aspect_items(&node.generic_clause);
|
||||
}
|
||||
VisitorResult::Stop
|
||||
}
|
||||
}
|
||||
|
||||
impl InterfaceDeclaration {
|
||||
/// Returns completable names for an interface declarations.
|
||||
/// Example:
|
||||
/// `signal my_signal : natural := 5` => `my_signal`
|
||||
fn completable_name(&self) -> String {
|
||||
match self {
|
||||
InterfaceDeclaration::Object(obj) => obj.ident.tree.to_string(),
|
||||
InterfaceDeclaration::File(file) => file.ident.to_string(),
|
||||
InterfaceDeclaration::Type(typ) => typ.tree.item.name().to_string(),
|
||||
InterfaceDeclaration::Subprogram(decl, _) => match decl {
|
||||
SubprogramDeclaration::Procedure(proc) => proc.designator.to_string(),
|
||||
SubprogramDeclaration::Function(func) => func.designator.to_string(),
|
||||
},
|
||||
InterfaceDeclaration::Package(package) => package.package_name.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Visitor responsible for completions in selected AST elements
|
||||
struct AutocompletionVisitor<'a> {
|
||||
root: &'a DesignRoot,
|
||||
cursor: Position,
|
||||
completions: &'a mut Vec<String>,
|
||||
completions: Vec<CompletionItem<'a>>,
|
||||
tokens: Vec<Token>,
|
||||
}
|
||||
|
||||
impl<'a> AutocompletionVisitor<'a> {
|
||||
pub fn new(
|
||||
root: &'a DesignRoot,
|
||||
cursor: Position,
|
||||
tokens: Vec<Token>,
|
||||
) -> AutocompletionVisitor<'a> {
|
||||
AutocompletionVisitor {
|
||||
root,
|
||||
cursor,
|
||||
completions: Vec::new(),
|
||||
tokens,
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads completion options for the given map aspect.
|
||||
/// Returns `true`, when the cursor is inside the map aspect and the search should not continue.
|
||||
/// Returns `false` otherwise
|
||||
|
|
@ -162,13 +178,19 @@ impl<'a> AutocompletionVisitor<'a> {
|
|||
if !map.span(ctx).contains(self.cursor) {
|
||||
return false;
|
||||
}
|
||||
let formals_in_map: HashSet<String> =
|
||||
HashSet::from_iter(map.formals().map(|name| name.to_string().to_lowercase()));
|
||||
match self.tokens[..] {
|
||||
[.., kind!(LeftPar | Comma)] | [.., kind!(LeftPar | Comma), kind!(Identifier)] => {}
|
||||
_ => return false,
|
||||
}
|
||||
let formals_in_map: HashSet<EntityId> =
|
||||
HashSet::from_iter(map.formals().filter_map(|it| *it));
|
||||
if let Some(ent) = node.entity_reference() {
|
||||
self.root
|
||||
.extract_port_or_generic_names(ent, self.completions, kind);
|
||||
self.completions
|
||||
.retain(|name| !formals_in_map.contains(&name.to_lowercase()));
|
||||
let ids = self.root.extract_port_or_generic_names(ent, kind);
|
||||
self.completions.extend(
|
||||
ids.iter()
|
||||
.filter(|id| !formals_in_map.contains(id))
|
||||
.map(|id| CompletionItem::Formal(self.root.get_ent(*id))),
|
||||
);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
|
@ -216,31 +238,6 @@ impl<'a> Visitor for AutocompletionVisitor<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the completable string representation of a declaration
|
||||
/// for example:
|
||||
/// `let alias = parse_vhdl("alias my_alias is ...")`
|
||||
/// `declaration_to_string(Declaration::Alias(alias)) == "my_alias"`
|
||||
/// Returns `None` if the declaration has no string representation that can be used for completion
|
||||
/// purposes.
|
||||
fn declaration_to_string(decl: &Declaration) -> Option<String> {
|
||||
match decl {
|
||||
Declaration::Object(o) => Some(o.ident.tree.item.to_string()),
|
||||
Declaration::File(f) => Some(f.ident.tree.item.to_string()),
|
||||
Declaration::Type(t) => Some(t.ident.tree.item.to_string()),
|
||||
Declaration::Component(c) => Some(c.ident.tree.item.to_string()),
|
||||
Declaration::Attribute(a) => match a {
|
||||
crate::ast::Attribute::Specification(spec) => Some(spec.ident.item.to_string()),
|
||||
crate::ast::Attribute::Declaration(decl) => Some(decl.ident.tree.item.to_string()),
|
||||
},
|
||||
Declaration::Alias(a) => Some(a.designator.to_string()),
|
||||
Declaration::SubprogramDeclaration(decl) => Some(decl.subpgm_designator().to_string()),
|
||||
Declaration::SubprogramBody(_) => None,
|
||||
Declaration::Use(_) => None,
|
||||
Declaration::Package(p) => Some(p.ident.to_string()),
|
||||
Declaration::Configuration(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Tokenizes `source` up to `cursor` but no further. The last token returned is the token
|
||||
/// where the cursor currently resides or the token right before the cursor.
|
||||
///
|
||||
|
|
@ -283,50 +280,76 @@ fn tokenize_input(symbols: &Symbols, source: &Source, cursor: Position) -> Vec<T
|
|||
|
||||
impl DesignRoot {
|
||||
/// helper function to list the name of all available libraries
|
||||
fn list_all_libraries(&self) -> Vec<String> {
|
||||
self.available_libraries()
|
||||
.map(|k| k.name().to_string())
|
||||
fn list_all_libraries(&self) -> Vec<CompletionItem> {
|
||||
self.libraries()
|
||||
.map(|lib| CompletionItem::Simple(self.get_ent(lib.id())))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// List the name of all primary units for a given library.
|
||||
/// If the library is non-resolvable, list an empty vector
|
||||
fn list_primaries_for_lib(&self, lib: &Symbol) -> Vec<String> {
|
||||
let Some(lib) = self.get_library_units(lib) else {
|
||||
fn list_primaries_for_lib(&self, lib: &Symbol) -> Vec<CompletionItem> {
|
||||
let Some(lib) = self.get_lib(lib) else {
|
||||
return vec![];
|
||||
};
|
||||
lib.keys()
|
||||
.filter_map(|key| match key {
|
||||
UnitKey::Primary(prim) => Some(prim.name().to_string()),
|
||||
UnitKey::Secondary(_, _) => None,
|
||||
})
|
||||
lib.primary_units()
|
||||
.filter_map(|it| it.unit.get().and_then(|unit| unit.ent_id()))
|
||||
.map(|id| CompletionItem::Simple(self.get_ent(id)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Lists all available declarations for a primary unit inside a given library
|
||||
/// If the library does not exist or there is no primary unit with the given name for that library,
|
||||
/// return an empty vector
|
||||
fn list_available_declarations(&self, lib: &Symbol, primary_unit: &Symbol) -> Vec<String> {
|
||||
let Some(lib) = self.get_library_units(lib) else {
|
||||
fn list_available_declarations(
|
||||
&self,
|
||||
lib: &Symbol,
|
||||
primary_unit: &Symbol,
|
||||
) -> Vec<CompletionItem> {
|
||||
let Some(unit) = self
|
||||
.get_lib(lib)
|
||||
.and_then(|lib| lib.primary_unit(primary_unit))
|
||||
.and_then(|unit| unit.unit.get())
|
||||
else {
|
||||
return vec![];
|
||||
};
|
||||
let Some(unit) = lib.get(&UnitKey::Primary(primary_unit.clone())) else {
|
||||
return vec![];
|
||||
};
|
||||
let unit = unit.unit.get();
|
||||
match unit.unwrap().to_owned() {
|
||||
AnyDesignUnit::Primary(AnyPrimaryUnit::Package(pkg)) => pkg
|
||||
.decl
|
||||
.iter()
|
||||
.filter_map(declaration_to_string)
|
||||
.unique()
|
||||
.chain(vec!["all".to_string()])
|
||||
.collect_vec(),
|
||||
|
||||
match unit.data() {
|
||||
AnyDesignUnit::Primary(AnyPrimaryUnit::Package(pkg)) => {
|
||||
let Some(pkg_id) = pkg.ident.decl else {
|
||||
return Vec::default();
|
||||
};
|
||||
let ent = self.get_ent(pkg_id);
|
||||
match &ent.kind {
|
||||
Design(crate::analysis::Design::Package(_, region)) => region
|
||||
.entities
|
||||
.values()
|
||||
.map(|named_ent| match named_ent {
|
||||
NamedEntities::Single(ent) => CompletionItem::Simple(ent),
|
||||
NamedEntities::Overloaded(overloaded) => match overloaded.as_unique() {
|
||||
None => CompletionItem::Overloaded(
|
||||
overloaded.designator().clone(),
|
||||
overloaded.len(),
|
||||
),
|
||||
Some(ent_ref) => CompletionItem::Simple(ent_ref),
|
||||
},
|
||||
})
|
||||
.chain(once(CompletionItem::Keyword(All)))
|
||||
.collect(),
|
||||
_ => Vec::default(),
|
||||
}
|
||||
}
|
||||
_ => Vec::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_completion_options(&self, source: &Source, cursor: Position) -> Vec<String> {
|
||||
/// Main entry point for completion. Given a source-file and a cursor position,
|
||||
/// lists available completion options at the cursor position.
|
||||
pub fn list_completion_options(
|
||||
&self,
|
||||
source: &Source,
|
||||
cursor: Position,
|
||||
) -> Vec<CompletionItem> {
|
||||
let tokens = tokenize_input(&self.symbols, source, cursor);
|
||||
match &tokens[..] {
|
||||
[.., kind!(Library)] | [.., kind!(Use)] | [.., kind!(Use), kind!(Identifier)] => {
|
||||
|
|
@ -341,14 +364,9 @@ impl DesignRoot {
|
|||
self.list_available_declarations(library, selected)
|
||||
}
|
||||
_ => {
|
||||
let mut completions = vec![];
|
||||
let mut visitor = AutocompletionVisitor {
|
||||
completions: &mut completions,
|
||||
root: self,
|
||||
cursor,
|
||||
};
|
||||
let mut visitor = AutocompletionVisitor::new(self, cursor, tokens);
|
||||
self.walk(&mut visitor);
|
||||
completions
|
||||
visitor.completions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -426,16 +444,20 @@ mod test {
|
|||
let (root, _) = LibraryBuilder::new().get_analyzed_root();
|
||||
let code = Code::new("use std.");
|
||||
let cursor = code.pos().end();
|
||||
let mut options = root.list_completion_options(code.source(), cursor);
|
||||
options.sort();
|
||||
assert_eq!(options, vec!["env", "standard", "textio"]);
|
||||
let options = root.list_completion_options(code.source(), cursor);
|
||||
assert!(options.contains(&CompletionItem::Simple(root.find_textio_pkg())));
|
||||
assert!(options.contains(&CompletionItem::Simple(root.find_standard_pkg())));
|
||||
assert!(options.contains(&CompletionItem::Simple(root.find_env_pkg())));
|
||||
assert_eq!(options.len(), 3);
|
||||
|
||||
let code = Code::new("use std.t");
|
||||
let cursor = code.pos().end();
|
||||
let mut options = root.list_completion_options(code.source(), cursor);
|
||||
options.sort();
|
||||
let options = root.list_completion_options(code.source(), cursor);
|
||||
// Note that the filtering only happens at client side
|
||||
assert_eq!(options, vec!["env", "standard", "textio"]);
|
||||
assert!(options.contains(&CompletionItem::Simple(root.find_textio_pkg())));
|
||||
assert!(options.contains(&CompletionItem::Simple(root.find_standard_pkg())));
|
||||
assert!(options.contains(&CompletionItem::Simple(root.find_env_pkg())));
|
||||
assert_eq!(options.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -445,7 +467,21 @@ mod test {
|
|||
let (root, _) = input.get_analyzed_root();
|
||||
let cursor = code.pos().end();
|
||||
let options = root.list_completion_options(code.source(), cursor);
|
||||
assert_eq!(options, vec!["stop", "finish", "resolution_limit", "all"])
|
||||
println!("{:?}", options);
|
||||
|
||||
assert!(options.contains(&CompletionItem::Overloaded(
|
||||
Designator::Identifier(root.symbol_utf8("stop")),
|
||||
2
|
||||
)));
|
||||
assert!(options.contains(&CompletionItem::Overloaded(
|
||||
Designator::Identifier(root.symbol_utf8("finish")),
|
||||
2
|
||||
)));
|
||||
assert!(options.contains(&CompletionItem::Simple(
|
||||
root.find_env_symbol("resolution_limit"),
|
||||
)));
|
||||
assert!(options.contains(&CompletionItem::Keyword(All)));
|
||||
assert_eq!(options.len(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -454,40 +490,67 @@ mod test {
|
|||
let code = input.code(
|
||||
"libname",
|
||||
"\
|
||||
entity my_ent is
|
||||
end entity my_ent;
|
||||
entity my_ent is
|
||||
end entity my_ent;
|
||||
|
||||
architecture arch of my_ent is
|
||||
component comp is
|
||||
generic (
|
||||
A: natural := 5;
|
||||
B: integer
|
||||
);
|
||||
port (
|
||||
clk : in bit;
|
||||
rst : in bit;
|
||||
dout : out bit
|
||||
);
|
||||
end component comp;
|
||||
signal clk, rst: bit;
|
||||
begin
|
||||
comp_inst: comp
|
||||
generic map (
|
||||
A => 2
|
||||
)
|
||||
port map (
|
||||
clk => clk
|
||||
);
|
||||
end arch;
|
||||
",
|
||||
architecture arch of my_ent is
|
||||
component comp is
|
||||
generic (
|
||||
A: natural := 5;
|
||||
B: integer
|
||||
);
|
||||
port (
|
||||
clk : in bit;
|
||||
rst : in bit;
|
||||
dout : out bit
|
||||
);
|
||||
end component comp;
|
||||
signal clk, rst: bit;
|
||||
begin
|
||||
comp_inst: comp
|
||||
generic map (
|
||||
A => 2
|
||||
)
|
||||
port map (
|
||||
clk => clk
|
||||
);
|
||||
end arch;
|
||||
",
|
||||
);
|
||||
let (root, _) = input.get_analyzed_root();
|
||||
let cursor = code.s1("generic map (").pos().end();
|
||||
let options = root.list_completion_options(code.source(), cursor);
|
||||
assert_eq!(options, vec!["B"]);
|
||||
let ent = root
|
||||
.search_reference(code.source(), code.s1("B").start())
|
||||
.unwrap();
|
||||
assert_eq!(options, vec![CompletionItem::Formal(ent)]);
|
||||
|
||||
let rst = root
|
||||
.search_reference(code.source(), code.s1("rst").start())
|
||||
.unwrap();
|
||||
|
||||
let dout = root
|
||||
.search_reference(code.source(), code.s1("dout").start())
|
||||
.unwrap();
|
||||
|
||||
let cursor = code.s1("port map (").pos().end();
|
||||
let options = root.list_completion_options(code.source(), cursor);
|
||||
assert_eq!(options, vec!["rst", "dout"]);
|
||||
assert!(options.contains(&CompletionItem::Formal(rst)));
|
||||
assert!(options.contains(&CompletionItem::Formal(dout)));
|
||||
assert_eq!(options.len(), 2);
|
||||
let cursor = code
|
||||
.s1("port map (
|
||||
clk =>")
|
||||
.pos()
|
||||
.end();
|
||||
let options = root.list_completion_options(code.source(), cursor);
|
||||
assert_eq!(options.len(), 0);
|
||||
let cursor = code
|
||||
.s1("port map (
|
||||
clk => c")
|
||||
.pos()
|
||||
.end();
|
||||
let options = root.list_completion_options(code.source(), cursor);
|
||||
assert_eq!(options.len(), 0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,14 @@
|
|||
|
||||
use super::formal_region::FormalRegion;
|
||||
use super::region::Region;
|
||||
use crate::ast::ExternalObjectClass;
|
||||
use crate::ast::{
|
||||
AnyPrimaryUnit, Designator, HasIdent, Ident, ObjectClass, SubprogramDeclaration, WithDecl,
|
||||
AliasDeclaration, AnyDesignUnit, AnyPrimaryUnit, AnySecondaryUnit, Attribute,
|
||||
AttributeDeclaration, AttributeSpecification, ComponentDeclaration, Declaration, Designator,
|
||||
FileDeclaration, HasIdent, Ident, InterfaceFileDeclaration, InterfacePackageDeclaration,
|
||||
ObjectClass, ObjectDeclaration, PackageInstantiation, SubprogramBody, SubprogramDeclaration,
|
||||
TypeDeclaration, WithDecl,
|
||||
};
|
||||
use crate::ast::{ExternalObjectClass, InterfaceDeclaration, InterfaceObjectDeclaration};
|
||||
use crate::data::*;
|
||||
|
||||
mod types;
|
||||
|
|
@ -455,6 +459,144 @@ impl HasEntityId for AnyPrimaryUnit {
|
|||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for AnyDesignUnit {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
match self {
|
||||
AnyDesignUnit::Primary(primary) => match primary {
|
||||
AnyPrimaryUnit::Entity(ent) => ent.ident.decl,
|
||||
AnyPrimaryUnit::Configuration(config) => config.ident.decl,
|
||||
AnyPrimaryUnit::Package(pkg) => pkg.ident.decl,
|
||||
AnyPrimaryUnit::PackageInstance(inst) => inst.ident.decl,
|
||||
AnyPrimaryUnit::Context(ctx) => ctx.ident.decl,
|
||||
},
|
||||
AnyDesignUnit::Secondary(secondary) => match secondary {
|
||||
AnySecondaryUnit::Architecture(arch) => arch.ident.decl,
|
||||
AnySecondaryUnit::PackageBody(bod) => bod.ident.decl,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for InterfaceDeclaration {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
match self {
|
||||
InterfaceDeclaration::Object(object) => object.ent_id(),
|
||||
InterfaceDeclaration::File(file) => file.ent_id(),
|
||||
InterfaceDeclaration::Type(typ) => typ.decl,
|
||||
InterfaceDeclaration::Subprogram(decl, _) => decl.ent_id(),
|
||||
InterfaceDeclaration::Package(pkg) => pkg.ent_id(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for InterfaceObjectDeclaration {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
self.ident.decl
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for InterfaceFileDeclaration {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
self.ident.decl
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for SubprogramDeclaration {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
match self {
|
||||
SubprogramDeclaration::Procedure(proc) => proc.designator.decl,
|
||||
SubprogramDeclaration::Function(func) => func.designator.decl,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for InterfacePackageDeclaration {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
self.ident.decl
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for Declaration {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
match self {
|
||||
Declaration::Object(object) => object.ent_id(),
|
||||
Declaration::File(file) => file.ent_id(),
|
||||
Declaration::Type(typ) => typ.ent_id(),
|
||||
Declaration::Component(comp) => comp.ent_id(),
|
||||
Declaration::Attribute(attr) => attr.ent_id(),
|
||||
Declaration::Alias(alias) => alias.ent_id(),
|
||||
Declaration::SubprogramDeclaration(decl) => decl.ent_id(),
|
||||
Declaration::SubprogramBody(body) => body.ent_id(),
|
||||
Declaration::Package(pkg) => pkg.ent_id(),
|
||||
Declaration::Use(_) => None,
|
||||
Declaration::Configuration(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for PackageInstantiation {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
self.ident.decl
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for SubprogramBody {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
self.specification.ent_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for AliasDeclaration {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
self.designator.decl
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for ObjectDeclaration {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
self.ident.decl
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for FileDeclaration {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
self.ident.decl
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for TypeDeclaration {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
self.ident.decl
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for ComponentDeclaration {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
self.ident.decl
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for Attribute {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
match self {
|
||||
Attribute::Specification(spec) => spec.ent_id(),
|
||||
Attribute::Declaration(decl) => decl.ent_id(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for AttributeDeclaration {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
self.ident.decl
|
||||
}
|
||||
}
|
||||
|
||||
impl HasEntityId for AttributeSpecification {
|
||||
fn ent_id(&self) -> Option<EntityId> {
|
||||
self.ident.reference
|
||||
}
|
||||
}
|
||||
|
||||
impl WithDecl<Ident> {
|
||||
pub fn define<'a>(
|
||||
&mut self,
|
||||
|
|
|
|||
|
|
@ -275,4 +275,16 @@ impl EntityId {
|
|||
fn local_id(&self) -> LocalId {
|
||||
LocalId((self.id & (u32::MAX as usize)) as u32)
|
||||
}
|
||||
|
||||
/// Returns an `EntityId` from a raw `usize` value
|
||||
/// for deserialization purposes.
|
||||
pub fn from_raw(id: usize) -> EntityId {
|
||||
EntityId { id }
|
||||
}
|
||||
|
||||
/// Converts an `EntityId` to a raw `usize` value
|
||||
/// for serialization purposes.
|
||||
pub fn to_raw(&self) -> usize {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ impl HasIdent for LockedUnit {
|
|||
/// Represents a VHDL library containing zero or more design units.
|
||||
///
|
||||
/// This struct also keeps track of which source file contained which design units.
|
||||
struct Library {
|
||||
pub struct Library {
|
||||
name: Symbol,
|
||||
|
||||
/// Arena is only used to store the AnyEnt for the library itself
|
||||
|
|
@ -252,6 +252,21 @@ impl Library {
|
|||
fn get_unit(&self, key: &UnitKey) -> Option<&LockedUnit> {
|
||||
self.units.get(key)
|
||||
}
|
||||
|
||||
pub fn id(&self) -> EntityId {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub(super) fn primary_units(&self) -> impl Iterator<Item = &LockedUnit> {
|
||||
self.units.iter().filter_map(|(key, value)| match key {
|
||||
UnitKey::Primary(_) => Some(value),
|
||||
UnitKey::Secondary(_, _) => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn primary_unit(&self, symbol: &Symbol) -> Option<&LockedUnit> {
|
||||
self.units.get(&UnitKey::Primary(symbol.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the entire design state.
|
||||
|
|
@ -330,6 +345,14 @@ impl DesignRoot {
|
|||
self.libraries.keys()
|
||||
}
|
||||
|
||||
pub fn libraries(&self) -> impl Iterator<Item = &Library> {
|
||||
self.libraries.values()
|
||||
}
|
||||
|
||||
pub fn get_lib(&self, sym: &Symbol) -> Option<&Library> {
|
||||
self.libraries.get(sym)
|
||||
}
|
||||
|
||||
pub(crate) fn get_design_entity<'a>(
|
||||
&'a self,
|
||||
library_name: &Symbol,
|
||||
|
|
@ -545,12 +568,11 @@ impl DesignRoot {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn find_standard_pkg(&self) -> &AnyEnt {
|
||||
fn find_std_package(&self, symbol: &str) -> &AnyEnt {
|
||||
let std_lib = self.libraries.get(&self.symbol_utf8("std")).unwrap();
|
||||
let unit = std_lib
|
||||
.get_unit(&UnitKey::Primary(self.symbol_utf8("standard")))
|
||||
.get_unit(&UnitKey::Primary(self.symbol_utf8(symbol)))
|
||||
.unwrap();
|
||||
|
||||
if let AnyPrimaryUnit::Package(pkg) = unit.unit.write().as_primary_mut().unwrap() {
|
||||
self.get_ent(pkg.ident.decl.unwrap())
|
||||
} else {
|
||||
|
|
@ -558,14 +580,55 @@ impl DesignRoot {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn find_standard_pkg(&self) -> &AnyEnt {
|
||||
self.find_std_package("standard")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn find_textio_pkg(&self) -> &AnyEnt {
|
||||
self.find_std_package("textio")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn find_env_pkg(&self) -> &AnyEnt {
|
||||
self.find_std_package("env")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn find_standard_symbol(&self, name: &str) -> &AnyEnt {
|
||||
if let AnyEntKind::Design(Design::Package(_, region)) = self.find_standard_pkg().kind() {
|
||||
region
|
||||
.lookup_immediate(&Designator::Identifier(self.symbol_utf8(name)))
|
||||
.unwrap()
|
||||
.as_non_overloaded()
|
||||
.unwrap()
|
||||
self.find_std_symbol("standard", name)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn find_env_symbol(&self, name: &str) -> &AnyEnt {
|
||||
self.find_std_symbol("env", name)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn find_overloaded_env_symbols(&self, name: &str) -> &NamedEntities {
|
||||
self.find_std_symbols("env", name)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn find_std_symbol(&self, package: &str, name: &str) -> &AnyEnt {
|
||||
if let AnyEntKind::Design(Design::Package(_, region)) =
|
||||
self.find_std_package(package).kind()
|
||||
{
|
||||
let sym = region.lookup_immediate(&Designator::Identifier(self.symbol_utf8(name)));
|
||||
sym.unwrap().first()
|
||||
} else {
|
||||
panic!("Not a package");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn find_std_symbols(&self, package: &str, name: &str) -> &NamedEntities {
|
||||
if let AnyEntKind::Design(Design::Package(_, region)) =
|
||||
self.find_std_package(package).kind()
|
||||
{
|
||||
let sym = region.lookup_immediate(&Designator::Identifier(self.symbol_utf8(name)));
|
||||
sym.unwrap()
|
||||
} else {
|
||||
panic!("Not a package");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1069,7 +1069,7 @@ pub struct MapAspect {
|
|||
|
||||
impl MapAspect {
|
||||
/// Returns an iterator over the formal elements of this map
|
||||
pub fn formals(&self) -> impl Iterator<Item = &Designator> {
|
||||
pub fn formals(&self) -> impl Iterator<Item = &Option<EntityId>> {
|
||||
self.list.formals()
|
||||
}
|
||||
|
||||
|
|
@ -1172,11 +1172,11 @@ impl<T> Default for SeparatedList<T> {
|
|||
|
||||
impl SeparatedList<AssociationElement> {
|
||||
/// Returns an iterator over the formal elements of this list
|
||||
pub fn formals(&self) -> impl Iterator<Item = &Designator> {
|
||||
pub fn formals(&self) -> impl Iterator<Item = &Option<EntityId>> {
|
||||
self.items.iter().filter_map(|el| match &el.formal {
|
||||
None => None,
|
||||
Some(name) => match &name.item {
|
||||
Name::Designator(desi) => Some(&desi.item),
|
||||
Name::Designator(desi) => Some(&desi.reference),
|
||||
_ => None,
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -23,9 +23,10 @@ pub use crate::data::{
|
|||
NullDiagnostics, NullMessages, Position, Range, Severity, Source, SrcPos,
|
||||
};
|
||||
|
||||
pub use crate::analysis::CompletionItem;
|
||||
pub use crate::analysis::{
|
||||
AnyEnt, AnyEntKind, Concurrent, Design, EntHierarchy, EntRef, EntityId, Object, Overloaded,
|
||||
Type,
|
||||
};
|
||||
pub use crate::project::{Project, SourceFile};
|
||||
pub use crate::syntax::{ParserResult, VHDLParser};
|
||||
pub use crate::syntax::{kind_str, ParserResult, VHDLParser};
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
//
|
||||
// Copyright (c) 2018, Olof Kraigher olof.kraigher@gmail.com
|
||||
|
||||
use crate::analysis::{AnyEnt, DesignRoot, EntRef};
|
||||
use crate::analysis::{AnyEnt, CompletionItem, DesignRoot, EntRef};
|
||||
use crate::ast::DesignFile;
|
||||
use crate::config::Config;
|
||||
use crate::syntax::VHDLParser;
|
||||
use crate::{data::*, EntHierarchy};
|
||||
use crate::{data::*, EntHierarchy, EntityId};
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
@ -278,6 +278,11 @@ impl Project {
|
|||
self.root.format_declaration(ent)
|
||||
}
|
||||
|
||||
pub fn format_entity(&self, id: EntityId) -> Option<String> {
|
||||
let ent = self.root.get_ent(id);
|
||||
self.format_declaration(ent)
|
||||
}
|
||||
|
||||
/// Search for all references to the declaration at decl_pos
|
||||
pub fn find_all_references(&self, ent: &AnyEnt) -> Vec<SrcPos> {
|
||||
self.root.find_all_references(ent)
|
||||
|
|
@ -293,7 +298,11 @@ impl Project {
|
|||
self.files.values()
|
||||
}
|
||||
|
||||
pub fn list_completion_options(&self, source: &Source, cursor: Position) -> Vec<String> {
|
||||
pub fn list_completion_options(
|
||||
&self,
|
||||
source: &Source,
|
||||
cursor: Position,
|
||||
) -> Vec<CompletionItem> {
|
||||
self.root.list_completion_options(source, cursor)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -214,6 +214,14 @@ impl ConnectionRpcChannel {
|
|||
}
|
||||
Err(request) => request,
|
||||
};
|
||||
let request = match extract::<request::ResolveCompletionItem>(request) {
|
||||
Ok((id, params)) => {
|
||||
let res = server.resolve_completion_item(¶ms);
|
||||
self.send_response(lsp_server::Response::new_ok(id, res));
|
||||
return;
|
||||
}
|
||||
Err(request) => request,
|
||||
};
|
||||
|
||||
debug!("Unhandled request: {:?}", request);
|
||||
self.send_response(lsp_server::Response::new_err(
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ use crate::rpc_channel::SharedRpcChannel;
|
|||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use vhdl_lang::{
|
||||
AnyEntKind, Concurrent, Config, Diagnostic, EntHierarchy, EntRef, Message, MessageHandler,
|
||||
Object, Overloaded, Project, Severity, Source, SrcPos, Type,
|
||||
kind_str, AnyEntKind, Concurrent, Config, Diagnostic, EntHierarchy, EntRef, EntityId, Message,
|
||||
MessageHandler, Object, Overloaded, Project, Severity, Source, SrcPos, Type,
|
||||
};
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
|
|
@ -130,11 +130,12 @@ impl VHDLServer {
|
|||
workspace_symbol_provider: Some(OneOf::Left(true)),
|
||||
document_symbol_provider: Some(OneOf::Left(true)),
|
||||
completion_provider: Some(CompletionOptions {
|
||||
resolve_provider: Some(false),
|
||||
resolve_provider: Some(true),
|
||||
trigger_characters: Some(trigger_chars),
|
||||
all_commit_characters: None,
|
||||
work_done_progress_options: Default::default(),
|
||||
completion_item: Default::default(),
|
||||
completion_item: Some(CompletionOptionsCompletionItem {
|
||||
label_details_support: Some(true),
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
|
@ -260,6 +261,41 @@ impl VHDLServer {
|
|||
}
|
||||
}
|
||||
|
||||
fn completion_item_to_lsp_item(
|
||||
&self,
|
||||
item: vhdl_lang::CompletionItem,
|
||||
) -> lsp_types::CompletionItem {
|
||||
match item {
|
||||
vhdl_lang::CompletionItem::Simple(ent) => entity_to_completion_item(ent),
|
||||
vhdl_lang::CompletionItem::Formal(ent) => {
|
||||
let mut item = entity_to_completion_item(ent);
|
||||
if self.client_supports_snippets() {
|
||||
item.insert_text_format = Some(InsertTextFormat::SNIPPET);
|
||||
item.insert_text = Some(format!("{} => $1,", item.insert_text.unwrap()));
|
||||
}
|
||||
item
|
||||
}
|
||||
vhdl_lang::CompletionItem::Overloaded(desi, count) => CompletionItem {
|
||||
label: desi.to_string(),
|
||||
detail: Some(format!("+{count} overloaded")),
|
||||
kind: match desi {
|
||||
Designator::Identifier(_) => Some(CompletionItemKind::FUNCTION),
|
||||
Designator::OperatorSymbol(_) => Some(CompletionItemKind::OPERATOR),
|
||||
_ => None,
|
||||
},
|
||||
insert_text: Some(desi.to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
vhdl_lang::CompletionItem::Keyword(kind) => CompletionItem {
|
||||
label: kind_str(kind).to_string(),
|
||||
detail: Some(kind_str(kind).to_string()),
|
||||
insert_text: Some(kind_str(kind).to_string()),
|
||||
kind: Some(CompletionItemKind::KEYWORD),
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when the client requests a completion.
|
||||
/// This function looks in the source code to find suitable options and then returns them
|
||||
pub fn request_completion(&mut self, params: &CompletionParams) -> CompletionList {
|
||||
|
|
@ -284,10 +320,7 @@ impl VHDLServer {
|
|||
.project
|
||||
.list_completion_options(&source, cursor)
|
||||
.into_iter()
|
||||
.map(|option| CompletionItem {
|
||||
label: option,
|
||||
..Default::default()
|
||||
})
|
||||
.map(|item| self.completion_item_to_lsp_item(item))
|
||||
.collect();
|
||||
|
||||
CompletionList {
|
||||
|
|
@ -296,6 +329,24 @@ impl VHDLServer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn resolve_completion_item(&mut self, params: &CompletionItem) -> CompletionItem {
|
||||
let mut params = params.clone();
|
||||
let eid = params
|
||||
.data
|
||||
.clone()
|
||||
.and_then(|val| serde_json::from_value::<usize>(val).ok())
|
||||
.map(EntityId::from_raw);
|
||||
if let Some(id) = eid {
|
||||
if let Some(text) = self.project.format_entity(id) {
|
||||
params.documentation = Some(Documentation::MarkupContent(MarkupContent {
|
||||
kind: MarkupKind::Markdown,
|
||||
value: format!("```vhdl\n{text}\n```"),
|
||||
}));
|
||||
}
|
||||
}
|
||||
params
|
||||
}
|
||||
|
||||
fn client_supports_related_information(&self) -> bool {
|
||||
let try_fun = || {
|
||||
self.init_params
|
||||
|
|
@ -324,6 +375,22 @@ impl VHDLServer {
|
|||
try_fun().unwrap_or(false)
|
||||
}
|
||||
|
||||
fn client_supports_snippets(&self) -> bool {
|
||||
let try_fun = || {
|
||||
self.init_params
|
||||
.as_ref()?
|
||||
.capabilities
|
||||
.text_document
|
||||
.as_ref()?
|
||||
.completion
|
||||
.as_ref()?
|
||||
.completion_item
|
||||
.as_ref()?
|
||||
.snippet_support
|
||||
};
|
||||
try_fun().unwrap_or(false)
|
||||
}
|
||||
|
||||
fn client_has_hierarchical_document_symbol_support(&self) -> bool {
|
||||
let try_fun = || {
|
||||
self.init_params
|
||||
|
|
@ -650,6 +717,49 @@ impl VHDLServer {
|
|||
}
|
||||
}
|
||||
|
||||
fn entity_to_completion_item(ent: EntRef) -> CompletionItem {
|
||||
CompletionItem {
|
||||
label: ent.designator.to_string(),
|
||||
detail: Some(ent.describe()),
|
||||
kind: Some(entity_kind_to_completion_kind(ent.kind())),
|
||||
data: serde_json::to_value(ent.id.to_raw()).ok(),
|
||||
insert_text: Some(ent.designator.to_string()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn entity_kind_to_completion_kind(kind: &AnyEntKind) -> CompletionItemKind {
|
||||
match kind {
|
||||
AnyEntKind::ExternalAlias { .. } | AnyEntKind::ObjectAlias { .. } => {
|
||||
CompletionItemKind::FIELD
|
||||
}
|
||||
AnyEntKind::File(_) | AnyEntKind::InterfaceFile(_) => CompletionItemKind::FILE,
|
||||
AnyEntKind::Component(_) => CompletionItemKind::MODULE,
|
||||
AnyEntKind::Attribute(_) => CompletionItemKind::REFERENCE,
|
||||
AnyEntKind::Overloaded(overloaded) => match overloaded {
|
||||
Overloaded::SubprogramDecl(_)
|
||||
| Overloaded::Subprogram(_)
|
||||
| Overloaded::InterfaceSubprogram(_) => CompletionItemKind::FUNCTION,
|
||||
Overloaded::EnumLiteral(_) => CompletionItemKind::ENUM_MEMBER,
|
||||
Overloaded::Alias(_) => CompletionItemKind::FIELD,
|
||||
},
|
||||
AnyEntKind::Type(_) => CompletionItemKind::TYPE_PARAMETER,
|
||||
AnyEntKind::ElementDeclaration(_) => CompletionItemKind::FIELD,
|
||||
AnyEntKind::Concurrent(_) => CompletionItemKind::MODULE,
|
||||
AnyEntKind::Sequential(_) => CompletionItemKind::MODULE,
|
||||
AnyEntKind::Object(object) => match object.class {
|
||||
ObjectClass::Signal => CompletionItemKind::EVENT,
|
||||
ObjectClass::Constant => CompletionItemKind::CONSTANT,
|
||||
ObjectClass::Variable | ObjectClass::SharedVariable => CompletionItemKind::VARIABLE,
|
||||
},
|
||||
AnyEntKind::LoopParameter(_) => CompletionItemKind::MODULE,
|
||||
AnyEntKind::PhysicalLiteral(_) => CompletionItemKind::UNIT,
|
||||
AnyEntKind::DeferredConstant(_) => CompletionItemKind::CONSTANT,
|
||||
AnyEntKind::Library => CompletionItemKind::MODULE,
|
||||
AnyEntKind::Design(_) => CompletionItemKind::MODULE,
|
||||
}
|
||||
}
|
||||
|
||||
struct MessageFilter {
|
||||
silent: bool,
|
||||
rpc: SharedRpcChannel,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue