Skip to content

Commit

Permalink
Refactor: Move completion to its own module (#220)
Browse files Browse the repository at this point in the history
  • Loading branch information
Schottkyc137 committed Nov 12, 2023
1 parent 7259553 commit d1c7e07
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 101 deletions.
3 changes: 0 additions & 3 deletions vhdl_lang/src/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,8 @@ mod standard;
mod static_expression;
mod target;

mod completion;

#[cfg(test)]
pub(crate) mod tests;
pub(crate) use root::{Library, LockedUnit};

pub use self::root::{DesignRoot, EntHierarchy};
pub use completion::CompletionItem;
5 changes: 5 additions & 0 deletions vhdl_lang/src/analysis/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,11 @@ impl DesignRoot {
pub fn get_ent(&self, id: EntityId) -> &AnyEnt {
self.arenas.get(id)
}

/// Returns a reference to the symbols that were used to analyze and parse the design root.
pub fn symbols(&self) -> &Symbols {
self.symbols.as_ref()
}
}

fn get_all_affected(
Expand Down
188 changes: 93 additions & 95 deletions vhdl_lang/src/analysis/completion.rs → vhdl_lang/src/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,105 +281,103 @@ fn tokenize_input(symbols: &Symbols, source: &Source, cursor: Position) -> Vec<T
tokens
}

impl DesignRoot {
/// helper function to list the name of all available libraries
fn list_all_libraries(&self) -> Vec<CompletionItem> {
self.libraries()
.map(|lib| CompletionItem::Simple(self.get_ent(lib.id())))
.collect()
}
/// helper function to list the name of all available libraries
fn list_all_libraries(root: &DesignRoot) -> Vec<CompletionItem> {
root.libraries()
.map(|lib| CompletionItem::Simple(root.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<CompletionItem> {
let Some(lib) = self.get_lib(lib) else {
return vec![];
};
lib.primary_units()
.filter_map(|it| it.unit.get().and_then(|unit| unit.ent_id()))
.map(|id| CompletionItem::Simple(self.get_ent(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<'a>(root: &'a DesignRoot, lib: &Symbol) -> Vec<CompletionItem<'a>> {
let Some(lib) = root.get_lib(lib) else {
return vec![];
};
lib.primary_units()
.filter_map(|it| it.unit.get().and_then(|unit| unit.ent_id()))
.map(|id| CompletionItem::Simple(root.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<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![];
};

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 {
AnyEntKind::Design(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(),
}
/// 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<'a>(
root: &'a DesignRoot,
lib: &Symbol,
primary_unit: &Symbol,
) -> Vec<CompletionItem<'a>> {
let Some(unit) = root
.get_lib(lib)
.and_then(|lib| lib.primary_unit(primary_unit))
.and_then(|unit| unit.unit.get())
else {
return vec![];
};

match unit.data() {
AnyDesignUnit::Primary(AnyPrimaryUnit::Package(pkg)) => {
let Some(pkg_id) = pkg.ident.decl else {
return Vec::default();
};
let ent = root.get_ent(pkg_id);
match &ent.kind {
AnyEntKind::Design(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(),
}
_ => Vec::default(),
}
}

/// 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)] => {
self.list_all_libraries()
}
[.., kind!(Use), ident!(library), kind!(Dot)]
| [.., kind!(Use), ident!(library), kind!(Dot), kind!(Identifier)] => {
self.list_primaries_for_lib(library)
}
[.., kind!(Use), ident!(library), kind!(Dot), ident!(selected), kind!(Dot)]
| [.., kind!(Use), ident!(library), kind!(Dot), ident!(selected), kind!(Dot), kind!(StringLiteral | Identifier)] => {
self.list_available_declarations(library, selected)
}
_ => {
let mut visitor = AutocompletionVisitor::new(self, cursor, tokens);
self.walk_source(source, &mut visitor);
visitor.completions
}
/// 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<'a>(
root: &'a DesignRoot,
source: &Source,
cursor: Position,
) -> Vec<CompletionItem<'a>> {
let tokens = tokenize_input(root.symbols(), source, cursor);
match &tokens[..] {
[.., kind!(Library)] | [.., kind!(Use)] | [.., kind!(Use), kind!(Identifier)] => {
list_all_libraries(root)
}
[.., kind!(Use), ident!(library), kind!(Dot)]
| [.., kind!(Use), ident!(library), kind!(Dot), kind!(Identifier)] => {
list_primaries_for_lib(root, library)
}
[.., kind!(Use), ident!(library), kind!(Dot), ident!(selected), kind!(Dot)]
| [.., kind!(Use), ident!(library), kind!(Dot), ident!(selected), kind!(Dot), kind!(StringLiteral | Identifier)] => {
list_available_declarations(root, library, selected)
}
_ => {
let mut visitor = AutocompletionVisitor::new(root, cursor, tokens);
root.walk_source(source, &mut visitor);
visitor.completions
}
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::analysis::completion::tokenize_input;
use crate::analysis::tests::LibraryBuilder;
use crate::completion::tokenize_input;
use crate::syntax::test::Code;
use assert_matches::assert_matches;

Expand Down Expand Up @@ -438,24 +436,24 @@ mod test {
let code = Code::new("library ");
let (root, _) = input.get_analyzed_root();
let cursor = code.s1("library ").pos().end();
let options = root.list_completion_options(code.source(), cursor);
assert_eq!(options, root.list_all_libraries())
let options = list_completion_options(&root, code.source(), cursor);
assert_eq!(options, list_all_libraries(&root))
}

#[test]
pub fn completing_primaries() {
let (root, _) = LibraryBuilder::new().get_analyzed_root();
let code = Code::new("use std.");
let cursor = code.pos().end();
let options = root.list_completion_options(code.source(), cursor);
let options = list_completion_options(&root, 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 options = root.list_completion_options(code.source(), cursor);
let options = list_completion_options(&root, code.source(), cursor);
// Note that the filtering only happens at client side
assert!(options.contains(&CompletionItem::Simple(root.find_textio_pkg())));
assert!(options.contains(&CompletionItem::Simple(root.find_standard_pkg())));
Expand All @@ -469,7 +467,7 @@ mod test {
let code = Code::new("use std.env.");
let (root, _) = input.get_analyzed_root();
let cursor = code.pos().end();
let options = root.list_completion_options(code.source(), cursor);
let options = list_completion_options(&root, code.source(), cursor);

assert!(options.contains(&CompletionItem::Overloaded(
Designator::Identifier(root.symbol_utf8("stop")),
Expand Down Expand Up @@ -521,7 +519,7 @@ mod test {
);
let (root, _) = input.get_analyzed_root();
let cursor = code.s1("generic map (").pos().end();
let options = root.list_completion_options(code.source(), cursor);
let options = list_completion_options(&root, code.source(), cursor);
let ent = root
.search_reference(code.source(), code.s1("B").start())
.unwrap();
Expand All @@ -536,7 +534,7 @@ mod test {
.unwrap();

let cursor = code.s1("port map (").pos().end();
let options = root.list_completion_options(code.source(), cursor);
let options = list_completion_options(&root, code.source(), cursor);
assert!(options.contains(&CompletionItem::Formal(rst)));
assert!(options.contains(&CompletionItem::Formal(dout)));
assert_eq!(options.len(), 2);
Expand All @@ -545,14 +543,14 @@ mod test {
clk =>")
.pos()
.end();
let options = root.list_completion_options(code.source(), cursor);
let options = list_completion_options(&root, 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);
let options = list_completion_options(&root, code.source(), cursor);
assert_eq!(options.len(), 0);
}

Expand Down Expand Up @@ -588,7 +586,7 @@ mod test {
.search_reference(code.source(), code.s1("type T").s1("T").start())
.unwrap();
let cursor = code.s1("generic map (").pos().end();
let options = root.list_completion_options(code.source(), cursor);
let options = list_completion_options(&root, code.source(), cursor);
assert!(options.contains(&CompletionItem::Formal(bar_func)));
assert!(options.contains(&CompletionItem::Formal(x)));
assert!(options.contains(&CompletionItem::Formal(t)));
Expand Down
5 changes: 4 additions & 1 deletion vhdl_lang/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ mod named_entity;
mod project;
mod syntax;

mod completion;

pub use crate::config::Config;
pub use crate::data::{
Diagnostic, Latin1String, Message, MessageHandler, MessagePrinter, MessageType,
NullDiagnostics, NullMessages, Position, Range, Severity, Source, SrcPos,
};

pub use crate::analysis::CompletionItem;
pub use crate::analysis::EntHierarchy;
pub use crate::named_entity::{
AnyEnt, AnyEntKind, Concurrent, Design, EntRef, EntityId, HasEntityId, Object, Overloaded,
Expand All @@ -40,3 +41,5 @@ pub use crate::project::{Project, SourceFile};
pub use crate::syntax::{
kind_str, HasTokenSpan, ParserResult, Token, TokenAccess, TokenId, TokenSpan, VHDLParser,
};

pub use completion::{list_completion_options, CompletionItem};
5 changes: 3 additions & 2 deletions vhdl_lang/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
//
// Copyright (c) 2018, Olof Kraigher [email protected]

use crate::analysis::{CompletionItem, DesignRoot};
use crate::analysis::DesignRoot;
use crate::ast::DesignFile;
use crate::completion::{list_completion_options, CompletionItem};
use crate::config::Config;
use crate::lint::dead_code::UnusedDeclarationsLinter;
use crate::named_entity::{AnyEnt, EntRef};
Expand Down Expand Up @@ -318,7 +319,7 @@ impl Project {
source: &Source,
cursor: Position,
) -> Vec<CompletionItem> {
self.root.list_completion_options(source, cursor)
list_completion_options(&self.root, source, cursor)
}
}

Expand Down

0 comments on commit d1c7e07

Please sign in to comment.