mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
break/continue completion
This commit is contained in:
parent
80ab3433d3
commit
8f552ab352
5 changed files with 73 additions and 39 deletions
|
@ -1,9 +1,11 @@
|
||||||
use libsyntax2::{
|
use libsyntax2::{
|
||||||
File, TextUnit, AstNode,
|
File, TextUnit, AstNode, SyntaxKind::*,
|
||||||
ast::self,
|
ast::{self, LoopBodyOwner},
|
||||||
algo::{
|
algo::{
|
||||||
ancestors,
|
ancestors,
|
||||||
|
visit::{visitor, Visitor},
|
||||||
},
|
},
|
||||||
|
text_utils::is_subrange,
|
||||||
};
|
};
|
||||||
|
|
||||||
use {
|
use {
|
||||||
|
@ -30,11 +32,9 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionI
|
||||||
|
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
if let Some(fn_def) = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next() {
|
if let Some(fn_def) = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next() {
|
||||||
complete_keywords(&file, Some(fn_def), name_ref, &mut res);
|
complete_expr_keywords(&file, fn_def, name_ref, &mut res);
|
||||||
let scopes = FnScopes::new(fn_def);
|
let scopes = FnScopes::new(fn_def);
|
||||||
complete_fn(name_ref, &scopes, &mut res);
|
complete_fn(name_ref, &scopes, &mut res);
|
||||||
} else {
|
|
||||||
complete_keywords(&file, None, name_ref, &mut res);
|
|
||||||
}
|
}
|
||||||
if let Some(root) = ancestors(name_ref.syntax()).filter_map(ast::Root::cast).next() {
|
if let Some(root) = ancestors(name_ref.syntax()).filter_map(ast::Root::cast).next() {
|
||||||
let scope = ModuleScope::new(root);
|
let scope = ModuleScope::new(root);
|
||||||
|
@ -58,7 +58,7 @@ fn is_single_segment(name_ref: ast::NameRef) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete_keywords(file: &File, fn_def: Option<ast::FnDef>, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) {
|
fn complete_expr_keywords(file: &File, fn_def: ast::FnDef, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) {
|
||||||
acc.push(keyword("if", "if $0 { }"));
|
acc.push(keyword("if", "if $0 { }"));
|
||||||
acc.push(keyword("match", "match $0 { }"));
|
acc.push(keyword("match", "match $0 { }"));
|
||||||
acc.push(keyword("while", "while $0 { }"));
|
acc.push(keyword("while", "while $0 { }"));
|
||||||
|
@ -72,22 +72,42 @@ fn complete_keywords(file: &File, fn_def: Option<ast::FnDef>, name_ref: ast::Nam
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if is_in_loop_body(name_ref) {
|
||||||
if let Some(fn_def) = fn_def {
|
acc.push(keyword("continue", "continue"));
|
||||||
acc.extend(complete_return(fn_def, name_ref));
|
acc.push(keyword("break", "break"));
|
||||||
}
|
}
|
||||||
|
acc.extend(complete_return(fn_def, name_ref));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_in_loop_body(name_ref: ast::NameRef) -> bool {
|
||||||
|
for node in ancestors(name_ref.syntax()) {
|
||||||
|
if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let loop_body = visitor()
|
||||||
|
.visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
|
||||||
|
.visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
|
||||||
|
.visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
|
||||||
|
.accept(node);
|
||||||
|
if let Some(Some(body)) = loop_body {
|
||||||
|
if is_subrange(body.syntax().range(), name_ref.syntax().range()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<CompletionItem> {
|
fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<CompletionItem> {
|
||||||
let is_last_in_block = ancestors(name_ref.syntax()).filter_map(ast::Expr::cast)
|
// let is_last_in_block = ancestors(name_ref.syntax()).filter_map(ast::Expr::cast)
|
||||||
.next()
|
// .next()
|
||||||
.and_then(|it| it.syntax().parent())
|
// .and_then(|it| it.syntax().parent())
|
||||||
.and_then(ast::Block::cast)
|
// .and_then(ast::Block::cast)
|
||||||
.is_some();
|
// .is_some();
|
||||||
|
|
||||||
if is_last_in_block {
|
// if is_last_in_block {
|
||||||
return None;
|
// return None;
|
||||||
}
|
// }
|
||||||
|
|
||||||
let is_stmt = match ancestors(name_ref.syntax()).filter_map(ast::ExprStmt::cast).next() {
|
let is_stmt = match ancestors(name_ref.syntax()).filter_map(ast::ExprStmt::cast).next() {
|
||||||
None => false,
|
None => false,
|
||||||
|
@ -220,7 +240,8 @@ mod tests {
|
||||||
", r#"[CompletionItem { name: "if", snippet: Some("if $0 { }") },
|
", r#"[CompletionItem { name: "if", snippet: Some("if $0 { }") },
|
||||||
CompletionItem { name: "match", snippet: Some("match $0 { }") },
|
CompletionItem { name: "match", snippet: Some("match $0 { }") },
|
||||||
CompletionItem { name: "while", snippet: Some("while $0 { }") },
|
CompletionItem { name: "while", snippet: Some("while $0 { }") },
|
||||||
CompletionItem { name: "loop", snippet: Some("loop {$0}") }]"#);
|
CompletionItem { name: "loop", snippet: Some("loop {$0}") },
|
||||||
|
CompletionItem { name: "return", snippet: Some("return") }]"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -236,7 +257,8 @@ mod tests {
|
||||||
CompletionItem { name: "while", snippet: Some("while $0 { }") },
|
CompletionItem { name: "while", snippet: Some("while $0 { }") },
|
||||||
CompletionItem { name: "loop", snippet: Some("loop {$0}") },
|
CompletionItem { name: "loop", snippet: Some("loop {$0}") },
|
||||||
CompletionItem { name: "else", snippet: Some("else {$0}") },
|
CompletionItem { name: "else", snippet: Some("else {$0}") },
|
||||||
CompletionItem { name: "else if", snippet: Some("else if $0 { }") }]"#);
|
CompletionItem { name: "else if", snippet: Some("else if $0 { }") },
|
||||||
|
CompletionItem { name: "return", snippet: Some("return") }]"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -277,4 +299,19 @@ mod tests {
|
||||||
CompletionItem { name: "loop", snippet: Some("loop {$0}") },
|
CompletionItem { name: "loop", snippet: Some("loop {$0}") },
|
||||||
CompletionItem { name: "return", snippet: Some("return $0") }]"#);
|
CompletionItem { name: "return", snippet: Some("return $0") }]"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_continue_break_completion() {
|
||||||
|
check_snippet_completion(r"
|
||||||
|
fn quux() -> i32 {
|
||||||
|
loop { <|> }
|
||||||
|
}
|
||||||
|
", r#"[CompletionItem { name: "if", snippet: Some("if $0 { }") },
|
||||||
|
CompletionItem { name: "match", snippet: Some("match $0 { }") },
|
||||||
|
CompletionItem { name: "while", snippet: Some("while $0 { }") },
|
||||||
|
CompletionItem { name: "loop", snippet: Some("loop {$0}") },
|
||||||
|
CompletionItem { name: "continue", snippet: Some("continue") },
|
||||||
|
CompletionItem { name: "break", snippet: Some("break") },
|
||||||
|
CompletionItem { name: "return", snippet: Some("return $0") }]"#);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
||||||
|
|
||||||
use libsyntax2::{
|
use libsyntax2::{
|
||||||
SyntaxNodeRef, SyntaxNode, SmolStr, AstNode,
|
SyntaxNodeRef, SyntaxNode, SmolStr, AstNode,
|
||||||
ast::{self, NameOwner},
|
ast::{self, NameOwner, LoopBodyOwner},
|
||||||
algo::{ancestors, generate, walk::preorder}
|
algo::{ancestors, generate, walk::preorder}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
|
||||||
let cond_scope = e.condition().and_then(|cond| {
|
let cond_scope = e.condition().and_then(|cond| {
|
||||||
compute_cond_scopes(cond, scopes, scope)
|
compute_cond_scopes(cond, scopes, scope)
|
||||||
});
|
});
|
||||||
if let Some(block) = e.body() {
|
if let Some(block) = e.loop_body() {
|
||||||
compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
|
compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -162,7 +162,7 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
|
||||||
scope = scopes.new_scope(scope);
|
scope = scopes.new_scope(scope);
|
||||||
scopes.add_bindings(scope, pat);
|
scopes.add_bindings(scope, pat);
|
||||||
}
|
}
|
||||||
if let Some(block) = e.body() {
|
if let Some(block) = e.loop_body() {
|
||||||
compute_block_scopes(block, scopes, scope);
|
compute_block_scopes(block, scopes, scope);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -181,7 +181,7 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
|
||||||
.for_each(|expr| compute_expr_scopes(expr, scopes, scope));
|
.for_each(|expr| compute_expr_scopes(expr, scopes, scope));
|
||||||
}
|
}
|
||||||
ast::Expr::LoopExpr(e) => {
|
ast::Expr::LoopExpr(e) => {
|
||||||
if let Some(block) = e.body() {
|
if let Some(block) = e.loop_body() {
|
||||||
compute_block_scopes(block, scopes, scope);
|
compute_block_scopes(block, scopes, scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -593,6 +593,7 @@ impl<'a> AstNode<'a> for ForExpr<'a> {
|
||||||
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
|
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> ast::LoopBodyOwner<'a> for ForExpr<'a> {}
|
||||||
impl<'a> ForExpr<'a> {
|
impl<'a> ForExpr<'a> {
|
||||||
pub fn pat(self) -> Option<Pat<'a>> {
|
pub fn pat(self) -> Option<Pat<'a>> {
|
||||||
super::child_opt(self)
|
super::child_opt(self)
|
||||||
|
@ -601,10 +602,6 @@ impl<'a> ForExpr<'a> {
|
||||||
pub fn iterable(self) -> Option<Expr<'a>> {
|
pub fn iterable(self) -> Option<Expr<'a>> {
|
||||||
super::child_opt(self)
|
super::child_opt(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn body(self) -> Option<Block<'a>> {
|
|
||||||
super::child_opt(self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForType
|
// ForType
|
||||||
|
@ -845,11 +842,8 @@ impl<'a> AstNode<'a> for LoopExpr<'a> {
|
||||||
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
|
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LoopExpr<'a> {
|
impl<'a> ast::LoopBodyOwner<'a> for LoopExpr<'a> {}
|
||||||
pub fn body(self) -> Option<Block<'a>> {
|
impl<'a> LoopExpr<'a> {}
|
||||||
super::child_opt(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MatchArm
|
// MatchArm
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -2106,13 +2100,10 @@ impl<'a> AstNode<'a> for WhileExpr<'a> {
|
||||||
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
|
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> ast::LoopBodyOwner<'a> for WhileExpr<'a> {}
|
||||||
impl<'a> WhileExpr<'a> {
|
impl<'a> WhileExpr<'a> {
|
||||||
pub fn condition(self) -> Option<Condition<'a>> {
|
pub fn condition(self) -> Option<Condition<'a>> {
|
||||||
super::child_opt(self)
|
super::child_opt(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn body(self) -> Option<Block<'a>> {
|
|
||||||
super::child_opt(self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,12 @@ pub trait NameOwner<'a>: AstNode<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait LoopBodyOwner<'a>: AstNode<'a> {
|
||||||
|
fn loop_body(self) -> Option<Block<'a>> {
|
||||||
|
child_opt(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait TypeParamsOwner<'a>: AstNode<'a> {
|
pub trait TypeParamsOwner<'a>: AstNode<'a> {
|
||||||
fn type_param_list(self) -> Option<TypeParamList<'a>> {
|
fn type_param_list(self) -> Option<TypeParamList<'a>> {
|
||||||
child_opt(self)
|
child_opt(self)
|
||||||
|
|
|
@ -354,19 +354,19 @@ Grammar(
|
||||||
options: [ ["condition", "Condition"] ]
|
options: [ ["condition", "Condition"] ]
|
||||||
),
|
),
|
||||||
"LoopExpr": (
|
"LoopExpr": (
|
||||||
options: [ ["body", "Block"] ]
|
traits: ["LoopBodyOwner"],
|
||||||
),
|
),
|
||||||
"ForExpr": (
|
"ForExpr": (
|
||||||
|
traits: ["LoopBodyOwner"],
|
||||||
options: [
|
options: [
|
||||||
["pat", "Pat"],
|
["pat", "Pat"],
|
||||||
["iterable", "Expr"],
|
["iterable", "Expr"],
|
||||||
["body", "Block"] ,
|
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
"WhileExpr": (
|
"WhileExpr": (
|
||||||
|
traits: ["LoopBodyOwner"],
|
||||||
options: [
|
options: [
|
||||||
["condition", "Condition"],
|
["condition", "Condition"],
|
||||||
["body", "Block"],
|
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
"ContinueExpr": (),
|
"ContinueExpr": (),
|
||||||
|
|
Loading…
Reference in a new issue