Skip to content

Commit 3be98f2

Browse files
committed
Add tests for action target ranges
1 parent a3622eb commit 3be98f2

File tree

10 files changed

+210
-16
lines changed

10 files changed

+210
-16
lines changed

crates/ra_assists/src/add_derive.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option<TextUnit> {
3939
#[cfg(test)]
4040
mod tests {
4141
use super::*;
42-
use crate::helpers::check_assist;
42+
use crate::helpers::{check_assist, check_assist_target};
4343

4444
#[test]
4545
fn add_derive_new() {
@@ -81,4 +81,21 @@ struct Foo { a: i32, }
8181
",
8282
);
8383
}
84+
85+
#[test]
86+
fn add_derive_target() {
87+
check_assist_target(
88+
add_derive,
89+
"
90+
struct SomeThingIrrelevant;
91+
/// `Foo` is a pretty important struct.
92+
/// It does stuff.
93+
struct Foo { a: i32<|>, }
94+
struct EvenMoreIrrelevant;
95+
",
96+
"/// `Foo` is a pretty important struct.
97+
/// It does stuff.
98+
struct Foo { a: i32, }",
99+
);
100+
}
84101
}

crates/ra_assists/src/add_impl.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
1111
let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
1212
let name = nominal.name()?;
1313
ctx.build("add impl", |edit| {
14+
edit.target(nominal.syntax().range());
1415
let type_params = nominal.type_param_list();
1516
let start_offset = nominal.syntax().range().end();
1617
let mut buf = String::new();
@@ -37,7 +38,7 @@ pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
3738
#[cfg(test)]
3839
mod tests {
3940
use super::*;
40-
use crate::helpers::check_assist;
41+
use crate::helpers::{check_assist, check_assist_target};
4142

4243
#[test]
4344
fn test_add_impl() {
@@ -54,4 +55,18 @@ mod tests {
5455
);
5556
}
5657

58+
#[test]
59+
fn add_impl_target() {
60+
check_assist_target(
61+
add_impl,
62+
"
63+
struct SomeThingIrrelevant;
64+
/// Has a lifetime parameter
65+
struct Foo<'a, T: Foo<'a>> {<|>}
66+
struct EvenMoreIrrelevant;
67+
",
68+
"/// Has a lifetime parameter
69+
struct Foo<'a, T: Foo<'a>> {}",
70+
);
71+
}
5772
}

crates/ra_assists/src/change_visibility.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ fn add_vis(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
3131
if parent.children().any(|child| child.kind() == VISIBILITY) {
3232
return None;
3333
}
34-
(vis_offset(parent), parent.range())
34+
(vis_offset(parent), keyword.range())
3535
} else {
3636
let ident = ctx.leaf_at_offset().find(|leaf| leaf.kind() == IDENT)?;
3737
let field = ident.ancestors().find_map(ast::NamedFieldDef::cast)?;
3838
if field.name()?.syntax().range() != ident.range() && field.visibility().is_some() {
3939
return None;
4040
}
41-
(vis_offset(field.syntax()), field.syntax().range())
41+
(vis_offset(field.syntax()), ident.range())
4242
};
4343

4444
ctx.build("make pub(crate)", |edit| {
@@ -80,7 +80,7 @@ fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option
8080
#[cfg(test)]
8181
mod tests {
8282
use super::*;
83-
use crate::helpers::check_assist;
83+
use crate::helpers::{check_assist, check_assist_target};
8484

8585
#[test]
8686
fn change_visibility_adds_pub_crate_to_items() {
@@ -138,4 +138,11 @@ mod tests {
138138
",
139139
)
140140
}
141+
142+
#[test]
143+
fn change_visibility_target() {
144+
check_assist_target(change_visibility, "<|>fn foo() {}", "fn");
145+
check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)");
146+
check_assist_target(change_visibility, "struct S { <|>field: u32 }", "field");
147+
}
141148
}

crates/ra_assists/src/fill_match_arms.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,15 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
6565
buf.push_str(" => (),\n");
6666
}
6767
buf.push_str("}");
68+
edit.target(match_expr.syntax().range());
6869
edit.set_cursor(expr.syntax().range().start());
6970
edit.replace_node_and_indent(match_expr.syntax(), buf);
7071
})
7172
}
7273

