Skip to content

Commit afc6827

Browse files
cpud36matklad
authored andcommitted
pull out suggest_name::* to utils; enchance heuristics
1 parent f915ab7 commit afc6827

File tree

4 files changed

+821
-89
lines changed

4 files changed

+821
-89
lines changed

crates/ide_assists/src/handlers/extract_variable.rs

Lines changed: 4 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
use itertools::Itertools;
2-
use stdx::{format_to, to_lower_snake_case};
1+
use stdx::format_to;
32
use syntax::{
4-
ast::{self, AstNode, NameOwner},
3+
ast::{self, AstNode},
54
SyntaxKind::{
65
BLOCK_EXPR, BREAK_EXPR, CLOSURE_EXPR, COMMENT, LOOP_EXPR, MATCH_ARM, PATH_EXPR, RETURN_EXPR,
76
},
87
SyntaxNode,
98
};
109
use test_utils::mark;
1110

12-
use crate::{AssistContext, AssistId, AssistKind, Assists};
11+
use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
1312

1413
// Assist: extract_variable
1514
//
@@ -55,7 +54,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
5554

5655
let var_name = match &field_shorthand {
5756
Some(it) => it.to_string(),
58-
None => suggest_variable_name(ctx, &to_extract),
57+
None => suggest_name::variable(&to_extract, &ctx.sema),
5958
};
6059
let expr_range = match &field_shorthand {
6160
Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()),
@@ -174,89 +173,6 @@ impl Anchor {
174173
}
175174
}
176175

177-
fn suggest_variable_name(ctx: &AssistContext, expr: &ast::Expr) -> String {
178-
// FIXME: account for existing names in the scope
179-
suggest_name_from_param(ctx, expr)
180-
.or_else(|| suggest_name_from_func(expr))
181-
.or_else(|| suggest_name_from_method(expr))
182-
.or_else(|| suggest_name_by_type(ctx, expr))
183-
.unwrap_or_else(|| "var_name".to_string())
184-
}
185-
186-
fn normalize_name(name: &str) -> Option<String> {
187-
let name = to_lower_snake_case(name);
188-
189-
let useless_names = ["new", "default", "some", "none", "ok", "err"];
190-
if useless_names.contains(&name.as_str()) {
191-
return None;
192-
}
193-
194-
Some(name)
195-
}
196-
197-
fn suggest_name_from_func(expr: &ast::Expr) -> Option<String> {
198-
let call = match expr {
199-
ast::Expr::CallExpr(call) => call,
200-
_ => return None,
201-
};
202-
let func = match call.expr()? {
203-
ast::Expr::PathExpr(path) => path,
204-
_ => return None,
205-
};
206-
let ident = func.path()?.segment()?.name_ref()?.ident_token()?;
207-
normalize_name(ident.text())
208-
}
209-
210-
fn suggest_name_from_method(expr: &ast::Expr) -> Option<String> {
211-
let method = match expr {
212-
ast::Expr::MethodCallExpr(call) => call,
213-
_ => return None,
214-
};
215-
let ident = method.name_ref()?.ident_token()?;
216-
normalize_name(ident.text())
217-
}
218-
219-
fn suggest_name_from_param(ctx: &AssistContext, expr: &ast::Expr) -> Option<String> {
220-
let arg_list = expr.syntax().parent().and_then(ast::ArgList::cast)?;
221-
let args_parent = arg_list.syntax().parent()?;
222-
let func = if let Some(call) = ast::CallExpr::cast(args_parent.clone()) {
223-
let func = call.expr()?;
224-
let func_ty = ctx.sema.type_of_expr(&func)?;
225-
func_ty.as_callable(ctx.db())?
226-
} else if let Some(method) = ast::MethodCallExpr::cast(args_parent) {
227-
ctx.sema.resolve_method_call_as_callable(&method)?
228-
} else {
229-
return None;
230-
};
231-
232-
let (idx, _) = arg_list.args().find_position(|it| it == expr).unwrap();
233-
let (pat, _) = func.params(ctx.db()).into_iter().nth(idx)?;
234-
let param = match pat? {
235-
either::Either::Right(ast::Pat::IdentPat(param)) => param,
236-
_ => return None,
237-
};
238-
let name = param.name()?;
239-
normalize_name(&name.to_string())
240-
}
241-
242-
fn suggest_name_by_type(ctx: &AssistContext, expr: &ast::Expr) -> Option<String> {
243-
let ty = ctx.sema.type_of_expr(expr)?;
244-
let ty = ty.remove_ref().unwrap_or(ty);
245-
246-
name_from_type(ty, ctx)
247-
}
248-
249-
fn name_from_type(ty: hir::Type, ctx: &AssistContext) -> Option<String> {
250-
let name = if let Some(adt) = ty.as_adt() {
251-
adt.name(ctx.db()).to_string()
252-
} else if let Some(trait_) = ty.as_dyn_trait() {
253-
trait_.name(ctx.db()).to_string()
254-
} else {
255-
return None;
256-
};
257-
normalize_name(&name)
258-
}
259-
260176
#[cfg(test)]
261177
mod tests {
262178
use test_utils::mark;

crates/ide_assists/src/tests.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use ide_db::{
1212
RootDatabase,
1313
};
1414
use stdx::{format_to, trim_indent};
15-
use syntax::TextRange;
15+
use syntax::{ast, AstNode, TextRange};
1616
use test_utils::{assert_eq_text, extract_offset};
1717

1818
use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists};
@@ -180,6 +180,50 @@ fn labels(assists: &[Assist]) -> String {
180180
labels.into_iter().collect::<String>()
181181
}
182182

