aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs123
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--docs/user/assists.md24
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 @@
1use crate::{AssistContext, AssistId, Assists};
2use 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?
28pub(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)]
74mod 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
264Change an anonymous lifetime to a named lifetime.
265
266```rust
267// BEFORE
268impl Cursor<'_<|>> {
269 fn node(self) -> &SyntaxNode {
270 match self {
271 Cursor::Replace(node) | Cursor::Before(node) => node,
272 }
273 }
274}
275
276// AFTER
277impl<'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
264Change the function's return type to Result. 288Change the function's return type to Result.