diff options
Diffstat (limited to 'crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs')
-rw-r--r-- | crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs | 123 |
1 files changed, 123 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 | } | ||