diff options
Diffstat (limited to 'crates/ra_editor/src/assists')
-rw-r--r-- | crates/ra_editor/src/assists/add_derive.rs | 61 | ||||
-rw-r--r-- | crates/ra_editor/src/assists/add_impl.rs | 36 | ||||
-rw-r--r-- | crates/ra_editor/src/assists/change_visibility.rs | 84 | ||||
-rw-r--r-- | crates/ra_editor/src/assists/flip_comma.rs | 36 | ||||
-rw-r--r-- | crates/ra_editor/src/assists/introduce_variable.rs | 84 |
5 files changed, 117 insertions, 184 deletions
diff --git a/crates/ra_editor/src/assists/add_derive.rs b/crates/ra_editor/src/assists/add_derive.rs index 33d9d2c31..1e2cd4f30 100644 --- a/crates/ra_editor/src/assists/add_derive.rs +++ b/crates/ra_editor/src/assists/add_derive.rs | |||
@@ -1,85 +1,73 @@ | |||
1 | use ra_text_edit::TextEditBuilder; | ||
2 | use ra_syntax::{ | 1 | use ra_syntax::{ |
3 | ast::{self, AstNode, AttrsOwner}, | 2 | ast::{self, AstNode, AttrsOwner}, |
4 | SourceFileNode, | ||
5 | SyntaxKind::{WHITESPACE, COMMENT}, | 3 | SyntaxKind::{WHITESPACE, COMMENT}, |
6 | TextUnit, | 4 | TextUnit, |
7 | }; | 5 | }; |
8 | 6 | ||
9 | use crate::{ | 7 | use crate::assists::{AssistCtx, Assist}; |
10 | find_node_at_offset, | ||
11 | assists::LocalEdit, | ||
12 | }; | ||
13 | 8 | ||
14 | pub fn add_derive<'a>( | 9 | pub fn add_derive(ctx: AssistCtx) -> Option<Assist> { |
15 | file: &'a SourceFileNode, | 10 | let nominal = ctx.node_at_offset::<ast::NominalDef>()?; |
16 | offset: TextUnit, | ||
17 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
18 | let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?; | ||
19 | let node_start = derive_insertion_offset(nominal)?; | 11 | let node_start = derive_insertion_offset(nominal)?; |
20 | return Some(move || { | 12 | ctx.build("add `#[derive]`", |edit| { |
21 | let derive_attr = nominal | 13 | let derive_attr = nominal |
22 | .attrs() | 14 | .attrs() |
23 | .filter_map(|x| x.as_call()) | 15 | .filter_map(|x| x.as_call()) |
24 | .filter(|(name, _arg)| name == "derive") | 16 | .filter(|(name, _arg)| name == "derive") |
25 | .map(|(_name, arg)| arg) | 17 | .map(|(_name, arg)| arg) |
26 | .next(); | 18 | .next(); |
27 | let mut edit = TextEditBuilder::new(); | ||
28 | let offset = match derive_attr { | 19 | let offset = match derive_attr { |
29 | None => { | 20 | None => { |
30 | edit.insert(node_start, "#[derive()]\n".to_string()); | 21 | edit.insert(node_start, "#[derive()]\n"); |
31 | node_start + TextUnit::of_str("#[derive(") | 22 | node_start + TextUnit::of_str("#[derive(") |
32 | } | 23 | } |
33 | Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'), | 24 | Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'), |
34 | }; | 25 | }; |
35 | LocalEdit { | 26 | edit.set_cursor(offset) |
36 | label: "add `#[derive]`".to_string(), | 27 | }) |
37 | edit: edit.finish(), | 28 | } |
38 | cursor_position: Some(offset), | ||
39 | } | ||
40 | }); | ||
41 | 29 | ||
42 | // Insert `derive` after doc comments. | 30 | // Insert `derive` after doc comments. |
43 | fn derive_insertion_offset(nominal: ast::NominalDef) -> Option<TextUnit> { | 31 | fn derive_insertion_offset(nominal: ast::NominalDef) -> Option<TextUnit> { |
44 | let non_ws_child = nominal | 32 | let non_ws_child = nominal |
45 | .syntax() | 33 | .syntax() |
46 | .children() | 34 | .children() |
47 | .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?; | 35 | .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?; |
48 | Some(non_ws_child.range().start()) | 36 | Some(non_ws_child.range().start()) |
49 | } | ||
50 | } | 37 | } |
51 | 38 | ||
52 | #[cfg(test)] | 39 | #[cfg(test)] |
53 | mod tests { | 40 | mod tests { |
54 | use super::*; | 41 | use super::*; |
55 | use crate::test_utils::check_action; | 42 | use crate::assists::check_assist; |
56 | 43 | ||
57 | #[test] | 44 | #[test] |
58 | fn add_derive_new() { | 45 | fn add_derive_new() { |
59 | check_action( | 46 | check_assist( |
47 | add_derive, | ||
60 | "struct Foo { a: i32, <|>}", | 48 | "struct Foo { a: i32, <|>}", |
61 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | 49 | "#[derive(<|>)]\nstruct Foo { a: i32, }", |
62 | |file, off| add_derive(file, off).map(|f| f()), | ||
63 | ); | 50 | ); |
64 | check_action( | 51 | check_assist( |
52 | add_derive, | ||
65 | "struct Foo { <|> a: i32, }", | 53 | "struct Foo { <|> a: i32, }", |
66 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | 54 | "#[derive(<|>)]\nstruct Foo { a: i32, }", |
67 | |file, off| add_derive(file, off).map(|f| f()), | ||
68 | ); | 55 | ); |
69 | } | 56 | } |
70 | 57 | ||
71 | #[test] | 58 | #[test] |
72 | fn add_derive_existing() { | 59 | fn add_derive_existing() { |
73 | check_action( | 60 | check_assist( |
61 | add_derive, | ||
74 | "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", | 62 | "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", |
75 | "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", | 63 | "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", |
76 | |file, off| add_derive(file, off).map(|f| f()), | ||
77 | ); | 64 | ); |
78 | } | 65 | } |
79 | 66 | ||
80 | #[test] | 67 | #[test] |
81 | fn add_derive_new_with_doc_comment() { | 68 | fn add_derive_new_with_doc_comment() { |
82 | check_action( | 69 | check_assist( |
70 | add_derive, | ||
83 | " | 71 | " |
84 | /// `Foo` is a pretty important struct. | 72 | /// `Foo` is a pretty important struct. |
85 | /// It does stuff. | 73 | /// It does stuff. |
@@ -91,7 +79,6 @@ struct Foo { a: i32<|>, } | |||
91 | #[derive(<|>)] | 79 | #[derive(<|>)] |
92 | struct Foo { a: i32, } | 80 | struct Foo { a: i32, } |
93 | ", | 81 | ", |
94 | |file, off| add_derive(file, off).map(|f| f()), | ||
95 | ); | 82 | ); |
96 | } | 83 | } |
97 | } | 84 | } |
diff --git a/crates/ra_editor/src/assists/add_impl.rs b/crates/ra_editor/src/assists/add_impl.rs index 50e00688e..9353e2717 100644 --- a/crates/ra_editor/src/assists/add_impl.rs +++ b/crates/ra_editor/src/assists/add_impl.rs | |||
@@ -1,23 +1,16 @@ | |||
1 | use join_to_string::join; | 1 | use join_to_string::join; |
2 | use ra_text_edit::TextEditBuilder; | ||
3 | use ra_syntax::{ | 2 | use ra_syntax::{ |
4 | ast::{self, AstNode, NameOwner, TypeParamsOwner}, | 3 | ast::{self, AstNode, NameOwner, TypeParamsOwner}, |
5 | SourceFileNode, | ||
6 | TextUnit, | 4 | TextUnit, |
7 | }; | 5 | }; |
8 | 6 | ||
9 | use crate::{find_node_at_offset, assists::LocalEdit}; | 7 | use crate::assists::{AssistCtx, Assist}; |
10 | 8 | ||
11 | pub fn add_impl<'a>( | 9 | pub fn add_impl(ctx: AssistCtx) -> Option<Assist> { |
12 | file: &'a SourceFileNode, | 10 | let nominal = ctx.node_at_offset::<ast::NominalDef>()?; |
13 | offset: TextUnit, | ||
14 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
15 | let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?; | ||
16 | let name = nominal.name()?; | 11 | let name = nominal.name()?; |
17 | 12 | ctx.build("add impl", |edit| { | |
18 | Some(move || { | ||
19 | let type_params = nominal.type_param_list(); | 13 | let type_params = nominal.type_param_list(); |
20 | let mut edit = TextEditBuilder::new(); | ||
21 | let start_offset = nominal.syntax().range().end(); | 14 | let start_offset = nominal.syntax().range().end(); |
22 | let mut buf = String::new(); | 15 | let mut buf = String::new(); |
23 | buf.push_str("\n\nimpl"); | 16 | buf.push_str("\n\nimpl"); |
@@ -40,38 +33,33 @@ pub fn add_impl<'a>( | |||
40 | .to_buf(&mut buf); | 33 | .to_buf(&mut buf); |
41 | } | 34 | } |
42 | buf.push_str(" {\n"); | 35 | buf.push_str(" {\n"); |
43 | let offset = start_offset + TextUnit::of_str(&buf); | 36 | edit.set_cursor(start_offset + TextUnit::of_str(&buf)); |
44 | buf.push_str("\n}"); | 37 | buf.push_str("\n}"); |
45 | edit.insert(start_offset, buf); | 38 | edit.insert(start_offset, buf); |
46 | LocalEdit { | ||
47 | label: "add impl".to_string(), | ||
48 | edit: edit.finish(), | ||
49 | cursor_position: Some(offset), | ||
50 | } | ||
51 | }) | 39 | }) |
52 | } | 40 | } |
53 | 41 | ||
54 | #[cfg(test)] | 42 | #[cfg(test)] |
55 | mod tests { | 43 | mod tests { |
56 | use super::*; | 44 | use super::*; |
57 | use crate::test_utils::check_action; | 45 | use crate::assists::check_assist; |
58 | 46 | ||
59 | #[test] | 47 | #[test] |
60 | fn test_add_impl() { | 48 | fn test_add_impl() { |
61 | check_action( | 49 | check_assist( |
50 | add_impl, | ||
62 | "struct Foo {<|>}\n", | 51 | "struct Foo {<|>}\n", |
63 | "struct Foo {}\n\nimpl Foo {\n<|>\n}\n", | 52 | "struct Foo {}\n\nimpl Foo {\n<|>\n}\n", |
64 | |file, off| add_impl(file, off).map(|f| f()), | ||
65 | ); | 53 | ); |
66 | check_action( | 54 | check_assist( |
55 | add_impl, | ||
67 | "struct Foo<T: Clone> {<|>}", | 56 | "struct Foo<T: Clone> {<|>}", |
68 | "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}", | 57 | "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}", |
69 | |file, off| add_impl(file, off).map(|f| f()), | ||
70 | ); | 58 | ); |
71 | check_action( | 59 | check_assist( |
60 | add_impl, | ||
72 | "struct Foo<'a, T: Foo<'a>> {<|>}", | 61 | "struct Foo<'a, T: Foo<'a>> {<|>}", |
73 | "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}", | 62 | "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}", |
74 | |file, off| add_impl(file, off).map(|f| f()), | ||
75 | ); | 63 | ); |
76 | } | 64 | } |
77 | 65 | ||
diff --git a/crates/ra_editor/src/assists/change_visibility.rs b/crates/ra_editor/src/assists/change_visibility.rs index 98c218f32..379e88d3c 100644 --- a/crates/ra_editor/src/assists/change_visibility.rs +++ b/crates/ra_editor/src/assists/change_visibility.rs | |||
@@ -1,90 +1,74 @@ | |||
1 | use ra_text_edit::TextEditBuilder; | ||
2 | use ra_syntax::{ | 1 | use ra_syntax::{ |
3 | SourceFileNode, | ||
4 | algo::find_leaf_at_offset, | ||
5 | SyntaxKind::{VISIBILITY, FN_KW, MOD_KW, STRUCT_KW, ENUM_KW, TRAIT_KW, FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF}, | 2 | SyntaxKind::{VISIBILITY, FN_KW, MOD_KW, STRUCT_KW, ENUM_KW, TRAIT_KW, FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF}, |
6 | TextUnit, | ||
7 | }; | 3 | }; |
8 | 4 | ||
9 | use crate::assists::LocalEdit; | 5 | use crate::assists::{AssistCtx, Assist}; |
10 | 6 | ||
11 | pub fn change_visibility<'a>( | 7 | pub fn change_visibility(ctx: AssistCtx) -> Option<Assist> { |
12 | file: &'a SourceFileNode, | 8 | let keyword = ctx.leaf_at_offset().find(|leaf| match leaf.kind() { |
13 | offset: TextUnit, | ||
14 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
15 | let syntax = file.syntax(); | ||
16 | |||
17 | let keyword = find_leaf_at_offset(syntax, offset).find(|leaf| match leaf.kind() { | ||
18 | FN_KW | MOD_KW | STRUCT_KW | ENUM_KW | TRAIT_KW => true, | 9 | FN_KW | MOD_KW | STRUCT_KW | ENUM_KW | TRAIT_KW => true, |
19 | _ => false, | 10 | _ => false, |
20 | })?; | 11 | })?; |
21 | let parent = keyword.parent()?; | 12 | let parent = keyword.parent()?; |
22 | let def_kws = vec![FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF]; | 13 | let def_kws = vec![FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF]; |
23 | let node_start = parent.range().start(); | 14 | // Parent is not a definition, can't add visibility |
24 | Some(move || { | 15 | if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) { |
25 | let mut edit = TextEditBuilder::new(); | 16 | return None; |
26 | 17 | } | |
27 | if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) | 18 | // Already have visibility, do nothing |
28 | || parent.children().any(|child| child.kind() == VISIBILITY) | 19 | if parent.children().any(|child| child.kind() == VISIBILITY) { |
29 | { | 20 | return None; |
30 | return LocalEdit { | 21 | } |
31 | label: "make pub crate".to_string(), | ||
32 | edit: edit.finish(), | ||
33 | cursor_position: Some(offset), | ||
34 | }; | ||
35 | } | ||
36 | 22 | ||
37 | edit.insert(node_start, "pub(crate) ".to_string()); | 23 | let node_start = parent.range().start(); |
38 | LocalEdit { | 24 | ctx.build("make pub crate", |edit| { |
39 | label: "make pub crate".to_string(), | 25 | edit.insert(node_start, "pub(crate) "); |
40 | edit: edit.finish(), | 26 | edit.set_cursor(node_start); |
41 | cursor_position: Some(node_start), | ||
42 | } | ||
43 | }) | 27 | }) |
44 | } | 28 | } |
45 | 29 | ||
46 | #[cfg(test)] | 30 | #[cfg(test)] |
47 | mod tests { | 31 | mod tests { |
48 | use super::*; | 32 | use super::*; |
49 | use crate::test_utils::check_action; | 33 | use crate::assists::check_assist; |
50 | 34 | ||
51 | #[test] | 35 | #[test] |
52 | fn test_change_visibility() { | 36 | fn test_change_visibility() { |
53 | check_action( | 37 | check_assist( |
38 | change_visibility, | ||
54 | "<|>fn foo() {}", | 39 | "<|>fn foo() {}", |
55 | "<|>pub(crate) fn foo() {}", | 40 | "<|>pub(crate) fn foo() {}", |
56 | |file, off| change_visibility(file, off).map(|f| f()), | ||
57 | ); | 41 | ); |
58 | check_action( | 42 | check_assist( |
43 | change_visibility, | ||
59 | "f<|>n foo() {}", | 44 | "f<|>n foo() {}", |
60 | "<|>pub(crate) fn foo() {}", | 45 | "<|>pub(crate) fn foo() {}", |
61 | |file, off| change_visibility(file, off).map(|f| f()), | ||
62 | ); | 46 | ); |
63 | check_action( | 47 | check_assist( |
48 | change_visibility, | ||
64 | "<|>struct Foo {}", | 49 | "<|>struct Foo {}", |
65 | "<|>pub(crate) struct Foo {}", | 50 | "<|>pub(crate) struct Foo {}", |
66 | |file, off| change_visibility(file, off).map(|f| f()), | ||
67 | ); | 51 | ); |
68 | check_action("<|>mod foo {}", "<|>pub(crate) mod foo {}", |file, off| { | 52 | check_assist( |
69 | change_visibility(file, off).map(|f| f()) | 53 | change_visibility, |
70 | }); | 54 | "<|>mod foo {}", |
71 | check_action( | 55 | "<|>pub(crate) mod foo {}", |
56 | ); | ||
57 | check_assist( | ||
58 | change_visibility, | ||
72 | "<|>trait Foo {}", | 59 | "<|>trait Foo {}", |
73 | "<|>pub(crate) trait Foo {}", | 60 | "<|>pub(crate) trait Foo {}", |
74 | |file, off| change_visibility(file, off).map(|f| f()), | ||
75 | ); | 61 | ); |
76 | check_action("m<|>od {}", "<|>pub(crate) mod {}", |file, off| { | 62 | check_assist(change_visibility, "m<|>od {}", "<|>pub(crate) mod {}"); |
77 | change_visibility(file, off).map(|f| f()) | 63 | check_assist( |
78 | }); | 64 | change_visibility, |
79 | check_action( | ||
80 | "pub(crate) f<|>n foo() {}", | 65 | "pub(crate) f<|>n foo() {}", |
81 | "pub(crate) f<|>n foo() {}", | 66 | "pub(crate) f<|>n foo() {}", |
82 | |file, off| change_visibility(file, off).map(|f| f()), | ||
83 | ); | 67 | ); |
84 | check_action( | 68 | check_assist( |
69 | change_visibility, | ||
85 | "unsafe f<|>n foo() {}", | 70 | "unsafe f<|>n foo() {}", |
86 | "<|>pub(crate) unsafe fn foo() {}", | 71 | "<|>pub(crate) unsafe fn foo() {}", |
87 | |file, off| change_visibility(file, off).map(|f| f()), | ||
88 | ); | 72 | ); |
89 | } | 73 | } |
90 | } | 74 | } |
diff --git a/crates/ra_editor/src/assists/flip_comma.rs b/crates/ra_editor/src/assists/flip_comma.rs index d8727db0d..a343413cc 100644 --- a/crates/ra_editor/src/assists/flip_comma.rs +++ b/crates/ra_editor/src/assists/flip_comma.rs | |||
@@ -1,45 +1,31 @@ | |||
1 | use ra_text_edit::TextEditBuilder; | ||
2 | use ra_syntax::{ | 1 | use ra_syntax::{ |
3 | algo::find_leaf_at_offset, | 2 | Direction, |
4 | Direction, SourceFileNode, | ||
5 | SyntaxKind::COMMA, | 3 | SyntaxKind::COMMA, |
6 | TextUnit, | ||
7 | }; | 4 | }; |
8 | 5 | ||
9 | use crate::assists::{LocalEdit, non_trivia_sibling}; | 6 | use crate::assists::{non_trivia_sibling, AssistCtx, Assist}; |
10 | 7 | ||
11 | pub fn flip_comma<'a>( | 8 | pub fn flip_comma(ctx: AssistCtx) -> Option<Assist> { |
12 | file: &'a SourceFileNode, | 9 | let comma = ctx.leaf_at_offset().find(|leaf| leaf.kind() == COMMA)?; |
13 | offset: TextUnit, | ||
14 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
15 | let syntax = file.syntax(); | ||
16 | |||
17 | let comma = find_leaf_at_offset(syntax, offset).find(|leaf| leaf.kind() == COMMA)?; | ||
18 | let prev = non_trivia_sibling(comma, Direction::Prev)?; | 10 | let prev = non_trivia_sibling(comma, Direction::Prev)?; |
19 | let next = non_trivia_sibling(comma, Direction::Next)?; | 11 | let next = non_trivia_sibling(comma, Direction::Next)?; |
20 | Some(move || { | 12 | ctx.build("flip comma", |edit| { |
21 | let mut edit = TextEditBuilder::new(); | 13 | edit.replace(prev.range(), next.text()); |
22 | edit.replace(prev.range(), next.text().to_string()); | 14 | edit.replace(next.range(), prev.text()); |
23 | edit.replace(next.range(), prev.text().to_string()); | ||
24 | LocalEdit { | ||
25 | label: "flip comma".to_string(), | ||
26 | edit: edit.finish(), | ||
27 | cursor_position: None, | ||
28 | } | ||
29 | }) | 15 | }) |
30 | } | 16 | } |
31 | 17 | ||
32 | #[cfg(test)] | 18 | #[cfg(test)] |
33 | mod tests { | 19 | mod tests { |
34 | use super::*; | 20 | use super::*; |
35 | use crate::test_utils::check_action; | 21 | use crate::assists::check_assist; |
36 | 22 | ||
37 | #[test] | 23 | #[test] |
38 | fn test_swap_comma() { | 24 | fn flip_comma_works_for_function_parameters() { |
39 | check_action( | 25 | check_assist( |
26 | flip_comma, | ||
40 | "fn foo(x: i32,<|> y: Result<(), ()>) {}", | 27 | "fn foo(x: i32,<|> y: Result<(), ()>) {}", |
41 | "fn foo(y: Result<(), ()>,<|> x: i32) {}", | 28 | "fn foo(y: Result<(), ()>,<|> x: i32) {}", |
42 | |file, off| flip_comma(file, off).map(|f| f()), | ||
43 | ) | 29 | ) |
44 | } | 30 | } |
45 | } | 31 | } |
diff --git a/crates/ra_editor/src/assists/introduce_variable.rs b/crates/ra_editor/src/assists/introduce_variable.rs index 17ab521fa..782861023 100644 --- a/crates/ra_editor/src/assists/introduce_variable.rs +++ b/crates/ra_editor/src/assists/introduce_variable.rs | |||
@@ -1,19 +1,13 @@ | |||
1 | use ra_text_edit::TextEditBuilder; | ||
2 | use ra_syntax::{ | 1 | use ra_syntax::{ |
3 | algo::{find_covering_node}, | ||
4 | ast::{self, AstNode}, | 2 | ast::{self, AstNode}, |
5 | SourceFileNode, | 3 | SyntaxKind::WHITESPACE, |
6 | SyntaxKind::{WHITESPACE}, | 4 | SyntaxNodeRef, TextUnit, |
7 | SyntaxNodeRef, TextRange, TextUnit, | ||
8 | }; | 5 | }; |
9 | 6 | ||
10 | use crate::assists::LocalEdit; | 7 | use crate::assists::{AssistCtx, Assist}; |
11 | 8 | ||
12 | pub fn introduce_variable<'a>( | 9 | pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> { |
13 | file: &'a SourceFileNode, | 10 | let node = ctx.covering_node(); |
14 | range: TextRange, | ||
15 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
16 | let node = find_covering_node(file.syntax(), range); | ||
17 | let expr = node.ancestors().filter_map(ast::Expr::cast).next()?; | 11 | let expr = node.ancestors().filter_map(ast::Expr::cast).next()?; |
18 | 12 | ||
19 | let anchor_stmt = anchor_stmt(expr)?; | 13 | let anchor_stmt = anchor_stmt(expr)?; |
@@ -21,9 +15,8 @@ pub fn introduce_variable<'a>( | |||
21 | if indent.kind() != WHITESPACE { | 15 | if indent.kind() != WHITESPACE { |
22 | return None; | 16 | return None; |
23 | } | 17 | } |
24 | return Some(move || { | 18 | ctx.build("introduce variable", move |edit| { |
25 | let mut buf = String::new(); | 19 | let mut buf = String::new(); |
26 | let mut edit = TextEditBuilder::new(); | ||
27 | 20 | ||
28 | buf.push_str("let var_name = "); | 21 | buf.push_str("let var_name = "); |
29 | expr.syntax().text().push_to(&mut buf); | 22 | expr.syntax().text().push_to(&mut buf); |
@@ -40,43 +33,39 @@ pub fn introduce_variable<'a>( | |||
40 | edit.replace(expr.syntax().range(), "var_name".to_string()); | 33 | edit.replace(expr.syntax().range(), "var_name".to_string()); |
41 | edit.insert(anchor_stmt.range().start(), buf); | 34 | edit.insert(anchor_stmt.range().start(), buf); |
42 | } | 35 | } |
43 | let cursor_position = anchor_stmt.range().start() + TextUnit::of_str("let "); | 36 | edit.set_cursor(anchor_stmt.range().start() + TextUnit::of_str("let ")); |
44 | LocalEdit { | 37 | }) |
45 | label: "introduce variable".to_string(), | 38 | } |
46 | edit: edit.finish(), | ||
47 | cursor_position: Some(cursor_position), | ||
48 | } | ||
49 | }); | ||
50 | 39 | ||
51 | /// Statement or last in the block expression, which will follow | 40 | /// Statement or last in the block expression, which will follow |
52 | /// the freshly introduced var. | 41 | /// the freshly introduced var. |
53 | fn anchor_stmt(expr: ast::Expr) -> Option<SyntaxNodeRef> { | 42 | fn anchor_stmt(expr: ast::Expr) -> Option<SyntaxNodeRef> { |
54 | expr.syntax().ancestors().find(|&node| { | 43 | expr.syntax().ancestors().find(|&node| { |
55 | if ast::Stmt::cast(node).is_some() { | 44 | if ast::Stmt::cast(node).is_some() { |
45 | return true; | ||
46 | } | ||
47 | if let Some(expr) = node | ||
48 | .parent() | ||
49 | .and_then(ast::Block::cast) | ||
50 | .and_then(|it| it.expr()) | ||
51 | { | ||
52 | if expr.syntax() == node { | ||
56 | return true; | 53 | return true; |
57 | } | 54 | } |
58 | if let Some(expr) = node | 55 | } |
59 | .parent() | 56 | false |
60 | .and_then(ast::Block::cast) | 57 | }) |
61 | .and_then(|it| it.expr()) | ||
62 | { | ||
63 | if expr.syntax() == node { | ||
64 | return true; | ||
65 | } | ||
66 | } | ||
67 | false | ||
68 | }) | ||
69 | } | ||
70 | } | 58 | } |
71 | 59 | ||
72 | #[cfg(test)] | 60 | #[cfg(test)] |
73 | mod tests { | 61 | mod tests { |
74 | use super::*; | 62 | use super::*; |
75 | use crate::test_utils::check_action_range; | 63 | use crate::assists::check_assist_range; |
76 | 64 | ||
77 | #[test] | 65 | #[test] |
78 | fn test_introduce_var_simple() { | 66 | fn test_introduce_var_simple() { |
79 | check_action_range( | 67 | check_assist_range( |
68 | introduce_variable, | ||
80 | " | 69 | " |
81 | fn foo() { | 70 | fn foo() { |
82 | foo(<|>1 + 1<|>); | 71 | foo(<|>1 + 1<|>); |
@@ -86,13 +75,13 @@ fn foo() { | |||
86 | let <|>var_name = 1 + 1; | 75 | let <|>var_name = 1 + 1; |
87 | foo(var_name); | 76 | foo(var_name); |
88 | }", | 77 | }", |
89 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
90 | ); | 78 | ); |
91 | } | 79 | } |
92 | 80 | ||
93 | #[test] | 81 | #[test] |
94 | fn test_introduce_var_expr_stmt() { | 82 | fn test_introduce_var_expr_stmt() { |
95 | check_action_range( | 83 | check_assist_range( |
84 | introduce_variable, | ||
96 | " | 85 | " |
97 | fn foo() { | 86 | fn foo() { |
98 | <|>1 + 1<|>; | 87 | <|>1 + 1<|>; |
@@ -101,13 +90,13 @@ fn foo() { | |||
101 | fn foo() { | 90 | fn foo() { |
102 | let <|>var_name = 1 + 1; | 91 | let <|>var_name = 1 + 1; |
103 | }", | 92 | }", |
104 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
105 | ); | 93 | ); |
106 | } | 94 | } |
107 | 95 | ||
108 | #[test] | 96 | #[test] |
109 | fn test_introduce_var_part_of_expr_stmt() { | 97 | fn test_introduce_var_part_of_expr_stmt() { |
110 | check_action_range( | 98 | check_assist_range( |
99 | introduce_variable, | ||
111 | " | 100 | " |
112 | fn foo() { | 101 | fn foo() { |
113 | <|>1<|> + 1; | 102 | <|>1<|> + 1; |
@@ -117,13 +106,13 @@ fn foo() { | |||
117 | let <|>var_name = 1; | 106 | let <|>var_name = 1; |
118 | var_name + 1; | 107 | var_name + 1; |
119 | }", | 108 | }", |
120 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
121 | ); | 109 | ); |
122 | } | 110 | } |
123 | 111 | ||
124 | #[test] | 112 | #[test] |
125 | fn test_introduce_var_last_expr() { | 113 | fn test_introduce_var_last_expr() { |
126 | check_action_range( | 114 | check_assist_range( |
115 | introduce_variable, | ||
127 | " | 116 | " |
128 | fn foo() { | 117 | fn foo() { |
129 | bar(<|>1 + 1<|>) | 118 | bar(<|>1 + 1<|>) |
@@ -133,13 +122,13 @@ fn foo() { | |||
133 | let <|>var_name = 1 + 1; | 122 | let <|>var_name = 1 + 1; |
134 | bar(var_name) | 123 | bar(var_name) |
135 | }", | 124 | }", |
136 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
137 | ); | 125 | ); |
138 | } | 126 | } |
139 | 127 | ||
140 | #[test] | 128 | #[test] |
141 | fn test_introduce_var_last_full_expr() { | 129 | fn test_introduce_var_last_full_expr() { |
142 | check_action_range( | 130 | check_assist_range( |
131 | introduce_variable, | ||
143 | " | 132 | " |
144 | fn foo() { | 133 | fn foo() { |
145 | <|>bar(1 + 1)<|> | 134 | <|>bar(1 + 1)<|> |
@@ -149,7 +138,6 @@ fn foo() { | |||
149 | let <|>var_name = bar(1 + 1); | 138 | let <|>var_name = bar(1 + 1); |
150 | var_name | 139 | var_name |
151 | }", | 140 | }", |
152 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
153 | ); | 141 | ); |
154 | } | 142 | } |
155 | 143 | ||