|
1 | 1 | 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 | +}; |
3 | 6 | use syntax::{
|
4 | 7 | ast::{
|
5 | 8 | self, edit::IndentLevel, edit_in_place::Indent, make, syntax_factory::SyntaxFactory,
|
@@ -320,24 +323,58 @@ impl ExtractionKind {
|
320 | 323 | ctx: &AssistContext<'_>,
|
321 | 324 | to_extract: &ast::Expr,
|
322 | 325 | ) -> (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()); |
332 | 336 | }
|
| 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) |
333 | 343 | };
|
334 | 344 |
|
335 | 345 | let var_name = match self {
|
336 |
| - ExtractionKind::Variable => var_name, |
| 346 | + ExtractionKind::Variable => var_name.to_lowercase(), |
337 | 347 | ExtractionKind::Constant | ExtractionKind::Static => var_name.to_uppercase(),
|
338 | 348 | };
|
339 | 349 |
|
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, |
341 | 378 | }
|
342 | 379 | }
|
343 | 380 |
|
@@ -493,7 +530,7 @@ fn main() {
|
493 | 530 | "#,
|
494 | 531 | r#"
|
495 | 532 | fn main() {
|
496 |
| - let $0var_name = "hello"; |
| 533 | + let $0hello = "hello"; |
497 | 534 | }
|
498 | 535 | "#,
|
499 | 536 | "Extract into variable",
|
@@ -588,7 +625,7 @@ fn main() {
|
588 | 625 | "#,
|
589 | 626 | r#"
|
590 | 627 | fn main() {
|
591 |
| - const $0VAR_NAME: &str = "hello"; |
| 628 | + const $0HELLO: &str = "hello"; |
592 | 629 | }
|
593 | 630 | "#,
|
594 | 631 | "Extract into constant",
|
@@ -683,7 +720,7 @@ fn main() {
|
683 | 720 | "#,
|
684 | 721 | r#"
|
685 | 722 | fn main() {
|
686 |
| - static $0VAR_NAME: &str = "hello"; |
| 723 | + static $0HELLO: &str = "hello"; |
687 | 724 | }
|
688 | 725 | "#,
|
689 | 726 | "Extract into static",
|
@@ -2479,4 +2516,120 @@ fn foo() {
|
2479 | 2516 | "Extract into variable",
|
2480 | 2517 | );
|
2481 | 2518 | }
|
| 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 | + } |
2482 | 2635 | }
|
0 commit comments