Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 4717662

Browse files
authored
Merge pull request rust-lang#18690 from Giga-Bowser/extract-variable-string
feat: Use string literal contents as a name when extracting into variable
2 parents 99a4592 + 5e196f0 commit 4717662

File tree

1 file changed

+168
-15
lines changed

1 file changed

+168
-15
lines changed

src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs

Lines changed: 168 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use hir::{HirDisplay, TypeInfo};
2-
use ide_db::{assists::GroupLabel, syntax_helpers::suggest_name};
2+
use ide_db::{
3+
assists::GroupLabel,
4+
syntax_helpers::{suggest_name, LexedStr},
5+
};
36
use syntax::{
47
ast::{
58
self, edit::IndentLevel, edit_in_place::Indent, make, syntax_factory::SyntaxFactory,
@@ -320,24 +323,58 @@ impl ExtractionKind {
320323
ctx: &AssistContext<'_>,
321324
to_extract: &ast::Expr,
322325
) -> (String, SyntaxNode) {
323-
let field_shorthand = to_extract
324-
.syntax()
325-
.parent()
326-
.and_then(ast::RecordExprField::cast)
327-
.filter(|field| field.name_ref().is_some());
328-
let (var_name, expr_replace) = match field_shorthand {
329-
Some(field) => (field.to_string(), field.syntax().clone()),
330-
None => {
331-
(suggest_name::for_variable(to_extract, &ctx.sema), to_extract.syntax().clone())
326+
// We only do this sort of extraction for fields because they should have lowercase names
327+
if let ExtractionKind::Variable = self {
328+
let field_shorthand = to_extract
329+
.syntax()
330+
.parent()
331+
.and_then(ast::RecordExprField::cast)
332+
.filter(|field| field.name_ref().is_some());
333+
334+
if let Some(field) = field_shorthand {
335+
return (field.to_string(), field.syntax().clone());
332336
}
337+
}
338+
339+
let var_name = if let Some(literal_name) = get_literal_name(ctx, to_extract) {
340+
literal_name
341+
} else {
342+
suggest_name::for_variable(to_extract, &ctx.sema)
333343
};
334344

335345
let var_name = match self {
336-
ExtractionKind::Variable => var_name,
346+
ExtractionKind::Variable => var_name.to_lowercase(),
337347
ExtractionKind::Constant | ExtractionKind::Static => var_name.to_uppercase(),
338348
};
339349

340-
(var_name, expr_replace)
350+
(var_name, to_extract.syntax().clone())
351+
}
352+
}
353+
354+
fn get_literal_name(ctx: &AssistContext<'_>, expr: &ast::Expr) -> Option<String> {
355+
let literal = match expr {
356+
ast::Expr::Literal(literal) => literal,
357+
_ => return None,
358+
};
359+
let inner = match literal.kind() {
360+
ast::LiteralKind::String(string) => string.value().ok()?.into_owned(),
361+
ast::LiteralKind::ByteString(byte_string) => {
362+
String::from_utf8(byte_string.value().ok()?.into_owned()).ok()?
363+
}
364+
ast::LiteralKind::CString(cstring) => {
365+
String::from_utf8(cstring.value().ok()?.into_owned()).ok()?
366+
}
367+
_ => return None,
368+
};
369+
370+
// Entirely arbitrary
371+
if inner.len() > 32 {
372+
return None;
373+
}
374+
375+
match LexedStr::single_token(ctx.file_id().edition(), &inner) {
376+
Some((SyntaxKind::IDENT, None)) => Some(inner),
377+
_ => None,
341378
}
342379
}
343380

@@ -493,7 +530,7 @@ fn main() {
493530
"#,
494531
r#"
495532
fn main() {
496-
let $0var_name = "hello";
533+
let $0hello = "hello";
497534
}
498535
"#,
499536
"Extract into variable",
@@ -588,7 +625,7 @@ fn main() {
588625
"#,
589626
r#"
590627
fn main() {
591-
const $0VAR_NAME: &str = "hello";
628+
const $0HELLO: &str = "hello";
592629
}
593630
"#,
594631
"Extract into constant",
@@ -683,7 +720,7 @@ fn main() {
683720
"#,
684721
r#"
685722
fn main() {
686-
static $0VAR_NAME: &str = "hello";
723+
static $0HELLO: &str = "hello";
687724
}
688725
"#,
689726
"Extract into static",
@@ -2479,4 +2516,120 @@ fn foo() {
24792516
"Extract into variable",
24802517
);
24812518
}
2519+
2520+
#[test]
2521+
fn extract_string_literal() {
2522+
check_assist_by_label(
2523+
extract_variable,
2524+
r#"
2525+
struct Entry(&str);
2526+
fn foo() {
2527+
let entry = Entry($0"Hello"$0);
2528+
}
2529+
"#,
2530+
r#"
2531+
struct Entry(&str);
2532+
fn foo() {
2533+
let $0hello = "Hello";
2534+
let entry = Entry(hello);
2535+
}
2536+
"#,
2537+
"Extract into variable",
2538+
);
2539+
2540+
check_assist_by_label(
2541+
extract_variable,
2542+
r#"
2543+
struct Entry(&str);
2544+
fn foo() {
2545+
let entry = Entry($0"Hello"$0);
2546+
}
2547+
"#,
2548+
r#"
2549+
struct Entry(&str);
2550+
fn foo() {
2551+
const $0HELLO: &str = "Hello";
2552+
let entry = Entry(HELLO);
2553+
}
2554+
"#,
2555+
"Extract into constant",
2556+
);
2557+
2558+
check_assist_by_label(
2559+
extract_variable,
2560+
r#"
2561+
struct Entry(&str);
2562+
fn foo() {
2563+
let entry = Entry($0"Hello"$0);
2564+
}
2565+
"#,
2566+
r#"
2567+
struct Entry(&str);
2568+
fn foo() {
2569+
static $0HELLO: &str = "Hello";
2570+
let entry = Entry(HELLO);
2571+
}
2572+
"#,
2573+
"Extract into static",
2574+
);
2575+
}
2576+
2577+
#[test]
2578+
fn extract_variable_string_literal_use_field_shorthand() {
2579+
// When field shorthand is available, it should
2580+
// only be used when extracting into a variable
2581+
check_assist_by_label(
2582+
extract_variable,
2583+
r#"
2584+
struct Entry { message: &str }
2585+
fn foo() {
2586+
let entry = Entry { message: $0"Hello"$0 };
2587+
}
2588+
"#,
2589+
r#"
2590+
struct Entry { message: &str }
2591+
fn foo() {
2592+
let $0message = "Hello";
2593+
let entry = Entry { message };
2594+
}
2595+
"#,
2596+
"Extract into variable",
2597+
);
2598+
2599+
check_assist_by_label(
2600+
extract_variable,
2601+
r#"
2602+
struct Entry { message: &str }
2603+
fn foo() {
2604+
let entry = Entry { message: $0"Hello"$0 };
2605+
}
2606+
"#,
2607+
r#"
2608+
struct Entry { message: &str }
2609+
fn foo() {
2610+
const $0HELLO: &str = "Hello";
2611+
let entry = Entry { message: HELLO };
2612+
}
2613+
"#,
2614+
"Extract into constant",
2615+
);
2616+
2617+
check_assist_by_label(
2618+
extract_variable,
2619+
r#"
2620+
struct Entry { message: &str }
2621+
fn foo() {
2622+
let entry = Entry { message: $0"Hello"$0 };
2623+
}
2624+
"#,
2625+
r#"
2626+
struct Entry { message: &str }
2627+
fn foo() {
2628+
static $0HELLO: &str = "Hello";
2629+
let entry = Entry { message: HELLO };
2630+
}
2631+
"#,
2632+
"Extract into static",
2633+
);
2634+
}
24822635
}

0 commit comments

Comments
 (0)