diff options
author | Jess Balint <[email protected]> | 2020-05-21 00:37:09 +0100 |
---|---|---|
committer | Jess Balint <[email protected]> | 2020-05-22 03:04:02 +0100 |
commit | 2ab79c6f4d972796d3fd9f4b7fae3342a551f5e8 (patch) | |
tree | d9b43b01ff1f42e47e9cee9952f01cc14ed0b4f2 | |
parent | 90332ca21901e1101154462053b50e5d2743a21c (diff) |
Assist: replace anonymous lifetime with a named one
(fixes #4523)
-rw-r--r-- | crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs | 123 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 2 | ||||
-rw-r--r-- | docs/user/assists.md | 24 |
3 files changed, 149 insertions, 0 deletions
diff --git a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs b/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs new file mode 100644 index 000000000..63f0a7dab --- /dev/null +++ b/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs | |||
@@ -0,0 +1,123 @@ | |||
1 | use crate::{AssistContext, AssistId, Assists}; | ||
2 | use ra_syntax::{ast, ast::{TypeParamsOwner}, AstNode, SyntaxKind}; | ||
3 | |||
4 | /// Assist: change_lifetime_anon_to_named | ||
5 | /// | ||
6 | /// Change an anonymous lifetime to a named lifetime. | ||
7 | /// | ||
8 | /// ``` | ||
9 | /// impl Cursor<'_<|>> { | ||
10 | /// fn node(self) -> &SyntaxNode { | ||
11 | /// match self { | ||
12 | /// Cursor::Replace(node) | Cursor::Before(node) => node, | ||
13 | /// } | ||
14 | /// } | ||
15 | /// } | ||
16 | /// ``` | ||
17 | /// -> | ||
18 | /// ``` | ||
19 | /// impl<'a> Cursor<'a> { | ||
20 | /// fn node(self) -> &SyntaxNode { | ||
21 | /// match self { | ||
22 | /// Cursor::Replace(node) | Cursor::Before(node) => node, | ||
23 | /// } | ||
24 | /// } | ||
25 | /// } | ||
26 | /// ``` | ||
27 | // TODO : How can we handle renaming any one of multiple anonymous lifetimes? | ||
28 | pub(crate) fn change_lifetime_anon_to_named(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
29 | let lifetime_token = ctx.find_token_at_offset(SyntaxKind::LIFETIME)?; | ||
30 | let lifetime_arg = ast::LifetimeArg::cast(lifetime_token.parent())?; | ||
31 | if lifetime_arg.syntax().text() != "'_" { | ||
32 | return None; | ||
33 | } | ||
34 | let next_token = lifetime_token.next_token()?; | ||
35 | if next_token.kind() != SyntaxKind::R_ANGLE { | ||
36 | // only allow naming the last anonymous lifetime | ||
37 | return None; | ||
38 | } | ||
39 | match lifetime_arg.syntax().ancestors().find_map(ast::ImplDef::cast) { | ||
40 | Some(impl_def) => { | ||
41 | // get the `impl` keyword so we know where to add the lifetime argument | ||
42 | let impl_kw = impl_def.syntax().first_child_or_token()?.into_token()?; | ||
43 | if impl_kw.kind() != SyntaxKind::IMPL_KW { | ||
44 | return None; | ||
45 | } | ||
46 | acc.add( | ||
47 | AssistId("change_lifetime_anon_to_named"), | ||
48 | "Give anonymous lifetime a name", | ||
49 | lifetime_arg.syntax().text_range(), | ||
50 | |builder| { | ||
51 | match impl_def.type_param_list() { | ||
52 | Some(type_params) => { | ||
53 | builder.insert( | ||
54 | (u32::from(type_params.syntax().text_range().end()) - 1).into(), | ||
55 | ", 'a", | ||
56 | ); | ||
57 | }, | ||
58 | None => { | ||
59 | builder.insert( | ||
60 | impl_kw.text_range().end(), | ||
61 | "<'a>", | ||
62 | ); | ||
63 | }, | ||
64 | } | ||
65 | builder.replace(lifetime_arg.syntax().text_range(), "'a"); | ||
66 | }, | ||
67 | ) | ||
68 | } | ||
69 | _ => None, | ||
70 | } | ||
71 | } | ||
72 | |||
73 | #[cfg(test)] | ||
74 | mod tests { | ||
75 | use super::*; | ||
76 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
77 | |||
78 | #[test] | ||
79 | fn test_example_case() { | ||
80 | check_assist( | ||
81 | change_lifetime_anon_to_named, | ||
82 | r#"impl Cursor<'_<|>> { | ||
83 | fn node(self) -> &SyntaxNode { | ||
84 | match self { | ||
85 | Cursor::Replace(node) | Cursor::Before(node) => node, | ||
86 | } | ||
87 | } | ||
88 | }"#, | ||
89 | r#"impl<'a> Cursor<'a> { | ||
90 | fn node(self) -> &SyntaxNode { | ||
91 | match self { | ||
92 | Cursor::Replace(node) | Cursor::Before(node) => node, | ||
93 | } | ||
94 | } | ||
95 | }"#, | ||
96 | ); | ||
97 | } | ||
98 | |||
99 | #[test] | ||
100 | fn test_example_case_simplified() { | ||
101 | check_assist( | ||
102 | change_lifetime_anon_to_named, | ||
103 | r#"impl Cursor<'_<|>> {"#, | ||
104 | r#"impl<'a> Cursor<'a> {"#, | ||
105 | ); | ||
106 | } | ||
107 | |||
108 | #[test] | ||
109 | fn test_not_applicable() { | ||
110 | check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<'_><|> {"#); | ||
111 | check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<|><'_> {"#); | ||
112 | check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<'a<|>> {"#); | ||
113 | } | ||
114 | |||
115 | #[test] | ||
116 | fn test_with_type_parameter() { | ||
117 | check_assist( | ||
118 | change_lifetime_anon_to_named, | ||
119 | r#"impl<T> Cursor<T, '_<|>>"#, | ||
120 | r#"impl<T, 'a> Cursor<T, 'a>"#, | ||
121 | ); | ||
122 | } | ||
123 | } | ||
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 464bc03dd..3f8f7ffbf 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -112,6 +112,7 @@ mod handlers { | |||
112 | mod add_turbo_fish; | 112 | mod add_turbo_fish; |
113 | mod apply_demorgan; | 113 | mod apply_demorgan; |
114 | mod auto_import; | 114 | mod auto_import; |
115 | mod change_lifetime_anon_to_named; | ||
115 | mod change_return_type_to_result; | 116 | mod change_return_type_to_result; |
116 | mod change_visibility; | 117 | mod change_visibility; |
117 | mod early_return; | 118 | mod early_return; |
@@ -151,6 +152,7 @@ mod handlers { | |||
151 | add_turbo_fish::add_turbo_fish, | 152 | add_turbo_fish::add_turbo_fish, |
152 | apply_demorgan::apply_demorgan, | 153 | apply_demorgan::apply_demorgan, |
153 | auto_import::auto_import, | 154 | auto_import::auto_import, |
155 | change_lifetime_anon_to_named::change_lifetime_anon_to_named, | ||
154 | change_return_type_to_result::change_return_type_to_result, | 156 | change_return_type_to_result::change_return_type_to_result, |
155 | change_visibility::change_visibility, | 157 | change_visibility::change_visibility, |
156 | early_return::convert_to_guarded_return, | 158 | early_return::convert_to_guarded_return, |
diff --git a/docs/user/assists.md b/docs/user/assists.md index 4ad7ea59d..29b64330e 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md | |||
@@ -259,6 +259,30 @@ fn main() { | |||
259 | } | 259 | } |
260 | ``` | 260 | ``` |
261 | 261 | ||
262 | ## `change_lifetime_anon_to_named` | ||
263 | |||
264 | Change an anonymous lifetime to a named lifetime. | ||
265 | |||
266 | ```rust | ||
267 | // BEFORE | ||
268 | impl Cursor<'_<|>> { | ||
269 | fn node(self) -> &SyntaxNode { | ||
270 | match self { | ||
271 | Cursor::Replace(node) | Cursor::Before(node) => node, | ||
272 | } | ||
273 | } | ||
274 | } | ||
275 | |||
276 | // AFTER | ||
277 | impl<'a> Cursor<'a> { | ||
278 | fn node(self) -> &SyntaxNode { | ||
279 | match self { | ||
280 | Cursor::Replace(node) | Cursor::Before(node) => node, | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | ``` | ||
285 | |||
262 | ## `change_return_type_to_result` | 286 | ## `change_return_type_to_result` |
263 | 287 | ||
264 | Change the function's return type to Result. | 288 | Change the function's return type to Result. |