Skip to content

Commit 546e2bf

Browse files
committed
Support WhileExpr and ForExpr for add_label_to_loop
Example --- ```rust fn main() { for$0 _ in 0..5 { break; continue; } } ``` **Before this PR** Assist not applicable **After this PR** ```rust fn main() { 'l: for _ in 0..5 { break 'l; continue 'l; } } ```
1 parent c46279d commit 546e2bf

File tree

3 files changed

+121
-9
lines changed

3 files changed

+121
-9
lines changed

crates/ide-assists/src/handlers/add_label_to_loop.rs

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
use ide_db::syntax_helpers::node_ext::for_each_break_and_continue_expr;
2-
use syntax::{
3-
T,
4-
ast::{self, AstNode, HasLoopBody},
5-
};
2+
use syntax::ast::{self, AstNode, HasLoopBody};
63

74
use crate::{AssistContext, AssistId, Assists};
85

@@ -28,9 +25,9 @@ use crate::{AssistContext, AssistId, Assists};
2825
// }
2926
// ```
3027
pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
31-
let loop_kw = ctx.find_token_syntax_at_offset(T![loop])?;
32-
let loop_expr = loop_kw.parent().and_then(ast::LoopExpr::cast)?;
33-
if loop_expr.label().is_some() {
28+
let loop_expr = ctx.find_node_at_offset::<ast::LoopLike>()?;
29+
let loop_kw = loop_expr.loop_token()?;
30+
if loop_expr.label().is_some() || !loop_kw.text_range().contains_inclusive(ctx.offset()) {
3431
return None;
3532
}
3633

@@ -90,6 +87,48 @@ fn main() {
9087
);
9188
}
9289

90+
#[test]
91+
fn add_label_to_while_expr() {
92+
check_assist(
93+
add_label_to_loop,
94+
r#"
95+
fn main() {
96+
while$0 true {
97+
break;
98+
continue;
99+
}
100+
}"#,
101+
r#"
102+
fn main() {
103+
'l: while true {
104+
break 'l;
105+
continue 'l;
106+
}
107+
}"#,
108+
);
109+
}
110+
111+
#[test]
112+
fn add_label_to_for_expr() {
113+
check_assist(
114+
add_label_to_loop,
115+
r#"
116+
fn main() {
117+
for$0 _ in 0..5 {
118+
break;
119+
continue;
120+
}
121+
}"#,
122+
r#"
123+
fn main() {
124+
'l: for _ in 0..5 {
125+
break 'l;
126+
continue 'l;
127+
}
128+
}"#,
129+
);
130+
}
131+
93132
#[test]
94133
fn add_label_to_outer_loop() {
95134
check_assist(
@@ -158,6 +197,31 @@ fn main() {
158197
break 'l;
159198
continue 'l;
160199
}
200+
}"#,
201+
);
202+
}
203+
204+
#[test]
205+
fn do_not_add_label_if_outside_keyword() {
206+
check_assist_not_applicable(
207+
add_label_to_loop,
208+
r#"
209+
fn main() {
210+
'l: loop {$0
211+
break 'l;
212+
continue 'l;
213+
}
214+
}"#,
215+
);
216+
217+
check_assist_not_applicable(
218+
add_label_to_loop,
219+
r#"
220+
fn main() {
221+
'l: while true {$0
222+
break 'l;
223+
continue 'l;
224+
}
161225
}"#,
162226
);
163227
}

crates/syntax/src/ast.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ pub use self::{
2525
expr_ext::{ArrayExprKind, BlockModifier, CallableExpr, ElseBranch, LiteralKind},
2626
generated::{nodes::*, tokens::*},
2727
node_ext::{
28-
AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind,
29-
SlicePatComponents, StructKind, TypeBoundKind, TypeOrConstParam, VisibilityKind,
28+
AttrKind, FieldKind, LoopLike, Macro, NameLike, NameOrNameRef, PathSegmentKind,
29+
SelfParamKind, SlicePatComponents, StructKind, TypeBoundKind, TypeOrConstParam,
30+
VisibilityKind,
3031
},
3132
operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
3233
token_ext::{

crates/syntax/src/ast/node_ext.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,53 @@ impl AstNode for TypeOrConstParam {
880880

881881
impl HasAttrs for TypeOrConstParam {}
882882

883+
pub enum LoopLike {
884+
ForExpr(ast::ForExpr),
885+
LoopExpr(ast::LoopExpr),
886+
WhileExpr(ast::WhileExpr),
887+
}
888+
889+
impl LoopLike {
890+
pub fn loop_token(&self) -> Option<SyntaxToken> {
891+
match self {
892+
LoopLike::ForExpr(it) => it.for_token(),
893+
LoopLike::LoopExpr(it) => it.loop_token(),
894+
LoopLike::WhileExpr(it) => it.while_token(),
895+
}
896+
}
897+
}
898+
899+
impl AstNode for LoopLike {
900+
fn can_cast(kind: SyntaxKind) -> bool
901+
where
902+
Self: Sized,
903+
{
904+
matches!(kind, SyntaxKind::FOR_EXPR | SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR)
905+
}
906+
907+
fn cast(syntax: SyntaxNode) -> Option<Self>
908+
where
909+
Self: Sized,
910+
{
911+
match syntax.kind() {
912+
SyntaxKind::FOR_EXPR => ast::ForExpr::cast(syntax).map(Self::ForExpr),
913+
SyntaxKind::LOOP_EXPR => ast::LoopExpr::cast(syntax).map(Self::LoopExpr),
914+
SyntaxKind::WHILE_EXPR => ast::WhileExpr::cast(syntax).map(Self::WhileExpr),
915+
_ => None,
916+
}
917+
}
918+
919+
fn syntax(&self) -> &SyntaxNode {
920+
match self {
921+
LoopLike::ForExpr(it) => it.syntax(),
922+
LoopLike::LoopExpr(it) => it.syntax(),
923+
LoopLike::WhileExpr(it) => it.syntax(),
924+
}
925+
}
926+
}
927+
928+
impl ast::HasLoopBody for LoopLike {}
929+
883930
pub enum VisibilityKind {
884931
In(ast::Path),
885932
PubCrate,

0 commit comments

Comments
 (0)