183+
pub(crate) type NameSuggestion = fn(&ast::Expr, &Semantics<'_, RootDatabase>) -> Option<String>;
184+
185+
#[track_caller]
186+
pub(crate) fn check_name_suggestion(
187+
suggestion: NameSuggestion,
188+
ra_fixture: &str,
189+
suggested_name: &str,
190+
) {
191+
check_name(suggestion, ra_fixture, Some(suggested_name));
192+
}
193+
194+
#[track_caller]
195+
pub(crate) fn check_name_suggestion_not_applicable(suggestion: NameSuggestion, ra_fixture: &str) {
196+
check_name(suggestion, ra_fixture, None);
197+
}
198+
199+
#[track_caller]
200+
fn check_name(suggestion: NameSuggestion, ra_fixture: &str, expected: Option<&str>) {
201+
let (db, file_with_carret_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
202+
let frange = FileRange { file_id: file_with_carret_id, range: range_or_offset.into() };
203+
204+
let sema = Semantics::new(&db);
205+
let source_file = sema.parse(frange.file_id);
206+
let element = source_file.syntax().covering_element(frange.range);
207+
let expr =
208+
element.ancestors().find_map(ast::Expr::cast).expect("selection is not an expression");
209+
assert_eq!(
210+
expr.syntax().text_range(),
211+
frange.range,
212+
"selection is not an expression(yet contained in one)"
213+
);
214+
215+
let name = suggestion(&expr, &sema);
216+
217+
match (name, expected) {
218+
(Some(name), Some(expected_name)) => {
219+
assert_eq_text!(&name, expected_name);
220+
}
221+
(Some(_), None) => panic!("name suggestion should not be applicable"),
222+
(None, Some(_)) => panic!("name suggestion is not applicable"),
223+
(None, None) => (),
224+
}
225+
}
226+
183227
#[test]
184228
fn assist_order_field_struct() {
185229
let before = "struct Foo { $0bar: u32 }";

crates/ide_assists/src/utils.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Assorted functions shared by several assists.
22
3+
pub(crate) mod suggest_name;
4+
35
use std::ops;
46

57
use ast::TypeBoundsOwner;

0 commit comments

Comments
 (0)