diff options
Diffstat (limited to 'crates/libeditor')
-rw-r--r-- | crates/libeditor/src/completion.rs | 178 | ||||
-rw-r--r-- | crates/libeditor/tests/test.rs | 3 |
2 files changed, 111 insertions, 70 deletions
diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs index 351781ec4..6335dba17 100644 --- a/crates/libeditor/src/completion.rs +++ b/crates/libeditor/src/completion.rs | |||
@@ -7,6 +7,7 @@ use libsyntax2::{ | |||
7 | ancestors, | 7 | ancestors, |
8 | visit::{visitor_ctx, VisitorCtx}, | 8 | visit::{visitor_ctx, VisitorCtx}, |
9 | walk::preorder, | 9 | walk::preorder, |
10 | generate, | ||
10 | }, | 11 | }, |
11 | }; | 12 | }; |
12 | 13 | ||
@@ -27,86 +28,125 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionI | |||
27 | file.incremental_reparse(&edit)? | 28 | file.incremental_reparse(&edit)? |
28 | }; | 29 | }; |
29 | let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), offset)?; | 30 | let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), offset)?; |
30 | Some(complete(name_ref)) | 31 | let fn_def = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next()?; |
32 | let scopes = compute_scopes(fn_def); | ||
33 | Some(complete(name_ref, &scopes)) | ||
31 | } | 34 | } |
32 | 35 | ||
33 | fn complete(name_ref: ast::NameRef) -> Vec<CompletionItem> { | 36 | fn complete(name_ref: ast::NameRef, scopes: &FnScopes) -> Vec<CompletionItem> { |
34 | let mut res = Vec::new(); | 37 | scopes.scope_chain(name_ref.syntax()) |
35 | for node in ancestors(name_ref.syntax()) { | 38 | .flat_map(|scope| scopes.entries(scope).iter()) |
36 | process_scope(node, &mut res); | 39 | .map(|entry| CompletionItem { |
37 | } | 40 | name: entry.name().to_string() |
38 | res | ||
39 | } | ||
40 | |||
41 | fn process_scope(node: SyntaxNodeRef, sink: &mut Vec<CompletionItem>) { | ||
42 | let _ = visitor_ctx(sink) | ||
43 | .visit::<ast::Block, _>(|block, sink| { | ||
44 | block.let_stmts() | ||
45 | .filter_map(|it| it.pat()) | ||
46 | .for_each(move |it| process_pat(it, sink)) | ||
47 | }) | ||
48 | .visit::<ast::FnDef, _>(|fn_def, sink| { | ||
49 | fn_def.param_list().into_iter() | ||
50 | .flat_map(|it| it.params()) | ||
51 | .filter_map(|it| it.pat()) | ||
52 | .for_each(move |it| process_pat(it, sink)) | ||
53 | }) | 41 | }) |
54 | .accept(node); | 42 | .collect() |
43 | } | ||
55 | 44 | ||
56 | fn process_pat(pat: ast::Pat, sink: &mut Vec<CompletionItem>) { | 45 | fn compute_scopes(fn_def: ast::FnDef) -> FnScopes { |
57 | let items = preorder(pat.syntax()) | 46 | let mut scopes = FnScopes::new(); |
58 | .filter_map(ast::BindPat::cast) | 47 | let root = scopes.root_scope(); |
59 | .filter_map(ast::BindPat::name) | 48 | fn_def.param_list().into_iter() |
60 | .map(|name| CompletionItem { name: name.text().to_string() }); | 49 | .flat_map(|it| it.params()) |
61 | sink.extend(items); | 50 | .filter_map(|it| it.pat()) |
51 | .for_each(|it| scopes.add_bindings(root, it)); | ||
52 | |||
53 | let mut scope = root; | ||
54 | if let Some(body) = fn_def.body() { | ||
55 | for child in body.syntax().children() { | ||
56 | let _ = visitor_ctx((&mut scopes, &mut scope)) | ||
57 | .visit::<ast::LetStmt, _>(|stmt, (scopes, scope)| { | ||
58 | *scope = scopes.new_scope(*scope); | ||
59 | if let Some(pat) = stmt.pat() { | ||
60 | scopes.add_bindings(*scope, pat); | ||
61 | } | ||
62 | if let Some(expr) = stmt.initializer() { | ||
63 | scopes.set_scope(expr.syntax(), *scope) | ||
64 | } | ||
65 | }) | ||
66 | .visit::<ast::ExprStmt, _>(|expr, (scopes, scope)| { | ||
67 | scopes.set_scope(expr.syntax(), *scope) | ||
68 | }) | ||
69 | .visit::<ast::Expr, _>(|expr, (scopes, scope)| { | ||
70 | scopes.set_scope(expr.syntax(), *scope) | ||
71 | }) | ||
72 | .accept(child); | ||
73 | } | ||
62 | } | 74 | } |
75 | scopes | ||
63 | } | 76 | } |
64 | 77 | ||
65 | // fn compute_scopes(fn_def: ast::FnDef) -> FnScopes { | 78 | type ScopeId = usize; |
66 | // let mut scopes = FnScopes::new(); | ||
67 | // } | ||
68 | |||
69 | // type ScopeId = usize; | ||
70 | 79 | ||
71 | // struct FnScopes { | 80 | struct FnScopes { |
72 | // scopes: Vec<ScopeData>, | 81 | scopes: Vec<ScopeData>, |
73 | // scope_for_expr: HashMap<SyntaxNode, ScopeId>, | 82 | scope_for: HashMap<SyntaxNode, ScopeId>, |
74 | // } | 83 | } |
75 | |||
76 | // impl FnScopes { | ||
77 | // fn new() -> FnScopes { | ||
78 | // FnScopes { | ||
79 | // scopes: vec![], | ||
80 | // scope_for_expr: HashMap::new(), | ||
81 | // } | ||
82 | // } | ||
83 | 84 | ||
84 | // fn new_scope(&mut Self) -> ScopeId { | 85 | impl FnScopes { |
85 | // let res = self.scopes.len(); | 86 | fn new() -> FnScopes { |
86 | // self.scopes.push(ScopeData { parent: None, entries: vec![] }) | 87 | FnScopes { |
87 | // } | 88 | scopes: vec![], |
89 | scope_for: HashMap::new(), | ||
90 | } | ||
91 | } | ||
92 | fn root_scope(&mut self) -> ScopeId { | ||
93 | let res = self.scopes.len(); | ||
94 | self.scopes.push(ScopeData { parent: None, entries: vec![] }); | ||
95 | res | ||
96 | } | ||
97 | fn new_scope(&mut self, parent: ScopeId) -> ScopeId { | ||
98 | let res = self.scopes.len(); | ||
99 | self.scopes.push(ScopeData { parent: Some(parent), entries: vec![] }); | ||
100 | res | ||
101 | } | ||
102 | fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) { | ||
103 | let entries = preorder(pat.syntax()) | ||
104 | .filter_map(ast::BindPat::cast) | ||
105 | .filter_map(ScopeEntry::new); | ||
106 | self.scopes[scope].entries.extend(entries); | ||
107 | } | ||
108 | fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) { | ||
109 | self.scope_for.insert(node.owned(), scope); | ||
110 | } | ||
111 | fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { | ||
112 | &self.scopes[scope].entries | ||
113 | } | ||
114 | fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> { | ||
115 | ancestors(node) | ||
116 | .filter_map(|it| self.scope_for.get(&it.owned()).map(|&scope| scope)) | ||
117 | .next() | ||
118 | } | ||
119 | fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item=ScopeId> + 'a { | ||
120 | generate(self.scope_for(node), move |&scope| self.scopes[scope].parent) | ||
121 | } | ||
122 | } | ||
88 | 123 | ||
89 | // fn set_parent | 124 | struct ScopeData { |
90 | // } | 125 | parent: Option<ScopeId>, |
126 | entries: Vec<ScopeEntry> | ||
127 | } | ||
91 | 128 | ||
92 | // struct ScopeData { | 129 | struct ScopeEntry { |
93 | // parent: Option<ScopeId>, | 130 | syntax: SyntaxNode |
94 | // entries: Vec<ScopeEntry> | 131 | } |
95 | // } | ||
96 | 132 | ||
97 | // struct ScopeEntry { | 133 | impl ScopeEntry { |
98 | // syntax: SyntaxNode | 134 | fn new(pat: ast::BindPat) -> Option<ScopeEntry> { |
99 | // } | 135 | if pat.name().is_some() { |
136 | Some(ScopeEntry { syntax: pat.syntax().owned() }) | ||
137 | } else { | ||
138 | None | ||
139 | } | ||
140 | } | ||
100 | 141 | ||
101 | // impl ScopeEntry { | 142 | fn name(&self) -> SmolStr { |
102 | // fn name(&self) -> SmolStr { | 143 | self.ast().name() |
103 | // self.ast().name() | 144 | .unwrap() |
104 | // .unwrap() | 145 | .text() |
105 | // .text() | 146 | } |
106 | // } | ||
107 | 147 | ||
108 | // fn ast(&self) -> ast::BindPat { | 148 | fn ast(&self) -> ast::BindPat { |
109 | // ast::BindPat::cast(self.syntax.borrowed()) | 149 | ast::BindPat::cast(self.syntax.borrowed()) |
110 | // .unwrap() | 150 | .unwrap() |
111 | // } | 151 | } |
112 | // } | 152 | } |
diff --git a/crates/libeditor/tests/test.rs b/crates/libeditor/tests/test.rs index ecdc149c7..7979bfffe 100644 --- a/crates/libeditor/tests/test.rs +++ b/crates/libeditor/tests/test.rs | |||
@@ -268,7 +268,8 @@ fn test_completion() { | |||
268 | do_check(r" | 268 | do_check(r" |
269 | fn quux(x: i32) { | 269 | fn quux(x: i32) { |
270 | let y = 92; | 270 | let y = 92; |
271 | 1 + <|> | 271 | 1 + <|>; |
272 | let z = (); | ||
272 | } | 273 | } |
273 | ", r#"[CompletionItem { name: "y" }, | 274 | ", r#"[CompletionItem { name: "y" }, |
274 | CompletionItem { name: "x" }]"#); | 275 | CompletionItem { name: "x" }]"#); |