mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Merge pull request #18690 from Giga-Bowser/extract-variable-string
feat: Use string literal contents as a name when extracting into variable
This commit is contained in:
commit
61c222e1af
1 changed files with 168 additions and 15 deletions
|
@ -1,5 +1,8 @@
|
||||||
use hir::{HirDisplay, TypeInfo};
|
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::{
|
use syntax::{
|
||||||
ast::{
|
ast::{
|
||||||
self, edit::IndentLevel, edit_in_place::Indent, make, syntax_factory::SyntaxFactory,
|
self, edit::IndentLevel, edit_in_place::Indent, make, syntax_factory::SyntaxFactory,
|
||||||
|
@ -320,24 +323,58 @@ impl ExtractionKind {
|
||||||
ctx: &AssistContext<'_>,
|
ctx: &AssistContext<'_>,
|
||||||
to_extract: &ast::Expr,
|
to_extract: &ast::Expr,
|
||||||
) -> (String, SyntaxNode) {
|
) -> (String, SyntaxNode) {
|
||||||
let field_shorthand = to_extract
|
// We only do this sort of extraction for fields because they should have lowercase names
|
||||||
.syntax()
|
if let ExtractionKind::Variable = self {
|
||||||
.parent()
|
let field_shorthand = to_extract
|
||||||
.and_then(ast::RecordExprField::cast)
|
.syntax()
|
||||||
.filter(|field| field.name_ref().is_some());
|
.parent()
|
||||||
let (var_name, expr_replace) = match field_shorthand {
|
.and_then(ast::RecordExprField::cast)
|
||||||
Some(field) => (field.to_string(), field.syntax().clone()),
|
.filter(|field| field.name_ref().is_some());
|
||||||
None => {
|
|
||||||
(suggest_name::for_variable(to_extract, &ctx.sema), to_extract.syntax().clone())
|
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 {
|
let var_name = match self {
|
||||||
ExtractionKind::Variable => var_name,
|
ExtractionKind::Variable => var_name.to_lowercase(),
|
||||||
ExtractionKind::Constant | ExtractionKind::Static => var_name.to_uppercase(),
|
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#"
|
r#"
|
||||||
fn main() {
|
fn main() {
|
||||||
let $0var_name = "hello";
|
let $0hello = "hello";
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
"Extract into variable",
|
"Extract into variable",
|
||||||
|
@ -588,7 +625,7 @@ fn main() {
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
fn main() {
|
fn main() {
|
||||||
const $0VAR_NAME: &str = "hello";
|
const $0HELLO: &str = "hello";
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
"Extract into constant",
|
"Extract into constant",
|
||||||
|
@ -683,7 +720,7 @@ fn main() {
|
||||||
"#,
|
"#,
|
||||||
r#"
|
r#"
|
||||||
fn main() {
|
fn main() {
|
||||||
static $0VAR_NAME: &str = "hello";
|
static $0HELLO: &str = "hello";
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
"Extract into static",
|
"Extract into static",
|
||||||
|
@ -2479,4 +2516,120 @@ fn foo() {
|
||||||
"Extract into variable",
|
"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",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue