From 4d1a4dc0d6d750514e010d1e2be50a8038109b5e Mon Sep 17 00:00:00 2001 From: longfangsong Date: Mon, 4 Oct 2021 23:53:32 +0800 Subject: [PATCH 1/4] Add generate_constant assist --- .../src/handlers/generate_constant.rs | 130 ++++++++++++++++++ crates/ide_assists/src/lib.rs | 2 + crates/ide_assists/src/tests/generated.rs | 22 +++ 3 files changed, 154 insertions(+) create mode 100644 crates/ide_assists/src/handlers/generate_constant.rs diff --git a/crates/ide_assists/src/handlers/generate_constant.rs b/crates/ide_assists/src/handlers/generate_constant.rs new file mode 100644 index 0000000000..2e3e3be48d --- /dev/null +++ b/crates/ide_assists/src/handlers/generate_constant.rs @@ -0,0 +1,130 @@ +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 statement = ctx.find_node_at_offset::()?; + let arg_list = ctx.find_node_at_offset::()?; + let expr = ctx.find_node_at_offset::()?; + let constant_token = ctx.find_node_at_offset::()?; + 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 !arg_list.syntax().text_range().contains_range(constant_token.syntax().text_range()) { + return None; + } + 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(); + let statement_syntax = statement.syntax().clone_for_update(); + acc.add( + AssistId("generate_constant", AssistKind::QuickFix), + "Generate constant", + target, + |builder| { + builder.replace( + statement.syntax().text_range(), + format!( + "const {}: {} = $0;\n{}{}", + constant_token, + type_name, + indent, + statement_syntax.text() + ), + ); + }, + ) +} + +#[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); +}"#, + ); + } +} diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index d41d06f2d9..bec51c508b 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs @@ -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, diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 9ad7b6097a..1daf96b827 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs @@ -648,6 +648,28 @@ fn foo() { } ) } +#[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( From fab238adf632f05993af992dca2f411e5ac02dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=99=E6=96=B9=E6=B7=9E?= Date: Wed, 6 Oct 2021 10:06:46 +0800 Subject: [PATCH 2/4] Update crates/ide_assists/src/handlers/generate_constant.rs Co-authored-by: Lukas Wirth --- crates/ide_assists/src/handlers/generate_constant.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/ide_assists/src/handlers/generate_constant.rs b/crates/ide_assists/src/handlers/generate_constant.rs index 2e3e3be48d..a296f1cefc 100644 --- a/crates/ide_assists/src/handlers/generate_constant.rs +++ b/crates/ide_assists/src/handlers/generate_constant.rs @@ -31,10 +31,9 @@ use syntax::{ // ``` pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { - let statement = ctx.find_node_at_offset::()?; - let arg_list = ctx.find_node_at_offset::()?; - let expr = ctx.find_node_at_offset::()?; let constant_token = ctx.find_node_at_offset::()?; + 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()?; From 7228dbadcaf0d97b0da421127e703d921b2bce53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=99=E6=96=B9=E6=B7=9E?= Date: Wed, 6 Oct 2021 10:06:51 +0800 Subject: [PATCH 3/4] Update crates/ide_assists/src/handlers/generate_constant.rs Co-authored-by: Lukas Wirth --- crates/ide_assists/src/handlers/generate_constant.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/ide_assists/src/handlers/generate_constant.rs b/crates/ide_assists/src/handlers/generate_constant.rs index a296f1cefc..5332484f49 100644 --- a/crates/ide_assists/src/handlers/generate_constant.rs +++ b/crates/ide_assists/src/handlers/generate_constant.rs @@ -57,14 +57,13 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext) -> Optio "Generate constant", target, |builder| { - builder.replace( - statement.syntax().text_range(), + builder.insert( + statement.syntax().text_range.start(), format!( - "const {}: {} = $0;\n{}{}", + "const {}: {} = $0;\n{}", constant_token, type_name, - indent, - statement_syntax.text() + indent ), ); }, From 3fde682bcfda4d2dc435330c9f0733b90d7c54c6 Mon Sep 17 00:00:00 2001 From: longfangsong Date: Wed, 6 Oct 2021 10:14:12 +0800 Subject: [PATCH 4/4] cleanup --- .../ide_assists/src/handlers/generate_constant.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/crates/ide_assists/src/handlers/generate_constant.rs b/crates/ide_assists/src/handlers/generate_constant.rs index 5332484f49..52ae60e2ed 100644 --- a/crates/ide_assists/src/handlers/generate_constant.rs +++ b/crates/ide_assists/src/handlers/generate_constant.rs @@ -39,9 +39,6 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext) -> Optio 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 !arg_list.syntax().text_range().contains_range(constant_token.syntax().text_range()) { - return None; - } if constant_token.to_string().chars().any(|it| !(it.is_uppercase() || it == '_')) { cov_mark::hit!(not_constant_name); return None; @@ -51,20 +48,14 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext) -> Optio return None; } let target = statement.syntax().parent()?.text_range(); - let statement_syntax = statement.syntax().clone_for_update(); 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 - ), + statement.syntax().text_range().start(), + format!("const {}: {} = $0;\n{}", constant_token, type_name, indent), ); }, )