aboutsummaryrefslogtreecommitdiff
path: root/crates/libeditor
diff options
context:
space:
mode:
Diffstat (limited to 'crates/libeditor')
-rw-r--r--crates/libeditor/src/completion.rs53
-rw-r--r--crates/libeditor/src/lib.rs6
-rw-r--r--crates/libeditor/src/scope/mod.rs7
-rw-r--r--crates/libeditor/src/scope/mod_scope.rs47
4 files changed, 95 insertions, 18 deletions
diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs
index c6ce62661..2756e472a 100644
--- a/crates/libeditor/src/completion.rs
+++ b/crates/libeditor/src/completion.rs
@@ -8,7 +8,7 @@ use libsyntax2::{
8 8
9use { 9use {
10 AtomEdit, find_node_at_offset, 10 AtomEdit, find_node_at_offset,
11 scope::FnScopes, 11 scope::{FnScopes, ModuleScope},
12}; 12};
13 13
14#[derive(Debug)] 14#[derive(Debug)]
@@ -24,18 +24,27 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionI
24 file.incremental_reparse(&edit)? 24 file.incremental_reparse(&edit)?
25 }; 25 };
26 let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), offset)?; 26 let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), offset)?;
27 let fn_def = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next()?; 27 let mut res = Vec::new();
28 let scopes = FnScopes::new(fn_def); 28 if let Some(fn_def) = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next() {
29 Some(complete(name_ref, &scopes)) 29 let scopes = FnScopes::new(fn_def);
30 complete_fn(name_ref, &scopes, &mut res);
31 }
32 if let Some(root) = ancestors(name_ref.syntax()).filter_map(ast::Root::cast).next() {
33 let scope = ModuleScope::new(root);
34 res.extend(
35 scope.entries().iter()
36 .map(|entry| CompletionItem { name: entry.name().to_string() })
37 )
38 }
39 Some(res)
30} 40}
31 41
32fn complete(name_ref: ast::NameRef, scopes: &FnScopes) -> Vec<CompletionItem> { 42fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
33 scopes.scope_chain(name_ref.syntax()) 43 acc.extend(
34 .flat_map(|scope| scopes.entries(scope).iter()) 44 scopes.scope_chain(name_ref.syntax())
35 .map(|entry| CompletionItem { 45 .flat_map(|scope| scopes.entries(scope).iter())
36 name: entry.name().to_string() 46 .map(|entry| CompletionItem { name: entry.name().to_string() })
37 }) 47 )
38 .collect()
39} 48}
40 49
41#[cfg(test)] 50#[cfg(test)]
@@ -59,7 +68,8 @@ mod tests {
59 let z = (); 68 let z = ();
60 } 69 }
61 ", r#"[CompletionItem { name: "y" }, 70 ", r#"[CompletionItem { name: "y" },
62 CompletionItem { name: "x" }]"#); 71 CompletionItem { name: "x" },
72 CompletionItem { name: "quux" }]"#);
63 } 73 }
64 74
65 #[test] 75 #[test]
@@ -75,7 +85,8 @@ mod tests {
75 } 85 }
76 } 86 }
77 ", r#"[CompletionItem { name: "b" }, 87 ", r#"[CompletionItem { name: "b" },
78 CompletionItem { name: "a" }]"#); 88 CompletionItem { name: "a" },
89 CompletionItem { name: "quux" }]"#);
79 } 90 }
80 91
81 #[test] 92 #[test]
@@ -86,6 +97,20 @@ mod tests {
86 <|> 97 <|>
87 } 98 }
88 } 99 }
89 ", r#"[CompletionItem { name: "x" }]"#); 100 ", r#"[CompletionItem { name: "x" },
101 CompletionItem { name: "quux" }]"#);
102 }
103
104 #[test]
105 fn test_completion_mod_scope() {
106 do_check(r"
107 struct Foo;
108 enum Baz {}
109 fn quux() {
110 <|>
111 }
112 ", r#"[CompletionItem { name: "Foo" },
113 CompletionItem { name: "Baz" },
114 CompletionItem { name: "quux" }]"#);
90 } 115 }
91} 116}
diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs
index b2e2c4782..06dac9d6d 100644
--- a/crates/libeditor/src/lib.rs
+++ b/crates/libeditor/src/lib.rs
@@ -18,7 +18,7 @@ mod test_utils;
18 18
19use libsyntax2::{ 19use libsyntax2::{
20 File, TextUnit, TextRange, SyntaxNodeRef, 20 File, TextUnit, TextRange, SyntaxNodeRef,
21 ast::{AstNode, NameOwner}, 21 ast::{self, AstNode, NameOwner},
22 algo::{walk, find_leaf_at_offset, ancestors}, 22 algo::{walk, find_leaf_at_offset, ancestors},
23 SyntaxKind::{self, *}, 23 SyntaxKind::{self, *},
24}; 24};
@@ -126,8 +126,8 @@ pub fn syntax_tree(file: &File) -> String {
126} 126}
127 127
128pub fn runnables(file: &File) -> Vec<Runnable> { 128pub fn runnables(file: &File) -> Vec<Runnable> {
129 file.ast() 129 walk::preorder(file.syntax())
130 .functions() 130 .filter_map(ast::FnDef::cast)
131 .filter_map(|f| { 131 .filter_map(|f| {
132 let name = f.name()?.text(); 132 let name = f.name()?.text();
133 let kind = if name == "main" { 133 let kind = if name == "main" {
diff --git a/crates/libeditor/src/scope/mod.rs b/crates/libeditor/src/scope/mod.rs
index 1a77a8b6e..2f25230f8 100644
--- a/crates/libeditor/src/scope/mod.rs
+++ b/crates/libeditor/src/scope/mod.rs
@@ -1,3 +1,8 @@
1mod fn_scope; 1mod fn_scope;
2mod mod_scope;
3
4pub use self::{
5 fn_scope::FnScopes,
6 mod_scope::ModuleScope,
7};
2 8
3pub 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 000000000..0e51108d9
--- /dev/null
+++ b/crates/libeditor/src/scope/mod_scope.rs
@@ -0,0 +1,47 @@
1use libsyntax2::{
2 AstNode, SyntaxNode, SmolStr, ast
3};
4
5pub struct ModuleScope {
6 entries: Vec<Entry>,
7}
8
9impl ModuleScope {
10 pub fn new(m: ast::Root) -> ModuleScope {
11 let entries = m.items().filter_map(|item| {
12 match item {
13 ast::ModuleItem::StructDef(item) => Entry::new(item),
14 ast::ModuleItem::EnumDef(item) => Entry::new(item),
15 ast::ModuleItem::FnDef(item) => Entry::new(item),
16 ast::ModuleItem::TraitDef(item) => Entry::new(item),
17 ast::ModuleItem::ExternCrateItem(_) |
18 ast::ModuleItem::ImplItem(_) |
19 ast::ModuleItem::UseItem(_) => None
20 }
21 }).collect();
22
23 ModuleScope { entries }
24 }
25
26 pub fn entries(&self) -> &[Entry] {
27 self.entries.as_slice()
28 }
29}
30
31pub struct Entry {
32 name: SyntaxNode,
33}
34
35impl Entry {
36 fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<Entry> {
37 let name = item.name()?;
38 Some(Entry { name: name.syntax().owned() })
39 }
40 pub fn name(&self) -> SmolStr {
41 self.ast().text()
42 }
43 fn ast(&self) -> ast::Name {
44 ast::Name::cast(self.name.borrowed()).unwrap()
45 }
46}
47