Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-db/src/syntax_helpers/suggest_name.rs')
| -rw-r--r-- | crates/ide-db/src/syntax_helpers/suggest_name.rs | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/crates/ide-db/src/syntax_helpers/suggest_name.rs b/crates/ide-db/src/syntax_helpers/suggest_name.rs index 557c95f704..0a7141c19b 100644 --- a/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -31,6 +31,12 @@ const USELESS_NAME_PREFIXES: &[&str] = &["from_", "with_", "into_"]; /// `Result<User, Error>` -> `User` const WRAPPER_TYPES: &[&str] = &["Box", "Arc", "Rc", "Option", "Result"]; +/// Generic types replaced by a plural of their first argument. +/// +/// # Examples +/// `Vec<Name>` -> "names" +const SEQUENCE_TYPES: &[&str] = &["Vec", "VecDeque", "LinkedList"]; + /// Prefixes to strip from methods names /// /// # Examples @@ -378,6 +384,11 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<S return name_of_type(&inner_ty, db, edition); } + if SEQUENCE_TYPES.contains(&name.as_str()) { + let inner_ty = ty.type_arguments().next(); + return Some(sequence_name(inner_ty.as_ref(), db, edition)); + } + name } else if let Some(trait_) = ty.as_dyn_trait() { trait_name(&trait_, db, edition)? @@ -390,12 +401,32 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option<S name } else if let Some(inner_ty) = ty.remove_ref() { return name_of_type(&inner_ty, db, edition); + } else if let Some(inner_ty) = ty.as_slice() { + return Some(sequence_name(Some(&inner_ty), db, edition)); } else { return None; }; normalize(&name) } +fn sequence_name(inner_ty: Option<&hir::Type>, db: &RootDatabase, edition: Edition) -> SmolStr { + let items_str = SmolStr::new_static("items"); + let Some(inner_ty) = inner_ty else { + return items_str; + }; + let Some(name) = name_of_type(inner_ty, db, edition) else { + return items_str; + }; + + if name.ends_with(['s', 'x', 'y']) { + // Given a type called e.g. "Boss", "Fox" or "Story", don't try to + // create a plural. + items_str + } else { + SmolStr::new(format!("{name}s")) + } +} + fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Option<String> { let name = trait_.name(db).display(db, edition).to_string(); if USELESS_TRAITS.contains(&name.as_str()) { @@ -898,6 +929,58 @@ fn foo() { $0(bar())$0; } } #[test] + fn vec_value() { + check( + r#" +struct Vec<T> {}; +struct Seed; +fn bar() -> Vec<Seed> {} +fn foo() { $0(bar())$0; } +"#, + "seeds", + ); + } + + #[test] + fn vec_value_ends_with_s() { + check( + r#" +struct Vec<T> {}; +struct Boss; +fn bar() -> Vec<Boss> {} +fn foo() { $0(bar())$0; } +"#, + "items", + ); + } + + #[test] + fn vecdeque_value() { + check( + r#" +struct VecDeque<T> {}; +struct Seed; +fn bar() -> VecDeque<Seed> {} +fn foo() { $0(bar())$0; } +"#, + "seeds", + ); + } + + #[test] + fn slice_value() { + check( + r#" +struct Vec<T> {}; +struct Seed; +fn bar() -> &[Seed] {} +fn foo() { $0(bar())$0; } +"#, + "seeds", + ); + } + + #[test] fn ref_call() { check( r#" |