Skip to content

Commit

Permalink
More elaborate completions (#205)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Schottkyc137 committed Nov 2, 2023
1 parent 4732a6c commit c84e06b
Show file tree
Hide file tree
Showing 10 changed files with 592 additions and 183 deletions.
1 change: 1 addition & 0 deletions vhdl_lang/src/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
371 changes: 217 additions & 154 deletions vhdl_lang/src/analysis/completion.rs

Large diffs are not rendered by default.

146 changes: 144 additions & 2 deletions vhdl_lang/src/analysis/named_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
12 changes: 12 additions & 0 deletions vhdl_lang/src/analysis/named_entity/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
83 changes: 73 additions & 10 deletions vhdl_lang/src/analysis/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -545,27 +568,67 @@ 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 {
panic!("Not a package");
}
}

#[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");
}
Expand Down
6 changes: 3 additions & 3 deletions vhdl_lang/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}

Expand Down Expand Up @@ -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,
},
})
Expand Down
3 changes: 2 additions & 1 deletion vhdl_lang/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
15 changes: 12 additions & 3 deletions vhdl_lang/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
//
// Copyright (c) 2018, Olof Kraigher [email protected]

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};
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}
}
Expand Down
8 changes: 8 additions & 0 deletions vhdl_ls/src/stdio_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(&params);
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(
Expand Down
Loading

0 comments on commit c84e06b

Please sign in to comment.