diff options
Diffstat (limited to 'crates/libeditor/src')
-rw-r--r-- | crates/libeditor/src/completion.rs | 73 | ||||
-rw-r--r-- | crates/libeditor/src/scope/fn_scope.rs | 8 |
2 files changed, 59 insertions, 22 deletions
diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs index f733cd2b2..2b6a1db86 100644 --- a/crates/libeditor/src/completion.rs +++ b/crates/libeditor/src/completion.rs | |||
@@ -1,9 +1,11 @@ | |||
1 | use libsyntax2::{ | 1 | use libsyntax2::{ |
2 | File, TextUnit, AstNode, | 2 | File, TextUnit, AstNode, SyntaxKind::*, |
3 | ast::self, | 3 | ast::{self, LoopBodyOwner}, |
4 | algo::{ | 4 | algo::{ |
5 | ancestors, | 5 | ancestors, |
6 | visit::{visitor, Visitor}, | ||
6 | }, | 7 | }, |
8 | text_utils::is_subrange, | ||
7 | }; | 9 | }; |
8 | 10 | ||
9 | use { | 11 | use { |
@@ -30,11 +32,9 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionI | |||
30 | 32 | ||
31 | let mut res = Vec::new(); | 33 | let mut res = Vec::new(); |
32 | if let Some(fn_def) = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next() { | 34 | if let Some(fn_def) = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next() { |
33 | complete_keywords(&file, Some(fn_def), name_ref, &mut res); | 35 | complete_expr_keywords(&file, fn_def, name_ref, &mut res); |
34 | let scopes = FnScopes::new(fn_def); | 36 | let scopes = FnScopes::new(fn_def); |
35 | complete_fn(name_ref, &scopes, &mut res); | 37 | complete_fn(name_ref, &scopes, &mut res); |
36 | } else { | ||
37 | complete_keywords(&file, None, name_ref, &mut res); | ||
38 | } | 38 | } |
39 | if let Some(root) = ancestors(name_ref.syntax()).filter_map(ast::Root::cast).next() { | 39 | if let Some(root) = ancestors(name_ref.syntax()).filter_map(ast::Root::cast).next() { |
40 | let scope = ModuleScope::new(root); | 40 | let scope = ModuleScope::new(root); |
@@ -58,7 +58,7 @@ fn is_single_segment(name_ref: ast::NameRef) -> bool { | |||
58 | } | 58 | } |
59 | } | 59 | } |
60 | 60 | ||
61 | fn complete_keywords(file: &File, fn_def: Option<ast::FnDef>, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) { | 61 | fn complete_expr_keywords(file: &File, fn_def: ast::FnDef, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) { |
62 | acc.push(keyword("if", "if $0 { }")); | 62 | acc.push(keyword("if", "if $0 { }")); |
63 | acc.push(keyword("match", "match $0 { }")); | 63 | acc.push(keyword("match", "match $0 { }")); |
64 | acc.push(keyword("while", "while $0 { }")); | 64 | acc.push(keyword("while", "while $0 { }")); |
@@ -72,22 +72,42 @@ fn complete_keywords(file: &File, fn_def: Option<ast::FnDef>, name_ref: ast::Nam | |||
72 | } | 72 | } |
73 | } | 73 | } |
74 | } | 74 | } |
75 | if is_in_loop_body(name_ref) { | ||
76 | acc.push(keyword("continue", "continue")); | ||
77 | acc.push(keyword("break", "break")); | ||
78 | } | ||
79 | acc.extend(complete_return(fn_def, name_ref)); | ||
80 | } | ||
75 | 81 | ||
76 | if let Some(fn_def) = fn_def { | 82 | fn is_in_loop_body(name_ref: ast::NameRef) -> bool { |
77 | acc.extend(complete_return(fn_def, name_ref)); | 83 | for node in ancestors(name_ref.syntax()) { |
84 | if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { | ||
85 | break; | ||
86 | } | ||
87 | let loop_body = visitor() | ||
88 | .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body) | ||
89 | .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body) | ||
90 | .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body) | ||
91 | .accept(node); | ||
92 | if let Some(Some(body)) = loop_body { | ||
93 | if is_subrange(body.syntax().range(), name_ref.syntax().range()) { | ||
94 | return true; | ||
95 | } | ||
96 | } | ||
78 | } | 97 | } |
98 | false | ||
79 | } | 99 | } |
80 | 100 | ||
81 | fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<CompletionItem> { | 101 | fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<CompletionItem> { |
82 | let is_last_in_block = ancestors(name_ref.syntax()).filter_map(ast::Expr::cast) | 102 | // let is_last_in_block = ancestors(name_ref.syntax()).filter_map(ast::Expr::cast) |
83 | .next() | 103 | // .next() |
84 | .and_then(|it| it.syntax().parent()) | 104 | // .and_then(|it| it.syntax().parent()) |
85 | .and_then(ast::Block::cast) | 105 | // .and_then(ast::Block::cast) |
86 | .is_some(); | 106 | // .is_some(); |
87 | 107 | ||
88 | if is_last_in_block { | 108 | // if is_last_in_block { |
89 | return None; | 109 | // return None; |
90 | } | 110 | // } |
91 | 111 | ||
92 | let is_stmt = match ancestors(name_ref.syntax()).filter_map(ast::ExprStmt::cast).next() { | 112 | let is_stmt = match ancestors(name_ref.syntax()).filter_map(ast::ExprStmt::cast).next() { |
93 | None => false, | 113 | None => false, |
@@ -220,7 +240,8 @@ mod tests { | |||
220 | ", r#"[CompletionItem { name: "if", snippet: Some("if $0 { }") }, | 240 | ", r#"[CompletionItem { name: "if", snippet: Some("if $0 { }") }, |
221 | CompletionItem { name: "match", snippet: Some("match $0 { }") }, | 241 | CompletionItem { name: "match", snippet: Some("match $0 { }") }, |
222 | CompletionItem { name: "while", snippet: Some("while $0 { }") }, | 242 | CompletionItem { name: "while", snippet: Some("while $0 { }") }, |
223 | CompletionItem { name: "loop", snippet: Some("loop {$0}") }]"#); | 243 | CompletionItem { name: "loop", snippet: Some("loop {$0}") }, |
244 | CompletionItem { name: "return", snippet: Some("return") }]"#); | ||
224 | } | 245 | } |
225 | 246 | ||
226 | #[test] | 247 | #[test] |
@@ -236,7 +257,8 @@ mod tests { | |||
236 | CompletionItem { name: "while", snippet: Some("while $0 { }") }, | 257 | CompletionItem { name: "while", snippet: Some("while $0 { }") }, |
237 | CompletionItem { name: "loop", snippet: Some("loop {$0}") }, | 258 | CompletionItem { name: "loop", snippet: Some("loop {$0}") }, |
238 | CompletionItem { name: "else", snippet: Some("else {$0}") }, | 259 | CompletionItem { name: "else", snippet: Some("else {$0}") }, |
239 | CompletionItem { name: "else if", snippet: Some("else if $0 { }") }]"#); | 260 | CompletionItem { name: "else if", snippet: Some("else if $0 { }") }, |
261 | CompletionItem { name: "return", snippet: Some("return") }]"#); | ||
240 | } | 262 | } |
241 | 263 | ||
242 | #[test] | 264 | #[test] |
@@ -277,4 +299,19 @@ mod tests { | |||
277 | CompletionItem { name: "loop", snippet: Some("loop {$0}") }, | 299 | CompletionItem { name: "loop", snippet: Some("loop {$0}") }, |
278 | CompletionItem { name: "return", snippet: Some("return $0") }]"#); | 300 | CompletionItem { name: "return", snippet: Some("return $0") }]"#); |
279 | } | 301 | } |
302 | |||
303 | #[test] | ||
304 | fn test_continue_break_completion() { | ||
305 | check_snippet_completion(r" | ||
306 | fn quux() -> i32 { | ||
307 | loop { <|> } | ||
308 | } | ||
309 | ", r#"[CompletionItem { name: "if", snippet: Some("if $0 { }") }, | ||
310 | CompletionItem { name: "match", snippet: Some("match $0 { }") }, | ||
311 | CompletionItem { name: "while", snippet: Some("while $0 { }") }, | ||
312 | CompletionItem { name: "loop", snippet: Some("loop {$0}") }, | ||
313 | CompletionItem { name: "continue", snippet: Some("continue") }, | ||
314 | CompletionItem { name: "break", snippet: Some("break") }, | ||
315 | CompletionItem { name: "return", snippet: Some("return $0") }]"#); | ||
316 | } | ||
280 | } | 317 | } |
diff --git a/crates/libeditor/src/scope/fn_scope.rs b/crates/libeditor/src/scope/fn_scope.rs index de38b33f0..4b643237f 100644 --- a/crates/libeditor/src/scope/fn_scope.rs +++ b/crates/libeditor/src/scope/fn_scope.rs | |||
@@ -5,7 +5,7 @@ use std::{ | |||
5 | 5 | ||
6 | use libsyntax2::{ | 6 | use libsyntax2::{ |
7 | SyntaxNodeRef, SyntaxNode, SmolStr, AstNode, | 7 | SyntaxNodeRef, SyntaxNode, SmolStr, AstNode, |
8 | ast::{self, NameOwner}, | 8 | ast::{self, NameOwner, LoopBodyOwner}, |
9 | algo::{ancestors, generate, walk::preorder} | 9 | algo::{ancestors, generate, walk::preorder} |
10 | }; | 10 | }; |
11 | 11 | ||
@@ -144,7 +144,7 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | |||
144 | let cond_scope = e.condition().and_then(|cond| { | 144 | let cond_scope = e.condition().and_then(|cond| { |
145 | compute_cond_scopes(cond, scopes, scope) | 145 | compute_cond_scopes(cond, scopes, scope) |
146 | }); | 146 | }); |
147 | if let Some(block) = e.body() { | 147 | if let Some(block) = e.loop_body() { |
148 | compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); | 148 | compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); |
149 | } | 149 | } |
150 | }, | 150 | }, |
@@ -162,7 +162,7 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | |||
162 | scope = scopes.new_scope(scope); | 162 | scope = scopes.new_scope(scope); |
163 | scopes.add_bindings(scope, pat); | 163 | scopes.add_bindings(scope, pat); |
164 | } | 164 | } |
165 | if let Some(block) = e.body() { | 165 | if let Some(block) = e.loop_body() { |
166 | compute_block_scopes(block, scopes, scope); | 166 | compute_block_scopes(block, scopes, scope); |
167 | } | 167 | } |
168 | }, | 168 | }, |
@@ -181,7 +181,7 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | |||
181 | .for_each(|expr| compute_expr_scopes(expr, scopes, scope)); | 181 | .for_each(|expr| compute_expr_scopes(expr, scopes, scope)); |
182 | } | 182 | } |
183 | ast::Expr::LoopExpr(e) => { | 183 | ast::Expr::LoopExpr(e) => { |
184 | if let Some(block) = e.body() { | 184 | if let Some(block) = e.loop_body() { |
185 | compute_block_scopes(block, scopes, scope); | 185 | compute_block_scopes(block, scopes, scope); |
186 | } | 186 | } |
187 | } | 187 | } |