diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs index c25b4c2177..52df6fd10a 100644 --- a/crates/libeditor/src/completion.rs +++ b/crates/libeditor/src/completion.rs @@ -2,7 +2,7 @@ use std::collections::{HashSet, HashMap}; use libsyntax2::{ File, TextUnit, AstNode, SyntaxNodeRef, SyntaxKind::*, - ast::{self, LoopBodyOwner}, + ast::{self, LoopBodyOwner, ModuleItemOwner}, algo::{ ancestors, visit::{visitor, Visitor, visitor_ctx, VisitorCtx}, @@ -58,22 +58,34 @@ fn complete_name_ref(file: &File, name_ref: ast::NameRef, acc: &mut Vec(name_ref.syntax()) { return; } - if let Some(fn_def) = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next() { - complete_expr_keywords(&file, fn_def, name_ref, acc); - let scopes = FnScopes::new(fn_def); - complete_fn(name_ref, &scopes, acc); - } - if let Some(root) = ancestors(name_ref.syntax()).filter_map(ast::Root::cast).next() { - let scope = ModuleScope::new(root); - acc.extend( - scope.entries().iter() - .filter(|entry| entry.syntax() != name_ref.syntax()) - .map(|entry| CompletionItem { - label: entry.name().to_string(), - lookup: None, - snippet: None, - }) - ); + let mut visited_fn = false; + for node in ancestors(name_ref.syntax()) { + if let Some(items) = visitor() + .visit::(|it| Some(it.items())) + .visit::(|it| Some(it.item_list()?.items())) + .accept(node) { + if let Some(items) = items { + let scope = ModuleScope::new(items); + acc.extend( + scope.entries().iter() + .filter(|entry| entry.syntax() != name_ref.syntax()) + .map(|entry| CompletionItem { + label: entry.name().to_string(), + lookup: None, + snippet: None, + }) + ); + } + 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); + } + } } } @@ -299,6 +311,18 @@ mod tests { ", 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] fn test_complete_type() { check_scope_completion(r" diff --git a/crates/libeditor/src/scope/mod_scope.rs b/crates/libeditor/src/scope/mod_scope.rs index 67baa8678e..0ec56a206d 100644 --- a/crates/libeditor/src/scope/mod_scope.rs +++ b/crates/libeditor/src/scope/mod_scope.rs @@ -1,5 +1,6 @@ use libsyntax2::{ - AstNode, SyntaxNode, SyntaxNodeRef, SmolStr, ast + AstNode, SyntaxNode, SyntaxNodeRef, SmolStr, + ast::{self, AstChildren}, }; pub struct ModuleScope { @@ -16,9 +17,9 @@ enum EntryKind { } impl ModuleScope { - pub fn new(m: ast::Root) -> ModuleScope { + pub fn new(items: AstChildren) -> ModuleScope { let mut entries = Vec::new(); - for item in m.items() { + for item in items { let entry = match item { ast::ModuleItem::StructDef(item) => Entry::new(item), ast::ModuleItem::EnumDef(item) => Entry::new(item), @@ -85,11 +86,11 @@ fn collect_imports(tree: ast::UseTree, acc: &mut Vec) { #[cfg(test)] mod tests { use super::*; - use libsyntax2::File; + use libsyntax2::{File, ast::ModuleItemOwner}; fn do_check(code: &str, expected: &[&str]) { let file = File::parse(&code); - let scope = ModuleScope::new(file.ast()); + let scope = ModuleScope::new(file.ast().items()); let actual = scope.entries .iter() .map(|it| it.name()) diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs index a239f0630d..c945c094ab 100644 --- a/crates/libsyntax2/src/ast/generated.rs +++ b/crates/libsyntax2/src/ast/generated.rs @@ -699,11 +699,8 @@ impl<'a> AstNode<'a> for ItemList<'a> { } impl<'a> ast::FnDefOwner<'a> for ItemList<'a> {} -impl<'a> ItemList<'a> { - pub fn items(self) -> impl Iterator> + 'a { - super::children(self) - } -} +impl<'a> ast::ModuleItemOwner<'a> for ItemList<'a> {} +impl<'a> ItemList<'a> {} // Label #[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::AttrsOwner<'a> for Module<'a> {} -impl<'a> ast::FnDefOwner<'a> for Module<'a> {} impl<'a> Module<'a> {pub fn item_list(self) -> Option> { super::child_opt(self) } @@ -1616,12 +1612,9 @@ impl<'a> AstNode<'a> for Root<'a> { 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> Root<'a> { - pub fn items(self) -> impl Iterator> + 'a { - super::children(self) - } - pub fn modules(self) -> impl Iterator> + 'a { super::children(self) } diff --git a/crates/libsyntax2/src/ast/mod.rs b/crates/libsyntax2/src/ast/mod.rs index 0b6868547e..a6da829577 100644 --- a/crates/libsyntax2/src/ast/mod.rs +++ b/crates/libsyntax2/src/ast/mod.rs @@ -36,7 +36,13 @@ pub trait ArgListOwner<'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) } } @@ -52,7 +58,7 @@ pub trait TypeParamsOwner<'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) } } @@ -158,7 +164,7 @@ impl<'a> IfExpr<'a> { pub fn else_branch(self) -> Option> { self.blocks().nth(1) } - fn blocks(self) -> AstNodeChildren<'a, Block<'a>> { + fn blocks(self) -> AstChildren<'a, Block<'a>> { children(self) } } @@ -167,27 +173,27 @@ fn child_opt<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> Option { children(parent).next() } -fn children<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> AstNodeChildren<'a, C> { - AstNodeChildren::new(parent.syntax()) +fn children<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> AstChildren<'a, C> { + AstChildren::new(parent.syntax()) } #[derive(Debug)] -pub struct AstNodeChildren<'a, N> { +pub struct AstChildren<'a, N> { inner: SyntaxNodeChildren>, ph: PhantomData, } -impl<'a, N> AstNodeChildren<'a, N> { +impl<'a, N> AstChildren<'a, N> { fn new(parent: SyntaxNodeRef<'a>) -> Self { - AstNodeChildren { + AstChildren { inner: parent.children(), 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; fn next(&mut self) -> Option { loop { diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron index 6ed658daa3..77ae4c7db2 100644 --- a/crates/libsyntax2/src/grammar.ron +++ b/crates/libsyntax2/src/grammar.ron @@ -238,9 +238,8 @@ Grammar( ], ast: { "Root": ( - traits: [ "FnDefOwner" ], + traits: [ "ModuleItemOwner", "FnDefOwner" ], collections: [ - ["items", "ModuleItem"], ["modules", "Module"], ] ), @@ -271,12 +270,11 @@ Grammar( ] ), "TraitDef": ( traits: ["NameOwner", "AttrsOwner"] ), "Module": ( - traits: ["NameOwner", "AttrsOwner", "FnDefOwner" ], + traits: ["NameOwner", "AttrsOwner" ], options: [ "ItemList" ] ), "ItemList": ( - traits: [ "FnDefOwner" ], - collections: [ ["items", "ModuleItem"] ] + traits: [ "FnDefOwner", "ModuleItemOwner" ], ), "ConstDef": ( traits: [ "NameOwner",