nested mod completion

This commit is contained in:
Aleksey Kladov 2018-09-08 01:35:20 +03:00
parent ff1c82216c
commit 127814d9a7
5 changed files with 68 additions and 46 deletions

View file

@ -2,7 +2,7 @@ use std::collections::{HashSet, HashMap};
use libsyntax2::{ use libsyntax2::{
File, TextUnit, AstNode, SyntaxNodeRef, SyntaxKind::*, File, TextUnit, AstNode, SyntaxNodeRef, SyntaxKind::*,
ast::{self, LoopBodyOwner}, ast::{self, LoopBodyOwner, ModuleItemOwner},
algo::{ algo::{
ancestors, ancestors,
visit::{visitor, Visitor, visitor_ctx, VisitorCtx}, visit::{visitor, Visitor, visitor_ctx, VisitorCtx},
@ -58,13 +58,14 @@ fn complete_name_ref(file: &File, name_ref: ast::NameRef, acc: &mut Vec<Completi
if !is_node::<ast::Path>(name_ref.syntax()) { if !is_node::<ast::Path>(name_ref.syntax()) {
return; return;
} }
if let Some(fn_def) = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next() { let mut visited_fn = false;
complete_expr_keywords(&file, fn_def, name_ref, acc); for node in ancestors(name_ref.syntax()) {
let scopes = FnScopes::new(fn_def); if let Some(items) = visitor()
complete_fn(name_ref, &scopes, acc); .visit::<ast::Root, _>(|it| Some(it.items()))
} .visit::<ast::Module, _>(|it| Some(it.item_list()?.items()))
if let Some(root) = ancestors(name_ref.syntax()).filter_map(ast::Root::cast).next() { .accept(node) {
let scope = ModuleScope::new(root); if let Some(items) = items {
let scope = ModuleScope::new(items);
acc.extend( acc.extend(
scope.entries().iter() scope.entries().iter()
.filter(|entry| entry.syntax() != name_ref.syntax()) .filter(|entry| entry.syntax() != name_ref.syntax())
@ -75,6 +76,17 @@ fn complete_name_ref(file: &File, name_ref: ast::NameRef, acc: &mut Vec<Completi
}) })
); );
} }
break;
} else if !visited_fn {
if let Some(fn_def) = ast::FnDef::cast(node) {
visited_fn = true;
complete_expr_keywords(&file, fn_def, name_ref, acc);
let scopes = FnScopes::new(fn_def);
complete_fn(name_ref, &scopes, acc);
}
}
}
} }
fn param_completions(ctx: SyntaxNodeRef, acc: &mut Vec<CompletionItem>) { fn param_completions(ctx: SyntaxNodeRef, acc: &mut Vec<CompletionItem>) {
@ -299,6 +311,18 @@ mod tests {
", r#"[]"#); ", r#"[]"#);
} }
#[test]
fn test_completion_mod_scope_nested() {
check_scope_completion(r"
struct Foo;
mod m {
struct Bar;
fn quux() { <|> }
}
", r#"[CompletionItem { label: "Bar", lookup: None, snippet: None },
CompletionItem { label: "quux", lookup: None, snippet: None }]"#);
}
#[test] #[test]
fn test_complete_type() { fn test_complete_type() {
check_scope_completion(r" check_scope_completion(r"

View file

@ -1,5 +1,6 @@
use libsyntax2::{ use libsyntax2::{
AstNode, SyntaxNode, SyntaxNodeRef, SmolStr, ast AstNode, SyntaxNode, SyntaxNodeRef, SmolStr,
ast::{self, AstChildren},
}; };
pub struct ModuleScope { pub struct ModuleScope {
@ -16,9 +17,9 @@ enum EntryKind {
} }
impl ModuleScope { impl ModuleScope {
pub fn new(m: ast::Root) -> ModuleScope { pub fn new(items: AstChildren<ast::ModuleItem>) -> ModuleScope {
let mut entries = Vec::new(); let mut entries = Vec::new();
for item in m.items() { for item in items {
let entry = match item { let entry = match item {
ast::ModuleItem::StructDef(item) => Entry::new(item), ast::ModuleItem::StructDef(item) => Entry::new(item),
ast::ModuleItem::EnumDef(item) => Entry::new(item), ast::ModuleItem::EnumDef(item) => Entry::new(item),
@ -85,11 +86,11 @@ fn collect_imports(tree: ast::UseTree, acc: &mut Vec<Entry>) {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use libsyntax2::File; use libsyntax2::{File, ast::ModuleItemOwner};
fn do_check(code: &str, expected: &[&str]) { fn do_check(code: &str, expected: &[&str]) {
let file = File::parse(&code); let file = File::parse(&code);
let scope = ModuleScope::new(file.ast()); let scope = ModuleScope::new(file.ast().items());
let actual = scope.entries let actual = scope.entries
.iter() .iter()
.map(|it| it.name()) .map(|it| it.name())

View file

@ -699,11 +699,8 @@ impl<'a> AstNode<'a> for ItemList<'a> {
} }
impl<'a> ast::FnDefOwner<'a> for ItemList<'a> {} impl<'a> ast::FnDefOwner<'a> for ItemList<'a> {}
impl<'a> ItemList<'a> { impl<'a> ast::ModuleItemOwner<'a> for ItemList<'a> {}
pub fn items(self) -> impl Iterator<Item = ModuleItem<'a>> + 'a { impl<'a> ItemList<'a> {}
super::children(self)
}
}
// Label // Label
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -979,7 +976,6 @@ impl<'a> AstNode<'a> for Module<'a> {
impl<'a> ast::NameOwner<'a> for Module<'a> {} impl<'a> ast::NameOwner<'a> for Module<'a> {}
impl<'a> ast::AttrsOwner<'a> for Module<'a> {} impl<'a> ast::AttrsOwner<'a> for Module<'a> {}
impl<'a> ast::FnDefOwner<'a> for Module<'a> {}
impl<'a> Module<'a> {pub fn item_list(self) -> Option<ItemList<'a>> { impl<'a> Module<'a> {pub fn item_list(self) -> Option<ItemList<'a>> {
super::child_opt(self) super::child_opt(self)
} }
@ -1616,12 +1612,9 @@ impl<'a> AstNode<'a> for Root<'a> {
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
} }
impl<'a> ast::ModuleItemOwner<'a> for Root<'a> {}
impl<'a> ast::FnDefOwner<'a> for Root<'a> {} impl<'a> ast::FnDefOwner<'a> for Root<'a> {}
impl<'a> Root<'a> { impl<'a> Root<'a> {
pub fn items(self) -> impl Iterator<Item = ModuleItem<'a>> + 'a {
super::children(self)
}
pub fn modules(self) -> impl Iterator<Item = Module<'a>> + 'a { pub fn modules(self) -> impl Iterator<Item = Module<'a>> + 'a {
super::children(self) super::children(self)
} }

View file

@ -36,7 +36,13 @@ pub trait ArgListOwner<'a>: AstNode<'a> {
} }
pub trait FnDefOwner<'a>: AstNode<'a> { pub trait FnDefOwner<'a>: AstNode<'a> {
fn functions(self) -> AstNodeChildren<'a, FnDef<'a>> { fn functions(self) -> AstChildren<'a, FnDef<'a>> {
children(self)
}
}
pub trait ModuleItemOwner<'a>: AstNode<'a> {
fn items(self) -> AstChildren<'a, ModuleItem<'a>> {
children(self) children(self)
} }
} }
@ -52,7 +58,7 @@ pub trait TypeParamsOwner<'a>: AstNode<'a> {
} }
pub trait AttrsOwner<'a>: AstNode<'a> { pub trait AttrsOwner<'a>: AstNode<'a> {
fn attrs(self) -> AstNodeChildren<'a, Attr<'a>> { fn attrs(self) -> AstChildren<'a, Attr<'a>> {
children(self) children(self)
} }
} }
@ -158,7 +164,7 @@ impl<'a> IfExpr<'a> {
pub fn else_branch(self) -> Option<Block<'a>> { pub fn else_branch(self) -> Option<Block<'a>> {
self.blocks().nth(1) self.blocks().nth(1)
} }
fn blocks(self) -> AstNodeChildren<'a, Block<'a>> { fn blocks(self) -> AstChildren<'a, Block<'a>> {
children(self) children(self)
} }
} }
@ -167,27 +173,27 @@ fn child_opt<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> Option<C> {
children(parent).next() children(parent).next()
} }
fn children<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> AstNodeChildren<'a, C> { fn children<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> AstChildren<'a, C> {
AstNodeChildren::new(parent.syntax()) AstChildren::new(parent.syntax())
} }
#[derive(Debug)] #[derive(Debug)]
pub struct AstNodeChildren<'a, N> { pub struct AstChildren<'a, N> {
inner: SyntaxNodeChildren<RefRoot<'a>>, inner: SyntaxNodeChildren<RefRoot<'a>>,
ph: PhantomData<N>, ph: PhantomData<N>,
} }
impl<'a, N> AstNodeChildren<'a, N> { impl<'a, N> AstChildren<'a, N> {
fn new(parent: SyntaxNodeRef<'a>) -> Self { fn new(parent: SyntaxNodeRef<'a>) -> Self {
AstNodeChildren { AstChildren {
inner: parent.children(), inner: parent.children(),
ph: PhantomData, ph: PhantomData,
} }
} }
} }
impl<'a, N: AstNode<'a>> Iterator for AstNodeChildren<'a, N> { impl<'a, N: AstNode<'a>> Iterator for AstChildren<'a, N> {
type Item = N; type Item = N;
fn next(&mut self) -> Option<N> { fn next(&mut self) -> Option<N> {
loop { loop {

View file

@ -238,9 +238,8 @@ Grammar(
], ],
ast: { ast: {
"Root": ( "Root": (
traits: [ "FnDefOwner" ], traits: [ "ModuleItemOwner", "FnDefOwner" ],
collections: [ collections: [
["items", "ModuleItem"],
["modules", "Module"], ["modules", "Module"],
] ]
), ),
@ -271,12 +270,11 @@ Grammar(
] ), ] ),
"TraitDef": ( traits: ["NameOwner", "AttrsOwner"] ), "TraitDef": ( traits: ["NameOwner", "AttrsOwner"] ),
"Module": ( "Module": (
traits: ["NameOwner", "AttrsOwner", "FnDefOwner" ], traits: ["NameOwner", "AttrsOwner" ],
options: [ "ItemList" ] options: [ "ItemList" ]
), ),
"ItemList": ( "ItemList": (
traits: [ "FnDefOwner" ], traits: [ "FnDefOwner", "ModuleItemOwner" ],
collections: [ ["items", "ModuleItem"] ]
), ),
"ConstDef": ( traits: [ "ConstDef": ( traits: [
"NameOwner", "NameOwner",