aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-08-30 19:32:12 +0100
committerAleksey Kladov <[email protected]>2018-08-30 19:32:12 +0100
commit8f552ab35222b7ea571f7ea9357db41489ae2247 (patch)
treeaf26565a1269276bf3026349b191be98ae5f3ea5
parent80ab3433d3376b7c44787d63af6e7b3217ae41d8 (diff)
break/continue completion
-rw-r--r--crates/libeditor/src/completion.rs73
-rw-r--r--crates/libeditor/src/scope/fn_scope.rs8
-rw-r--r--crates/libsyntax2/src/ast/generated.rs17
-rw-r--r--crates/libsyntax2/src/ast/mod.rs6
-rw-r--r--crates/libsyntax2/src/grammar.ron6
5 files changed, 72 insertions, 38 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}
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
6use libsyntax2::{ 6use 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 }
diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs
index 1bb3b11d1..0668dbfa7 100644
--- a/crates/libsyntax2/src/ast/generated.rs
+++ b/crates/libsyntax2/src/ast/generated.rs
@@ -593,6 +593,7 @@ impl<'a> AstNode<'a> for ForExpr<'a> {
593 fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } 593 fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
594} 594}
595 595
596impl<'a> ast::LoopBodyOwner<'a> for ForExpr<'a> {}
596impl<'a> ForExpr<'a> { 597impl<'a> ForExpr<'a> {
597 pub fn pat(self) -> Option<Pat<'a>> { 598 pub fn pat(self) -> Option<Pat<'a>> {
598 super::child_opt(self) 599 super::child_opt(self)
@@ -601,10 +602,6 @@ impl<'a> ForExpr<'a> {
601 pub fn iterable(self) -> Option<Expr<'a>> { 602 pub fn iterable(self) -> Option<Expr<'a>> {
602 super::child_opt(self) 603 super::child_opt(self)
603 } 604 }
604
605 pub fn body(self) -> Option<Block<'a>> {
606 super::child_opt(self)
607 }
608} 605}
609 606
610// ForType 607// ForType
@@ -845,11 +842,8 @@ impl<'a> AstNode<'a> for LoopExpr<'a> {
845 fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } 842 fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
846} 843}
847 844
848impl<'a> LoopExpr<'a> { 845impl<'a> ast::LoopBodyOwner<'a> for LoopExpr<'a> {}
849 pub fn body(self) -> Option<Block<'a>> { 846impl<'a> LoopExpr<'a> {}
850 super::child_opt(self)
851 }
852}
853 847
854// MatchArm 848// MatchArm
855#[derive(Debug, Clone, Copy)] 849#[derive(Debug, Clone, Copy)]
@@ -2106,13 +2100,10 @@ impl<'a> AstNode<'a> for WhileExpr<'a> {
2106 fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } 2100 fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
2107} 2101}
2108 2102
2103impl<'a> ast::LoopBodyOwner<'a> for WhileExpr<'a> {}
2109impl<'a> WhileExpr<'a> { 2104impl<'a> WhileExpr<'a> {
2110 pub fn condition(self) -> Option<Condition<'a>> { 2105 pub fn condition(self) -> Option<Condition<'a>> {
2111 super::child_opt(self) 2106 super::child_opt(self)
2112 } 2107 }
2113
2114 pub fn body(self) -> Option<Block<'a>> {
2115 super::child_opt(self)
2116 }
2117} 2108}
2118 2109
diff --git a/crates/libsyntax2/src/ast/mod.rs b/crates/libsyntax2/src/ast/mod.rs
index 3b5e9269f..49e283f5e 100644
--- a/crates/libsyntax2/src/ast/mod.rs
+++ b/crates/libsyntax2/src/ast/mod.rs
@@ -20,6 +20,12 @@ pub trait NameOwner<'a>: AstNode<'a> {
20 } 20 }
21} 21}
22 22
23pub trait LoopBodyOwner<'a>: AstNode<'a> {
24 fn loop_body(self) -> Option<Block<'a>> {
25 child_opt(self)
26 }
27}
28
23pub trait TypeParamsOwner<'a>: AstNode<'a> { 29pub trait TypeParamsOwner<'a>: AstNode<'a> {
24 fn type_param_list(self) -> Option<TypeParamList<'a>> { 30 fn type_param_list(self) -> Option<TypeParamList<'a>> {
25 child_opt(self) 31 child_opt(self)
diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron
index 8267c2854..da18da8a9 100644
--- a/crates/libsyntax2/src/grammar.ron
+++ b/crates/libsyntax2/src/grammar.ron
@@ -354,19 +354,19 @@ Grammar(
354 options: [ ["condition", "Condition"] ] 354 options: [ ["condition", "Condition"] ]
355 ), 355 ),
356 "LoopExpr": ( 356 "LoopExpr": (
357 options: [ ["body", "Block"] ] 357 traits: ["LoopBodyOwner"],
358 ), 358 ),
359 "ForExpr": ( 359 "ForExpr": (
360 traits: ["LoopBodyOwner"],
360 options: [ 361 options: [
361 ["pat", "Pat"], 362 ["pat", "Pat"],
362 ["iterable", "Expr"], 363 ["iterable", "Expr"],
363 ["body", "Block"] ,
364 ] 364 ]
365 ), 365 ),
366 "WhileExpr": ( 366 "WhileExpr": (
367 traits: ["LoopBodyOwner"],
367 options: [ 368 options: [
368 ["condition", "Condition"], 369 ["condition", "Condition"],
369 ["body", "Block"],
370 ] 370 ]
371 ), 371 ),
372 "ContinueExpr": (), 372 "ContinueExpr": (),