7374
#[cfg(test)]
7475
mod tests {
75-
use crate::helpers::check_assist;
76+
use crate::helpers::{check_assist, check_assist_target};
7677

7778
use super::fill_match_arms;
7879

@@ -139,4 +140,19 @@ mod tests {
139140
"#,
140141
);
141142
}
143+
144+
#[test]
145+
fn fill_match_arms_target() {
146+
check_assist_target(
147+
fill_match_arms,
148+
r#"
149+
enum E { X, Y}
150+
151+
fn main() {
152+
match E::X<|> {}
153+
}
154+
"#,
155+
"match E::X {}",
156+
);
157+
}
142158
}

crates/ra_assists/src/flip_comma.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub(crate) fn flip_comma(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
1111
let prev = non_trivia_sibling(comma, Direction::Prev)?;
1212
let next = non_trivia_sibling(comma, Direction::Next)?;
1313
ctx.build("flip comma", |edit| {
14+
edit.target(comma.range());
1415
edit.replace(prev.range(), next.text());
1516
edit.replace(next.range(), prev.text());
1617
})
@@ -20,7 +21,7 @@ pub(crate) fn flip_comma(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
2021
mod tests {
2122
use super::*;
2223

23-
use crate::helpers::check_assist;
24+
use crate::helpers::{check_assist, check_assist_target};
2425

2526
#[test]
2627
fn flip_comma_works_for_function_parameters() {
@@ -30,4 +31,9 @@ mod tests {
3031
"fn foo(y: Result<(), ()>,<|> x: i32) {}",
3132
)
3233
}
34+
35+
#[test]
36+
fn flip_comma_target() {
37+
check_assist_target(flip_comma, "fn foo(x: i32,<|> y: Result<(), ()>) {}", ",")
38+
}
3339
}

crates/ra_assists/src/introduce_variable.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub(crate) fn introduce_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Ass
4545
} else {
4646
buf.push_str(";");
4747
indent.text().push_to(&mut buf);
48+
edit.target(expr.syntax().range());
4849
edit.replace(expr.syntax().range(), "var_name".to_string());
4950
edit.insert(anchor_stmt.range().start(), buf);
5051
if wrap_in_block {
@@ -58,7 +59,7 @@ pub(crate) fn introduce_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Ass
5859
fn valid_covering_node(node: &SyntaxNode) -> bool {
5960
node.kind() != COMMENT
6061
}
61-
/// Check wether the node is a valid expression which can be extracted to a variable.
62+
/// Check whether the node is a valid expression which can be extracted to a variable.
6263
/// In general that's true for any expression, but in some cases that would produce invalid code.
6364
fn valid_target_expr(node: &SyntaxNode) -> Option<&ast::Expr> {
6465
match node.kind() {
@@ -74,7 +75,7 @@ fn valid_target_expr(node: &SyntaxNode) -> Option<&ast::Expr> {
7475
/// and a boolean indicating whether we have to wrap it within a { } block
7576
/// to produce correct code.
7677
/// It can be a statement, the last in a block expression or a wanna be block
77-
/// expression like a lamba or match arm.
78+
/// expression like a lambda or match arm.
7879
fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> {
7980
expr.syntax().ancestors().find_map(|node| {
8081
if ast::Stmt::cast(node).is_some() {
@@ -100,7 +101,7 @@ fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> {
100101
#[cfg(test)]
101102
mod tests {
102103
use super::*;
103-
use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_range};
104+
use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_range, check_assist_target, check_assist_range_target};
104105

105106
#[test]
106107
fn test_introduce_var_simple() {
@@ -425,4 +426,32 @@ fn main() {
425426
",
426427
);
427428
}
429+
430+
// FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic
431+
#[test]
432+
fn introduce_var_target() {
433+
check_assist_target(
434+
introduce_variable,
435+
"
436+
fn foo() -> u32 {
437+
r<|>eturn 2 + 2;
438+
}
439+
",
440+
"2 + 2",
441+
);
442+
443+
check_assist_range_target(
444+
introduce_variable,
445+
"
446+
fn main() {
447+
let x = true;
448+
let tuple = match x {
449+
true => (<|>2 + 2<|>, true)
450+
_ => (0, false)
451+
};
452+
}
453+
",
454+
"2 + 2",
455+
);
456+
}
428457
}

crates/ra_assists/src/lib.rs

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ where
6565
Assist::Unresolved(..) => unreachable!(),
6666
})
6767
.collect::<Vec<(AssistLabel, AssistAction)>>();
68-
a.sort_unstable_by(|a, b| match a {
68+
a.sort_by(|a, b| match a {
6969
// Some(y) < Some(x) < None for y < x
7070
(_, AssistAction { target: Some(a), .. }) => match b {
7171
(_, AssistAction { target: Some(b), .. }) => a.len().cmp(&b.len()),
@@ -163,6 +163,45 @@ mod helpers {
163163
assert_eq_text!(after, &actual);
164164
}
165165

166+
pub(crate) fn check_assist_target(
167+
assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
168+
before: &str,
169+
target: &str,
170+
) {
171+
let (before_cursor_pos, before) = extract_offset(before);
172+
let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
173+
let frange =
174+
FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
175+
let assist =
176+
AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
177+
let action = match assist {
178+
Assist::Unresolved(_) => unreachable!(),
179+
Assist::Resolved(_, it) => it,
180+
};
181+
182+
let range = action.target.expect("expected target on action");
183+
assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
184+
}
185+
186+
pub(crate) fn check_assist_range_target(
187+
assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
188+
before: &str,
189+
target: &str,
190+
) {
191+
let (range, before) = extract_range(before);
192+
let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
193+
let frange = FileRange { file_id, range };
194+
let assist =
195+
AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
196+
let action = match assist {
197+
Assist::Unresolved(_) => unreachable!(),
198+
Assist::Resolved(_, it) => it,
199+
};
200+
201+
let range = action.target.expect("expected target on action");
202+
assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
203+
}
204+
166205
pub(crate) fn check_assist_not_applicable(
167206
assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
168207
before: &str,
@@ -181,10 +220,10 @@ mod tests {
181220
use hir::mock::MockDatabase;
182221
use ra_syntax::TextRange;
183222
use ra_db::FileRange;
184-
use test_utils::extract_offset;
223+
use test_utils::{extract_offset, extract_range};
185224

186225
#[test]
187-
fn assist_order() {
226+
fn assist_order_field_struct() {
188227
let before = "struct Foo { <|>bar: u32 }";
189228
let (before_cursor_pos, before) = extract_offset(before);
190229
let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
@@ -197,4 +236,25 @@ mod tests {
197236
assert_eq!(assists.next().expect("expected assist").0.label, "add `#[derive]`");
198237
}
199238

239+
#[test]
240+
fn assist_order_if_expr() {
241+
let before = "
242+
pub fn test_some_range(a: int) -> bool {
243+
if let 2..6 = 5<|> {
244+
true
245+
} else {
246+
false
247+
}
248+
}";
249+
let (before_cursor_pos, before) = extract_offset(before);
250+
let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
251+
let frange =
252+
FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
253+
let assists = super::assists(&db, frange);
254+
let mut assists = assists.iter();
255+
256+
assert_eq!(assists.next().expect("expected assist").0.label, "introduce variable");
257+
assert_eq!(assists.next().expect("expected assist").0.label, "replace with match");
258+
}
259+
200260
}

crates/ra_assists/src/remove_dbg.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub(crate) fn remove_dbg(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
4747
};
4848

4949
ctx.build("remove dbg!()", |edit| {
50+
edit.target(macro_call.syntax().range());
5051
edit.replace(macro_range, macro_content);
5152
edit.set_cursor(cursor_pos);
5253
})
@@ -78,7 +79,7 @@ fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<b
7879
#[cfg(test)]
7980
mod tests {
8081
use super::*;
81-
use crate::helpers::{check_assist, check_assist_not_applicable};
82+
use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
8283

8384
#[test]
8485
fn test_remove_dbg() {
@@ -120,4 +121,19 @@ fn foo(n: usize) {
120121
check_assist_not_applicable(remove_dbg, "<|>dbg(5, 6, 7)");
121122
check_assist_not_applicable(remove_dbg, "<|>dbg!(5, 6, 7");
122123
}
124+
125+
#[test]
126+
fn remove_dbg_target() {
127+
check_assist_target(
128+
remove_dbg,
129+
"
130+
fn foo(n: usize) {
131+
if let Some(_) = dbg!(n.<|>checked_sub(4)) {
132+
// ...
133+
}
134+
}
135+
",
136+
"dbg!(n.checked_sub(4))",
137+
);
138+
}
123139
}

0 commit comments

Comments
 (0)