about summary refs log tree commit diff
path: root/src/tools/rust-analyzer
diff options
context:
space:
mode:
authorGiga Bowser <45986823+Giga-Bowser@users.noreply.github.com>2024-12-14 13:36:11 -0500
committerGiga Bowser <45986823+Giga-Bowser@users.noreply.github.com>2024-12-14 13:36:57 -0500
commit5e196f07419443d17ffc26a61e275da27261de45 (patch)
treefc574756b128f4554b5fabf2eeda48cbd5166b94 /src/tools/rust-analyzer
parent97de6dce98b8a6be93bc3993323781b2a32c526a (diff)
downloadrust-5e196f07419443d17ffc26a61e275da27261de45.tar.gz
rust-5e196f07419443d17ffc26a61e275da27261de45.zip
feat: Use string literal contents as a name when extracting into variable
Diffstat (limited to 'src/tools/rust-analyzer')
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs183
1 files changed, 168 insertions, 15 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
index a8d71ed7f4d..6735d7dcbe1 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
@@ -1,5 +1,8 @@
 use hir::{HirDisplay, TypeInfo};
-use ide_db::{assists::GroupLabel, syntax_helpers::suggest_name};
+use ide_db::{
+    assists::GroupLabel,
+    syntax_helpers::{suggest_name, LexedStr},
+};
 use syntax::{
     ast::{
         self, edit::IndentLevel, edit_in_place::Indent, make, syntax_factory::SyntaxFactory,
@@ -320,24 +323,58 @@ impl ExtractionKind {
         ctx: &AssistContext<'_>,
         to_extract: &ast::Expr,
     ) -> (String, SyntaxNode) {
-        let field_shorthand = to_extract
-            .syntax()
-            .parent()
-            .and_then(ast::RecordExprField::cast)
-            .filter(|field| field.name_ref().is_some());
-        let (var_name, expr_replace) = match field_shorthand {
-            Some(field) => (field.to_string(), field.syntax().clone()),
-            None => {
-                (suggest_name::for_variable(to_extract, &ctx.sema), to_extract.syntax().clone())
+        // We only do this sort of extraction for fields because they should have lowercase names
+        if let ExtractionKind::Variable = self {
+            let field_shorthand = to_extract
+                .syntax()
+                .parent()
+                .and_then(ast::RecordExprField::cast)
+                .filter(|field| field.name_ref().is_some());
+
+            if let Some(field) = field_shorthand {
+                return (field.to_string(), field.syntax().clone());
             }
+        }
+
+        let var_name = if let Some(literal_name) = get_literal_name(ctx, to_extract) {
+            literal_name
+        } else {
+            suggest_name::for_variable(to_extract, &ctx.sema)
         };
 
         let var_name = match self {
-            ExtractionKind::Variable => var_name,
+            ExtractionKind::Variable => var_name.to_lowercase(),
             ExtractionKind::Constant | ExtractionKind::Static => var_name.to_uppercase(),
         };
 
-        (var_name, expr_replace)
+        (var_name, to_extract.syntax().clone())
+    }
+}
+
+fn get_literal_name(ctx: &AssistContext<'_>, expr: &ast::Expr) -> Option<String> {
+    let literal = match expr {
+        ast::Expr::Literal(literal) => literal,
+        _ => return None,
+    };
+    let inner = match literal.kind() {
+        ast::LiteralKind::String(string) => string.value().ok()?.into_owned(),
+        ast::LiteralKind::ByteString(byte_string) => {
+            String::from_utf8(byte_string.value().ok()?.into_owned()).ok()?
+        }
+        ast::LiteralKind::CString(cstring) => {
+            String::from_utf8(cstring.value().ok()?.into_owned()).ok()?
+        }
+        _ => return None,
+    };
+
+    // Entirely arbitrary
+    if inner.len() > 32 {
+        return None;
+    }
+
+    match LexedStr::single_token(ctx.file_id().edition(), &inner) {
+        Some((SyntaxKind::IDENT, None)) => Some(inner),
+        _ => None,
     }
 }
 
@@ -493,7 +530,7 @@ fn main() {
 "#,
             r#"
 fn main() {
-    let $0var_name = "hello";
+    let $0hello = "hello";
 }
 "#,
             "Extract into variable",
@@ -588,7 +625,7 @@ fn main() {
 "#,
             r#"
 fn main() {
-    const $0VAR_NAME: &str = "hello";
+    const $0HELLO: &str = "hello";
 }
 "#,
             "Extract into constant",
@@ -683,7 +720,7 @@ fn main() {
 "#,
             r#"
 fn main() {
-    static $0VAR_NAME: &str = "hello";
+    static $0HELLO: &str = "hello";
 }
 "#,
             "Extract into static",
@@ -2479,4 +2516,120 @@ fn foo() {
             "Extract into variable",
         );
     }
+
+    #[test]
+    fn extract_string_literal() {
+        check_assist_by_label(
+            extract_variable,
+            r#"
+struct Entry(&str);
+fn foo() {
+    let entry = Entry($0"Hello"$0);
+}
+"#,
+            r#"
+struct Entry(&str);
+fn foo() {
+    let $0hello = "Hello";
+    let entry = Entry(hello);
+}
+"#,
+            "Extract into variable",
+        );
+
+        check_assist_by_label(
+            extract_variable,
+            r#"
+struct Entry(&str);
+fn foo() {
+    let entry = Entry($0"Hello"$0);
+}
+"#,
+            r#"
+struct Entry(&str);
+fn foo() {
+    const $0HELLO: &str = "Hello";
+    let entry = Entry(HELLO);
+}
+"#,
+            "Extract into constant",
+        );
+
+        check_assist_by_label(
+            extract_variable,
+            r#"
+struct Entry(&str);
+fn foo() {
+    let entry = Entry($0"Hello"$0);
+}
+"#,
+            r#"
+struct Entry(&str);
+fn foo() {
+    static $0HELLO: &str = "Hello";
+    let entry = Entry(HELLO);
+}
+"#,
+            "Extract into static",
+        );
+    }
+
+    #[test]
+    fn extract_variable_string_literal_use_field_shorthand() {
+        // When field shorthand is available, it should
+        // only be used when extracting into a variable
+        check_assist_by_label(
+            extract_variable,
+            r#"
+struct Entry { message: &str }
+fn foo() {
+    let entry = Entry { message: $0"Hello"$0 };
+}
+"#,
+            r#"
+struct Entry { message: &str }
+fn foo() {
+    let $0message = "Hello";
+    let entry = Entry { message };
+}
+"#,
+            "Extract into variable",
+        );
+
+        check_assist_by_label(
+            extract_variable,
+            r#"
+struct Entry { message: &str }
+fn foo() {
+    let entry = Entry { message: $0"Hello"$0 };
+}
+"#,
+            r#"
+struct Entry { message: &str }
+fn foo() {
+    const $0HELLO: &str = "Hello";
+    let entry = Entry { message: HELLO };
+}
+"#,
+            "Extract into constant",
+        );
+
+        check_assist_by_label(
+            extract_variable,
+            r#"
+struct Entry { message: &str }
+fn foo() {
+    let entry = Entry { message: $0"Hello"$0 };
+}
+"#,
+            r#"
+struct Entry { message: &str }
+fn foo() {
+    static $0HELLO: &str = "Hello";
+    let entry = Entry { message: HELLO };
+}
+"#,
+            "Extract into static",
+        );
+    }
 }