aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/handlers')
-rw-r--r--crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs123
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 @@
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}