diff options
author | Aleksey Kladov <[email protected]> | 2018-08-30 19:32:12 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-08-30 19:32:12 +0100 |
commit | 8f552ab35222b7ea571f7ea9357db41489ae2247 (patch) | |
tree | af26565a1269276bf3026349b191be98ae5f3ea5 | |
parent | 80ab3433d3376b7c44787d63af6e7b3217ae41d8 (diff) |
break/continue completion
-rw-r--r-- | crates/libeditor/src/completion.rs | 73 | ||||
-rw-r--r-- | crates/libeditor/src/scope/fn_scope.rs | 8 | ||||
-rw-r--r-- | crates/libsyntax2/src/ast/generated.rs | 17 | ||||
-rw-r--r-- | crates/libsyntax2/src/ast/mod.rs | 6 | ||||
-rw-r--r-- | crates/libsyntax2/src/grammar.ron | 6 |
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 @@ | |||
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 | } |
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 | ||
596 | impl<'a> ast::LoopBodyOwner<'a> for ForExpr<'a> {} | ||
596 | impl<'a> ForExpr<'a> { | 597 | impl<'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 | ||
848 | impl<'a> LoopExpr<'a> { | 845 | impl<'a> ast::LoopBodyOwner<'a> for LoopExpr<'a> {} |
849 | pub fn body(self) -> Option<Block<'a>> { | 846 | impl<'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 | ||
2103 | impl<'a> ast::LoopBodyOwner<'a> for WhileExpr<'a> {} | ||
2109 | impl<'a> WhileExpr<'a> { | 2104 | impl<'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 | ||
23 | pub trait LoopBodyOwner<'a>: AstNode<'a> { | ||
24 | fn loop_body(self) -> Option<Block<'a>> { | ||
25 | child_opt(self) | ||
26 | } | ||
27 | } | ||
28 | |||
23 | pub trait TypeParamsOwner<'a>: AstNode<'a> { | 29 | pub 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": (), |