diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs index c6ce6266153..2756e472add 100644 --- a/crates/libeditor/src/completion.rs +++ b/crates/libeditor/src/completion.rs @@ -8,7 +8,7 @@ use { AtomEdit, find_node_at_offset, - scope::FnScopes, + scope::{FnScopes, ModuleScope}, }; #[derive(Debug)] @@ -24,18 +24,27 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option(file.syntax(), offset)?; - let fn_def = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next()?; - let scopes = FnScopes::new(fn_def); - Some(complete(name_ref, &scopes)) + let mut res = Vec::new(); + if let Some(fn_def) = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next() { + let scopes = FnScopes::new(fn_def); + complete_fn(name_ref, &scopes, &mut res); + } + if let Some(root) = ancestors(name_ref.syntax()).filter_map(ast::Root::cast).next() { + let scope = ModuleScope::new(root); + res.extend( + scope.entries().iter() + .map(|entry| CompletionItem { name: entry.name().to_string() }) + ) + } + Some(res) } -fn complete(name_ref: ast::NameRef, scopes: &FnScopes) -> Vec { - scopes.scope_chain(name_ref.syntax()) - .flat_map(|scope| scopes.entries(scope).iter()) - .map(|entry| CompletionItem { - name: entry.name().to_string() - }) - .collect() +fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec) { + acc.extend( + scopes.scope_chain(name_ref.syntax()) + .flat_map(|scope| scopes.entries(scope).iter()) + .map(|entry| CompletionItem { name: entry.name().to_string() }) + ) } #[cfg(test)] @@ -59,7 +68,8 @@ fn quux(x: i32) { let z = (); } ", r#"[CompletionItem { name: "y" }, - CompletionItem { name: "x" }]"#); + CompletionItem { name: "x" }, + CompletionItem { name: "quux" }]"#); } #[test] @@ -75,7 +85,8 @@ fn quux() { } } ", r#"[CompletionItem { name: "b" }, - CompletionItem { name: "a" }]"#); + CompletionItem { name: "a" }, + CompletionItem { name: "quux" }]"#); } #[test] @@ -86,6 +97,20 @@ fn quux() { <|> } } - ", r#"[CompletionItem { name: "x" }]"#); + ", r#"[CompletionItem { name: "x" }, + CompletionItem { name: "quux" }]"#); + } + + #[test] + fn test_completion_mod_scope() { + do_check(r" + struct Foo; + enum Baz {} + fn quux() { + <|> + } + ", r#"[CompletionItem { name: "Foo" }, + CompletionItem { name: "Baz" }, + CompletionItem { name: "quux" }]"#); } } diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs index b2e2c4782c6..06dac9d6d04 100644 --- a/crates/libeditor/src/lib.rs +++ b/crates/libeditor/src/lib.rs @@ -18,7 +18,7 @@ use libsyntax2::{ File, TextUnit, TextRange, SyntaxNodeRef, - ast::{AstNode, NameOwner}, + ast::{self, AstNode, NameOwner}, algo::{walk, find_leaf_at_offset, ancestors}, SyntaxKind::{self, *}, }; @@ -126,8 +126,8 @@ pub fn syntax_tree(file: &File) -> String { } pub fn runnables(file: &File) -> Vec { - file.ast() - .functions() + walk::preorder(file.syntax()) + .filter_map(ast::FnDef::cast) .filter_map(|f| { let name = f.name()?.text(); let kind = if name == "main" { diff --git a/crates/libeditor/src/scope/mod.rs b/crates/libeditor/src/scope/mod.rs index 1a77a8b6e27..2f25230f8f3 100644 --- a/crates/libeditor/src/scope/mod.rs +++ b/crates/libeditor/src/scope/mod.rs @@ -1,3 +1,8 @@ mod fn_scope; +mod mod_scope; + +pub use self::{ + fn_scope::FnScopes, + mod_scope::ModuleScope, +}; -pub use self::fn_scope::FnScopes; diff --git a/crates/libeditor/src/scope/mod_scope.rs b/crates/libeditor/src/scope/mod_scope.rs new file mode 100644 index 00000000000..0e51108d984 --- /dev/null +++ b/crates/libeditor/src/scope/mod_scope.rs @@ -0,0 +1,47 @@ +use libsyntax2::{ + AstNode, SyntaxNode, SmolStr, ast +}; + +pub struct ModuleScope { + entries: Vec, +} + +impl ModuleScope { + pub fn new(m: ast::Root) -> ModuleScope { + let entries = m.items().filter_map(|item| { + match item { + ast::ModuleItem::StructDef(item) => Entry::new(item), + ast::ModuleItem::EnumDef(item) => Entry::new(item), + ast::ModuleItem::FnDef(item) => Entry::new(item), + ast::ModuleItem::TraitDef(item) => Entry::new(item), + ast::ModuleItem::ExternCrateItem(_) | + ast::ModuleItem::ImplItem(_) | + ast::ModuleItem::UseItem(_) => None + } + }).collect(); + + ModuleScope { entries } + } + + pub fn entries(&self) -> &[Entry] { + self.entries.as_slice() + } +} + +pub struct Entry { + name: SyntaxNode, +} + +impl Entry { + fn new<'a>(item: impl ast::NameOwner<'a>) -> Option { + let name = item.name()?; + Some(Entry { name: name.syntax().owned() }) + } + pub fn name(&self) -> SmolStr { + self.ast().text() + } + fn ast(&self) -> ast::Name { + ast::Name::cast(self.name.borrowed()).unwrap() + } +} + diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs index b24fd2aba2b..2b400b8474f 100644 --- a/crates/libsyntax2/src/ast/generated.rs +++ b/crates/libsyntax2/src/ast/generated.rs @@ -1441,6 +1441,10 @@ fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } } impl<'a> Root<'a> { + pub fn items(self) -> impl Iterator> + 'a { + super::children(self) + } + pub fn functions(self) -> impl Iterator> + 'a { super::children(self) } diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron index c3a29c10c79..8055a468713 100644 --- a/crates/libsyntax2/src/grammar.ron +++ b/crates/libsyntax2/src/grammar.ron @@ -238,6 +238,7 @@ Grammar( ast: { "Root": ( collections: [ + ["items", "ModuleItem"], ["functions", "FnDef"], ["modules", "Module"], ]