10459: feat: Add generate constant assist r=Veykril a=longfangsong

Close #10330.
![demo(1)](https://user-images.githubusercontent.com/13777628/135885262-c80de86f-5555-4f84-9508-822243f8a876.gif)



Co-authored-by: longfangsong <longfangsong@icloud.com>
Co-authored-by: 龙方淞 <longfangsong@icloud.com>
This commit is contained in:
bors[bot] 2021-10-06 09:38:58 +00:00 committed by GitHub
commit 4cfe237a56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 143 additions and 0 deletions

View File

@ -0,0 +1,119 @@
use crate::assist_context::{AssistContext, Assists};
use hir::HirDisplay;
use ide_db::{
assists::{AssistId, AssistKind},
defs::NameRefClass,
};
use syntax::{
ast::{self, edit::IndentLevel},
AstNode,
};
// Assist: generate_constant
//
// Generate a named constant.
//
// ```
// struct S { i: usize }
// impl S { pub fn new(n: usize) {} }
// fn main() {
// let v = S::new(CAPA$0CITY);
// }
// ```
// ->
// ```
// struct S { i: usize }
// impl S { pub fn new(n: usize) {} }
// fn main() {
// const CAPACITY: usize = $0;
// let v = S::new(CAPACITY);
// }
// ```
pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let constant_token = ctx.find_node_at_offset::<ast::NameRef>()?;
let expr = constant_token.syntax().ancestors().find_map(ast::Expr::cast)?;
let statement = expr.syntax().ancestors().find_map(ast::Stmt::cast)?;
let ty = ctx.sema.type_of_expr(&expr)?;
let scope = ctx.sema.scope(statement.syntax());
let module = scope.module()?;
let type_name = ty.original().display_source_code(ctx.db(), module.into()).ok()?;
let indent = IndentLevel::from_node(statement.syntax());
if constant_token.to_string().chars().any(|it| !(it.is_uppercase() || it == '_')) {
cov_mark::hit!(not_constant_name);
return None;
}
if NameRefClass::classify(&ctx.sema, &constant_token).is_some() {
cov_mark::hit!(already_defined);
return None;
}
let target = statement.syntax().parent()?.text_range();
acc.add(
AssistId("generate_constant", AssistKind::QuickFix),
"Generate constant",
target,
|builder| {
builder.insert(
statement.syntax().text_range().start(),
format!("const {}: {} = $0;\n{}", constant_token, type_name, indent),
);
},
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{check_assist, check_assist_not_applicable};
#[test]
fn test_trivial() {
check_assist(
generate_constant,
r#"struct S { i: usize }
impl S {
pub fn new(n: usize) {}
}
fn main() {
let v = S::new(CAPA$0CITY);
}"#,
r#"struct S { i: usize }
impl S {
pub fn new(n: usize) {}
}
fn main() {
const CAPACITY: usize = $0;
let v = S::new(CAPACITY);
}"#,
);
}
#[test]
fn test_wont_apply_when_defined() {
cov_mark::check!(already_defined);
check_assist_not_applicable(
generate_constant,
r#"struct S { i: usize }
impl S {
pub fn new(n: usize) {}
}
fn main() {
const CAPACITY: usize = 10;
let v = S::new(CAPAC$0ITY);
}"#,
);
}
#[test]
fn test_wont_apply_when_maybe_not_constant() {
cov_mark::check!(not_constant_name);
check_assist_not_applicable(
generate_constant,
r#"struct S { i: usize }
impl S {
pub fn new(n: usize) {}
}
fn main() {
let v = S::new(capa$0city);
}"#,
);
}
}

View File

@ -130,6 +130,7 @@ mod handlers {
mod flip_binexpr;
mod flip_comma;
mod flip_trait_bound;
mod generate_constant;
mod generate_default_from_enum_variant;
mod generate_default_from_new;
mod generate_deref;
@ -205,6 +206,7 @@ mod handlers {
flip_binexpr::flip_binexpr,
flip_comma::flip_comma,
flip_trait_bound::flip_trait_bound,
generate_constant::generate_constant,
generate_default_from_enum_variant::generate_default_from_enum_variant,
generate_default_from_new::generate_default_from_new,
generate_deref::generate_deref,

View File

@ -648,6 +648,28 @@ fn foo<T: Copy + Clone>() { }
)
}
#[test]
fn doctest_generate_constant() {
check_doc_test(
"generate_constant",
r#####"
struct S { i: usize }
impl S { pub fn new(n: usize) {} }
fn main() {
let v = S::new(CAPA$0CITY);
}
"#####,
r#####"
struct S { i: usize }
impl S { pub fn new(n: usize) {} }
fn main() {
const CAPACITY: usize = $0;
let v = S::new(CAPACITY);
}
"#####,
)
}
#[test]
fn doctest_generate_default_from_enum_variant() {
check_doc_test(