New assist: add turbo fish
This commit is contained in:
parent
1bc1f28bc5
commit
80545e5d3a
134
crates/ra_assists/src/handlers/add_turbo_fish.rs
Normal file
134
crates/ra_assists/src/handlers/add_turbo_fish.rs
Normal file
@ -0,0 +1,134 @@
|
||||
use ra_ide_db::defs::{classify_name_ref, Definition, NameRefClass};
|
||||
use ra_syntax::{ast, AstNode, SyntaxKind, T};
|
||||
|
||||
use crate::{
|
||||
assist_context::{AssistContext, Assists},
|
||||
AssistId,
|
||||
};
|
||||
use test_utils::tested_by;
|
||||
|
||||
// Assist: add_turbo_fish
|
||||
//
|
||||
// Adds `::<_>` to a call of a generic method or function.
|
||||
//
|
||||
// ```
|
||||
// fn make<T>() -> T { todo!() }
|
||||
// fn main() {
|
||||
// let x = make<|>();
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn make<T>() -> T { todo!() }
|
||||
// fn main() {
|
||||
// let x = make::<${0:_}>();
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||
let ident = ctx.find_token_at_offset(SyntaxKind::IDENT)?;
|
||||
let next_token = ident.next_token()?;
|
||||
if next_token.kind() == T![::] {
|
||||
tested_by!(add_turbo_fish_one_fish_is_enough);
|
||||
return None;
|
||||
}
|
||||
let name_ref = ast::NameRef::cast(ident.parent())?;
|
||||
let def = match classify_name_ref(&ctx.sema, &name_ref)? {
|
||||
NameRefClass::Definition(def) => def,
|
||||
NameRefClass::FieldShorthand { .. } => return None,
|
||||
};
|
||||
let fun = match def {
|
||||
Definition::ModuleDef(hir::ModuleDef::Function(it)) => it,
|
||||
_ => return None,
|
||||
};
|
||||
let generics = hir::GenericDef::Function(fun).params(ctx.sema.db);
|
||||
if generics.is_empty() {
|
||||
tested_by!(add_turbo_fish_non_generic);
|
||||
return None;
|
||||
}
|
||||
acc.add(AssistId("add_turbo_fish"), "Add `::<>`", ident.text_range(), |builder| {
|
||||
match ctx.config.snippet_cap {
|
||||
Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"),
|
||||
None => builder.insert(ident.text_range().end(), "::<_>"),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||
|
||||
use super::*;
|
||||
use test_utils::covers;
|
||||
|
||||
#[test]
|
||||
fn add_turbo_fish_function() {
|
||||
check_assist(
|
||||
add_turbo_fish,
|
||||
r#"
|
||||
fn make<T>() -> T {}
|
||||
fn main() {
|
||||
make<|>();
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn make<T>() -> T {}
|
||||
fn main() {
|
||||
make::<${0:_}>();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_turbo_fish_method() {
|
||||
check_assist(
|
||||
add_turbo_fish,
|
||||
r#"
|
||||
struct S;
|
||||
impl S {
|
||||
fn make<T>(&self) -> T {}
|
||||
}
|
||||
fn main() {
|
||||
S.make<|>();
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct S;
|
||||
impl S {
|
||||
fn make<T>(&self) -> T {}
|
||||
}
|
||||
fn main() {
|
||||
S.make::<${0:_}>();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_turbo_fish_one_fish_is_enough() {
|
||||
covers!(add_turbo_fish_one_fish_is_enough);
|
||||
check_assist_not_applicable(
|
||||
add_turbo_fish,
|
||||
r#"
|
||||
fn make<T>() -> T {}
|
||||
fn main() {
|
||||
make<|>::<()>();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_turbo_fish_non_generic() {
|
||||
covers!(add_turbo_fish_non_generic);
|
||||
check_assist_not_applicable(
|
||||
add_turbo_fish,
|
||||
r#"
|
||||
fn make() -> () {}
|
||||
fn main() {
|
||||
make<|>();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -110,6 +110,7 @@ mod handlers {
|
||||
mod add_impl;
|
||||
mod add_missing_impl_members;
|
||||
mod add_new;
|
||||
mod add_turbo_fish;
|
||||
mod apply_demorgan;
|
||||
mod auto_import;
|
||||
mod change_return_type_to_result;
|
||||
@ -147,6 +148,7 @@ pub(crate) fn all() -> &'static [Handler] {
|
||||
add_function::add_function,
|
||||
add_impl::add_impl,
|
||||
add_new::add_new,
|
||||
add_turbo_fish::add_turbo_fish,
|
||||
apply_demorgan::apply_demorgan,
|
||||
auto_import::auto_import,
|
||||
change_return_type_to_result::change_return_type_to_result,
|
||||
|
@ -9,4 +9,6 @@
|
||||
test_not_applicable_if_variable_unused
|
||||
change_visibility_field_false_positive
|
||||
test_add_from_impl_already_exists
|
||||
add_turbo_fish_one_fish_is_enough
|
||||
add_turbo_fish_non_generic
|
||||
];
|
||||
|
@ -211,6 +211,25 @@ fn new(data: T) -> Self { Self { data } }
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_add_turbo_fish() {
|
||||
check_doc_test(
|
||||
"add_turbo_fish",
|
||||
r#####"
|
||||
fn make<T>() -> T { todo!() }
|
||||
fn main() {
|
||||
let x = make<|>();
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn make<T>() -> T { todo!() }
|
||||
fn main() {
|
||||
let x = make::<${0:_}>();
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_apply_demorgan() {
|
||||
check_doc_test(
|
||||
|
@ -203,6 +203,24 @@ impl<T: Clone> Ctx<T> {
|
||||
|
||||
```
|
||||
|
||||
## `add_turbo_fish`
|
||||
|
||||
Adds `::<_>` to a call of a generic method or function.
|
||||
|
||||
```rust
|
||||
// BEFORE
|
||||
fn make<T>() -> T { todo!() }
|
||||
fn main() {
|
||||
let x = make┃();
|
||||
}
|
||||
|
||||
// AFTER
|
||||
fn make<T>() -> T { todo!() }
|
||||
fn main() {
|
||||
let x = make::<${0:_}>();
|
||||
}
|
||||
```
|
||||
|
||||
## `apply_demorgan`
|
||||
|
||||
Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws).
|
||||
|
@ -57,6 +57,7 @@ fn check_todo(path: &Path, text: &str) {
|
||||
"tests/generated.rs",
|
||||
"handlers/add_missing_impl_members.rs",
|
||||
"handlers/add_function.rs",
|
||||
"handlers/add_turbo_fish.rs",
|
||||
// To support generating `todo!()` in assists, we have `expr_todo()` in ast::make.
|
||||
"ast/make.rs",
|
||||
];
|
||||
|
Loading…
Reference in New Issue
Block a user