2021-10-04 10:53:32 -05:00
|
|
|
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>()?;
|
2021-10-05 21:06:46 -05:00
|
|
|
let expr = constant_token.syntax().ancestors().find_map(ast::Expr::cast)?;
|
|
|
|
let statement = expr.syntax().ancestors().find_map(ast::Stmt::cast)?;
|
2021-10-04 10:53:32 -05:00
|
|
|
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| {
|
2021-10-05 21:06:51 -05:00
|
|
|
builder.insert(
|
|
|
|
statement.syntax().text_range.start(),
|
2021-10-04 10:53:32 -05:00
|
|
|
format!(
|
2021-10-05 21:06:51 -05:00
|
|
|
"const {}: {} = $0;\n{}",
|
2021-10-04 10:53:32 -05:00
|
|
|
constant_token,
|
|
|
|
type_name,
|
2021-10-05 21:06:51 -05:00
|
|
|
indent
|
2021-10-04 10:53:32 -05:00
|
|
|
),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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);
|
|
|
|
}"#,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|