mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 04:53:34 +00:00
complete items from module scope
This commit is contained in:
parent
d34588bf83
commit
537ea620bb
6 changed files with 100 additions and 18 deletions
|
@ -8,7 +8,7 @@ use libsyntax2::{
|
||||||
|
|
||||||
use {
|
use {
|
||||||
AtomEdit, find_node_at_offset,
|
AtomEdit, find_node_at_offset,
|
||||||
scope::FnScopes,
|
scope::{FnScopes, ModuleScope},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -24,18 +24,27 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionI
|
||||||
file.incremental_reparse(&edit)?
|
file.incremental_reparse(&edit)?
|
||||||
};
|
};
|
||||||
let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), offset)?;
|
let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), offset)?;
|
||||||
let fn_def = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next()?;
|
let mut res = Vec::new();
|
||||||
let scopes = FnScopes::new(fn_def);
|
if let Some(fn_def) = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next() {
|
||||||
Some(complete(name_ref, &scopes))
|
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<CompletionItem> {
|
fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
|
||||||
scopes.scope_chain(name_ref.syntax())
|
acc.extend(
|
||||||
.flat_map(|scope| scopes.entries(scope).iter())
|
scopes.scope_chain(name_ref.syntax())
|
||||||
.map(|entry| CompletionItem {
|
.flat_map(|scope| scopes.entries(scope).iter())
|
||||||
name: entry.name().to_string()
|
.map(|entry| CompletionItem { name: entry.name().to_string() })
|
||||||
})
|
)
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -59,7 +68,8 @@ mod tests {
|
||||||
let z = ();
|
let z = ();
|
||||||
}
|
}
|
||||||
", r#"[CompletionItem { name: "y" },
|
", r#"[CompletionItem { name: "y" },
|
||||||
CompletionItem { name: "x" }]"#);
|
CompletionItem { name: "x" },
|
||||||
|
CompletionItem { name: "quux" }]"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -75,7 +85,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
", r#"[CompletionItem { name: "b" },
|
", r#"[CompletionItem { name: "b" },
|
||||||
CompletionItem { name: "a" }]"#);
|
CompletionItem { name: "a" },
|
||||||
|
CompletionItem { name: "quux" }]"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -86,6 +97,20 @@ mod tests {
|
||||||
<|>
|
<|>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
", 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" }]"#);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ mod test_utils;
|
||||||
|
|
||||||
use libsyntax2::{
|
use libsyntax2::{
|
||||||
File, TextUnit, TextRange, SyntaxNodeRef,
|
File, TextUnit, TextRange, SyntaxNodeRef,
|
||||||
ast::{AstNode, NameOwner},
|
ast::{self, AstNode, NameOwner},
|
||||||
algo::{walk, find_leaf_at_offset, ancestors},
|
algo::{walk, find_leaf_at_offset, ancestors},
|
||||||
SyntaxKind::{self, *},
|
SyntaxKind::{self, *},
|
||||||
};
|
};
|
||||||
|
@ -126,8 +126,8 @@ pub fn syntax_tree(file: &File) -> String {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runnables(file: &File) -> Vec<Runnable> {
|
pub fn runnables(file: &File) -> Vec<Runnable> {
|
||||||
file.ast()
|
walk::preorder(file.syntax())
|
||||||
.functions()
|
.filter_map(ast::FnDef::cast)
|
||||||
.filter_map(|f| {
|
.filter_map(|f| {
|
||||||
let name = f.name()?.text();
|
let name = f.name()?.text();
|
||||||
let kind = if name == "main" {
|
let kind = if name == "main" {
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
mod fn_scope;
|
mod fn_scope;
|
||||||
|
mod mod_scope;
|
||||||
|
|
||||||
|
pub use self::{
|
||||||
|
fn_scope::FnScopes,
|
||||||
|
mod_scope::ModuleScope,
|
||||||
|
};
|
||||||
|
|
||||||
pub use self::fn_scope::FnScopes;
|
|
||||||
|
|
47
crates/libeditor/src/scope/mod_scope.rs
Normal file
47
crates/libeditor/src/scope/mod_scope.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use libsyntax2::{
|
||||||
|
AstNode, SyntaxNode, SmolStr, ast
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ModuleScope {
|
||||||
|
entries: Vec<Entry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Entry> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1441,6 +1441,10 @@ impl<'a> AstNode<'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 functions(self) -> impl Iterator<Item = FnDef<'a>> + 'a {
|
pub fn functions(self) -> impl Iterator<Item = FnDef<'a>> + 'a {
|
||||||
super::children(self)
|
super::children(self)
|
||||||
}
|
}
|
||||||
|
|
|
@ -238,6 +238,7 @@ Grammar(
|
||||||
ast: {
|
ast: {
|
||||||
"Root": (
|
"Root": (
|
||||||
collections: [
|
collections: [
|
||||||
|
["items", "ModuleItem"],
|
||||||
["functions", "FnDef"],
|
["functions", "FnDef"],
|
||||||
["modules", "Module"],
|
["modules", "Module"],
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in a new issue