aboutsummaryrefslogtreecommitdiff
path: root/crates/libeditor
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-08-27 10:22:09 +0100
committerAleksey Kladov <[email protected]>2018-08-27 10:22:09 +0100
commit07cbb7d73deed8dac3eecdbdc7e1eaf6938a6cd6 (patch)
tree4a1fa22fa8c908f0c3c9489a98aa2479f05def59 /crates/libeditor
parentc16530c988e817c5596fa38ebe9e12a302886a8f (diff)
Support if-let in scopes
Diffstat (limited to 'crates/libeditor')
-rw-r--r--crates/libeditor/src/completion.rs101
-rw-r--r--crates/libeditor/tests/test.rs13
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 @@
1use std::collections::HashMap; 1use std::{
2 fmt,
3 collections::HashMap,
4};
2 5
3use libsyntax2::{ 6use 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); 61fn 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
87fn 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
77type ScopeId = usize; 135type ScopeId = usize;
78 136
137#[derive(Debug)]
79struct FnScopes { 138struct 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)]
123struct ScopeData { 183struct 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
213impl 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"
278fn 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
278fn file(text: &str) -> File { 291fn file(text: &str) -> File {