aboutsummaryrefslogtreecommitdiff
path: root/crates/libeditor/src/completion.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/libeditor/src/completion.rs')
-rw-r--r--crates/libeditor/src/completion.rs73
1 files changed, 55 insertions, 18 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 @@
1use libsyntax2::{ 1use 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
9use { 11use {
@@ -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
61fn complete_keywords(file: &File, fn_def: Option<ast::FnDef>, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) { 61fn 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 { 82fn 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
81fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<CompletionItem> { 101fn 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}