From 80545e5d3a72ef05a77ff9584234f030c69bfe9f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 20 May 2020 00:07:00 +0200 Subject: New assist: add turbo fish --- crates/ra_assists/src/handlers/add_turbo_fish.rs | 134 +++++++++++++++++++++++ crates/ra_assists/src/lib.rs | 2 + crates/ra_assists/src/marks.rs | 2 + crates/ra_assists/src/tests/generated.rs | 19 ++++ docs/user/assists.md | 18 +++ xtask/tests/tidy.rs | 1 + 6 files changed, 176 insertions(+) create mode 100644 crates/ra_assists/src/handlers/add_turbo_fish.rs diff --git a/crates/ra_assists/src/handlers/add_turbo_fish.rs b/crates/ra_assists/src/handlers/add_turbo_fish.rs new file mode 100644 index 000000000..a0363bc78 --- /dev/null +++ b/crates/ra_assists/src/handlers/add_turbo_fish.rs @@ -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 { todo!() } +// fn main() { +// let x = make<|>(); +// } +// ``` +// -> +// ``` +// fn make() -> 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 {} +fn main() { + make<|>(); +} +"#, + r#" +fn make() -> T {} +fn main() { + make::<${0:_}>(); +} +"#, + ); + } + + #[test] + fn add_turbo_fish_method() { + check_assist( + add_turbo_fish, + r#" +struct S; +impl S { + fn make(&self) -> T {} +} +fn main() { + S.make<|>(); +} +"#, + r#" +struct S; +impl S { + fn make(&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 {} +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<|>(); +} +"#, + ); + } +} diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 7f0a723c9..339f24100 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -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 @@ mod handlers { 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, diff --git a/crates/ra_assists/src/marks.rs b/crates/ra_assists/src/marks.rs index 8d910205f..d579e627f 100644 --- a/crates/ra_assists/src/marks.rs +++ b/crates/ra_assists/src/marks.rs @@ -9,4 +9,6 @@ test_utils::marks![ 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 ]; diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 9487c9239..32fbcdef4 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs @@ -211,6 +211,25 @@ impl Ctx { ) } +#[test] +fn doctest_add_turbo_fish() { + check_doc_test( + "add_turbo_fish", + r#####" +fn make() -> T { todo!() } +fn main() { + let x = make<|>(); +} +"#####, + r#####" +fn make() -> T { todo!() } +fn main() { + let x = make::<${0:_}>(); +} +"#####, + ) +} + #[test] fn doctest_apply_demorgan() { check_doc_test( diff --git a/docs/user/assists.md b/docs/user/assists.md index 41c5df528..c72b50a4d 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md @@ -203,6 +203,24 @@ impl Ctx { ``` +## `add_turbo_fish` + +Adds `::<_>` to a call of a generic method or function. + +```rust +// BEFORE +fn make() -> T { todo!() } +fn main() { + let x = make┃(); +} + +// AFTER +fn make() -> T { todo!() } +fn main() { + let x = make::<${0:_}>(); +} +``` + ## `apply_demorgan` Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws). diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index b8e8860ba..2e9fcf07c 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs @@ -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", ]; -- cgit v1.2.3