diff options
author | Aleksey Kladov <[email protected]> | 2018-08-27 10:22:09 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-08-27 10:22:09 +0100 |
commit | 07cbb7d73deed8dac3eecdbdc7e1eaf6938a6cd6 (patch) | |
tree | 4a1fa22fa8c908f0c3c9489a98aa2479f05def59 /crates/libeditor | |
parent | c16530c988e817c5596fa38ebe9e12a302886a8f (diff) |
Support if-let in scopes
Diffstat (limited to 'crates/libeditor')
-rw-r--r-- | crates/libeditor/src/completion.rs | 101 | ||||
-rw-r--r-- | crates/libeditor/tests/test.rs | 13 |
2 files changed, 98 insertions, 16 deletions
diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs index ec9388ee3..69c039e83 100644 --- a/crates/libeditor/src/completion.rs +++ b/crates/libeditor/src/completion.rs | |||
@@ -1,4 +1,7 @@ | |||
1 | use std::collections::HashMap; | 1 | use std::{ |
2 | fmt, | ||
3 | collections::HashMap, | ||
4 | }; | ||
2 | 5 | ||
3 | use libsyntax2::{ | 6 | use libsyntax2::{ |
4 | File, TextUnit, AstNode, SyntaxNodeRef, SyntaxNode, SmolStr, | 7 | File, TextUnit, AstNode, SyntaxNodeRef, SyntaxNode, SmolStr, |
@@ -49,33 +52,89 @@ fn compute_scopes(fn_def: ast::FnDef) -> FnScopes { | |||
49 | .filter_map(|it| it.pat()) | 52 | .filter_map(|it| it.pat()) |
50 | .for_each(|it| scopes.add_bindings(root, it)); | 53 | .for_each(|it| scopes.add_bindings(root, it)); |
51 | 54 | ||
52 | let mut scope = root; | ||
53 | if let Some(body) = fn_def.body() { | 55 | if let Some(body) = fn_def.body() { |
54 | for stmt in body.statements() { | 56 | compute_block_scopes(body, &mut scopes, root) |
55 | match stmt { | 57 | } |
56 | ast::Stmt::LetStmt(stmt) => { | 58 | scopes |
57 | scope = scopes.new_scope(scope); | 59 | } |
58 | if let Some(pat) = stmt.pat() { | 60 | |
59 | scopes.add_bindings(scope, pat); | 61 | fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) { |
60 | } | 62 | for stmt in block.statements() { |
61 | if let Some(expr) = stmt.initializer() { | 63 | match stmt { |
62 | scopes.set_scope(expr.syntax(), scope) | 64 | ast::Stmt::LetStmt(stmt) => { |
63 | } | 65 | scope = scopes.new_scope(scope); |
66 | if let Some(pat) = stmt.pat() { | ||
67 | scopes.add_bindings(scope, pat); | ||
64 | } | 68 | } |
65 | ast::Stmt::ExprStmt(expr) => { | 69 | if let Some(expr) = stmt.initializer() { |
66 | scopes.set_scope(expr.syntax(), scope) | 70 | scopes.set_scope(expr.syntax(), scope) |
67 | } | 71 | } |
68 | } | 72 | } |
73 | ast::Stmt::ExprStmt(expr_stmt) => { | ||
74 | if let Some(expr) = expr_stmt.expr() { | ||
75 | scopes.set_scope(expr.syntax(), scope); | ||
76 | compute_expr_scopes(expr, scopes, scope); | ||
77 | } | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | if let Some(expr) = block.expr() { | ||
82 | scopes.set_scope(expr.syntax(), scope); | ||
83 | compute_expr_scopes(expr, scopes, scope); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | ||
88 | match expr { | ||
89 | ast::Expr::IfExpr(e) => { | ||
90 | let cond_scope = e.condition().and_then(|cond| { | ||
91 | compute_cond_scopes(cond, scopes, scope) | ||
92 | }); | ||
93 | if let Some(block) = e.then_branch() { | ||
94 | compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); | ||
95 | } | ||
96 | if let Some(block) = e.else_branch() { | ||
97 | compute_block_scopes(block, scopes, scope); | ||
98 | } | ||
99 | }, | ||
100 | ast::Expr::WhileExpr(e) => { | ||
101 | let cond_scope = e.condition().and_then(|cond| { | ||
102 | compute_cond_scopes(cond, scopes, scope) | ||
103 | }); | ||
104 | if let Some(block) = e.body() { | ||
105 | compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); | ||
106 | } | ||
107 | }, | ||
108 | ast::Expr::BlockExpr(e) => { | ||
109 | if let Some(block) = e.block() { | ||
110 | compute_block_scopes(block, scopes, scope); | ||
111 | } | ||
69 | } | 112 | } |
70 | if let Some(expr) = body.expr() { | 113 | // ForExpr(e) => TODO, |
71 | scopes.set_scope(expr.syntax(), scope) | 114 | _ => { |
115 | expr.syntax().children() | ||
116 | .filter_map(ast::Expr::cast) | ||
117 | .for_each(|expr| compute_expr_scopes(expr, scopes, scope)) | ||
118 | } | ||
119 | }; | ||
120 | |||
121 | fn compute_cond_scopes(cond: ast::Condition, scopes: &mut FnScopes, scope: ScopeId) -> Option<ScopeId> { | ||
122 | if let Some(expr) = cond.expr() { | ||
123 | compute_expr_scopes(expr, scopes, scope); | ||
124 | } | ||
125 | if let Some(pat) = cond.pat() { | ||
126 | let s = scopes.new_scope(scope); | ||
127 | scopes.add_bindings(s, pat); | ||
128 | Some(s) | ||
129 | } else { | ||
130 | None | ||
72 | } | 131 | } |
73 | } | 132 | } |
74 | scopes | ||
75 | } | 133 | } |
76 | 134 | ||
77 | type ScopeId = usize; | 135 | type ScopeId = usize; |
78 | 136 | ||
137 | #[derive(Debug)] | ||
79 | struct FnScopes { | 138 | struct FnScopes { |
80 | scopes: Vec<ScopeData>, | 139 | scopes: Vec<ScopeData>, |
81 | scope_for: HashMap<SyntaxNode, ScopeId>, | 140 | scope_for: HashMap<SyntaxNode, ScopeId>, |
@@ -120,6 +179,7 @@ impl FnScopes { | |||
120 | } | 179 | } |
121 | } | 180 | } |
122 | 181 | ||
182 | #[derive(Debug)] | ||
123 | struct ScopeData { | 183 | struct ScopeData { |
124 | parent: Option<ScopeId>, | 184 | parent: Option<ScopeId>, |
125 | entries: Vec<ScopeEntry> | 185 | entries: Vec<ScopeEntry> |
@@ -149,3 +209,12 @@ impl ScopeEntry { | |||
149 | .unwrap() | 209 | .unwrap() |
150 | } | 210 | } |
151 | } | 211 | } |
212 | |||
213 | impl fmt::Debug for ScopeEntry { | ||
214 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
215 | f.debug_struct("ScopeEntry") | ||
216 | .field("name", &self.name()) | ||
217 | .field("syntax", &self.syntax) | ||
218 | .finish() | ||
219 | } | ||
220 | } | ||
diff --git a/crates/libeditor/tests/test.rs b/crates/libeditor/tests/test.rs index 7979bfffe..d051980b0 100644 --- a/crates/libeditor/tests/test.rs +++ b/crates/libeditor/tests/test.rs | |||
@@ -273,6 +273,19 @@ fn quux(x: i32) { | |||
273 | } | 273 | } |
274 | ", r#"[CompletionItem { name: "y" }, | 274 | ", r#"[CompletionItem { name: "y" }, |
275 | CompletionItem { name: "x" }]"#); | 275 | CompletionItem { name: "x" }]"#); |
276 | |||
277 | do_check(r" | ||
278 | fn quux() { | ||
279 | if let Some(x) = foo() { | ||
280 | let y = 92; | ||
281 | }; | ||
282 | if let Some(a) = bar() { | ||
283 | let b = 62; | ||
284 | 1 + <|> | ||
285 | } | ||
286 | } | ||
287 | ", r#"[CompletionItem { name: "b" }, | ||
288 | CompletionItem { name: "a" }]"#); | ||
276 | } | 289 | } |
277 | 290 | ||
278 | fn file(text: &str) -> File { | 291 | fn file(text: &str) -> File { |