From 705712993ffe24898e3c1fe006e1108b7d02d6bc Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Mon, 1 Mar 2021 10:51:47 +0000 Subject: feat: add type ascription assist --- .../src/handlers/add_type_ascription.rs | 198 +++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 crates/ide_assists/src/handlers/add_type_ascription.rs (limited to 'crates/ide_assists/src/handlers') diff --git a/crates/ide_assists/src/handlers/add_type_ascription.rs b/crates/ide_assists/src/handlers/add_type_ascription.rs new file mode 100644 index 000000000..e9dc37150 --- /dev/null +++ b/crates/ide_assists/src/handlers/add_type_ascription.rs @@ -0,0 +1,198 @@ +use ide_db::defs::{Definition, NameRefClass}; +use syntax::{ast, AstNode, SyntaxKind, T}; +use test_utils::mark; + +use crate::{ + assist_context::{AssistContext, Assists}, + AssistId, AssistKind, +}; + +// Assist: add_type_ascription +// +// Adds `: _` before the assignment operator to prompt the user for a type +// +// ``` +// fn make() -> T { todo!() } +// fn main() { +// let x = make$0(); +// } +// ``` +// -> +// ``` +// fn make() -> T { todo!() } +// fn main() { +// let x: ${0:_} = make(); +// } +// ``` +pub(crate) fn add_type_ascription(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + let let_stmt = ctx.find_node_at_offset::()?; + if let_stmt.colon_token().is_some() { + mark::hit!(add_type_ascription_already_typed); + return None + } + + let ident = ctx.find_token_syntax_at_offset(SyntaxKind::IDENT).or_else(|| { + let arg_list = ctx.find_node_at_offset::()?; + if arg_list.args().count() > 0 { + return None; + } + mark::hit!(add_type_ascription_after_call); + arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT) + })?; + let next_token = ident.next_token()?; + if next_token.kind() == T![::] { + mark::hit!(add_type_ascription_turbofished); + return None; + } + let name_ref = ast::NameRef::cast(ident.parent())?; + let def = match NameRefClass::classify(&ctx.sema, &name_ref)? { + NameRefClass::Definition(def) => def, + NameRefClass::ExternCrate(_) | 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() { + mark::hit!(add_type_ascription_non_generic); + return None; + } + let pat = let_stmt.pat()?.syntax().last_token()?.text_range().end(); + acc.add( + AssistId("add_type_ascription", AssistKind::RefactorRewrite), + "Add `: _` before assignment operator", + ident.text_range(), + |builder| match ctx.config.snippet_cap { + Some(cap) => builder.insert_snippet(cap, pat, ": ${0:_}"), + None => builder.insert(pat, ": _"), + }, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + use test_utils::mark; + + #[test] + fn add_type_ascription_function() { + check_assist( + add_type_ascription, + r#" +fn make() -> T {} +fn main() { + let x = make$0(); +} +"#, + r#" +fn make() -> T {} +fn main() { + let x: ${0:_} = make(); +} +"#, + ); + } + + #[test] + fn add_type_ascription_after_call() { + mark::check!(add_type_ascription_after_call); + check_assist( + add_type_ascription, + r#" +fn make() -> T {} +fn main() { + let x = make()$0; +} +"#, + r#" +fn make() -> T {} +fn main() { + let x: ${0:_} = make(); +} +"#, + ); + } + + #[test] + fn add_type_ascription_method() { + check_assist( + add_type_ascription, + r#" +struct S; +impl S { + fn make(&self) -> T {} +} +fn main() { + let x = S.make$0(); +} +"#, + r#" +struct S; +impl S { + fn make(&self) -> T {} +} +fn main() { + let x: ${0:_} = S.make(); +} +"#, + ); + } + + #[test] + fn add_type_ascription_turbofished() { + mark::check!(add_type_ascription_turbofished); + check_assist_not_applicable( + add_type_ascription, + r#" +fn make() -> T {} +fn main() { + let x = make$0::<()>(); +} +"#, + ); + } + + #[test] + fn add_type_ascription_already_typed() { + mark::check!(add_type_ascription_already_typed); + check_assist_not_applicable( + add_type_ascription, + r#" +fn make() -> T {} +fn main() { + let x: () = make$0(); +} +"#, + ); + } + + #[test] + fn add_type_ascription_non_generic() { + mark::check!(add_type_ascription_non_generic); + check_assist_not_applicable( + add_type_ascription, + r#" +fn make() -> () {} +fn main() { + let x = make$0(); +} +"#, + ); + } + + #[test] + fn add_type_ascription_no_let() { + check_assist_not_applicable( + add_type_ascription, + r#" +fn make() -> T {} +fn main() { + make$0(); +} +"#, + ); + } +} -- cgit v1.2.3 From 218390b9fb0cfa713d4b6b38425f3a07fee2b564 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Mon, 1 Mar 2021 11:21:02 +0000 Subject: chore: rename var --- crates/ide_assists/src/handlers/add_type_ascription.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'crates/ide_assists/src/handlers') diff --git a/crates/ide_assists/src/handlers/add_type_ascription.rs b/crates/ide_assists/src/handlers/add_type_ascription.rs index e9dc37150..ea9f249d3 100644 --- a/crates/ide_assists/src/handlers/add_type_ascription.rs +++ b/crates/ide_assists/src/handlers/add_type_ascription.rs @@ -30,6 +30,7 @@ pub(crate) fn add_type_ascription(acc: &mut Assists, ctx: &AssistContext) -> Opt mark::hit!(add_type_ascription_already_typed); return None } + let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end(); let ident = ctx.find_token_syntax_at_offset(SyntaxKind::IDENT).or_else(|| { let arg_list = ctx.find_node_at_offset::()?; @@ -58,14 +59,13 @@ pub(crate) fn add_type_ascription(acc: &mut Assists, ctx: &AssistContext) -> Opt mark::hit!(add_type_ascription_non_generic); return None; } - let pat = let_stmt.pat()?.syntax().last_token()?.text_range().end(); acc.add( AssistId("add_type_ascription", AssistKind::RefactorRewrite), "Add `: _` before assignment operator", ident.text_range(), |builder| match ctx.config.snippet_cap { - Some(cap) => builder.insert_snippet(cap, pat, ": ${0:_}"), - None => builder.insert(pat, ": _"), + Some(cap) => builder.insert_snippet(cap, type_pos, ": ${0:_}"), + None => builder.insert(type_pos, ": _"), }, ) } -- cgit v1.2.3 From 4a36129c7a018828548e2eddebdd49cdb7d6879d Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Mon, 1 Mar 2021 11:51:52 +0000 Subject: chore: fmt + docs --- crates/ide_assists/src/handlers/add_type_ascription.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'crates/ide_assists/src/handlers') diff --git a/crates/ide_assists/src/handlers/add_type_ascription.rs b/crates/ide_assists/src/handlers/add_type_ascription.rs index ea9f249d3..060c326bf 100644 --- a/crates/ide_assists/src/handlers/add_type_ascription.rs +++ b/crates/ide_assists/src/handlers/add_type_ascription.rs @@ -9,7 +9,7 @@ use crate::{ // Assist: add_type_ascription // -// Adds `: _` before the assignment operator to prompt the user for a type +// Adds `: _` before the assignment operator to prompt the user for a type. // // ``` // fn make() -> T { todo!() } @@ -28,7 +28,7 @@ pub(crate) fn add_type_ascription(acc: &mut Assists, ctx: &AssistContext) -> Opt let let_stmt = ctx.find_node_at_offset::()?; if let_stmt.colon_token().is_some() { mark::hit!(add_type_ascription_already_typed); - return None + return None; } let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end(); -- cgit v1.2.3 From d4fad2be8de4906ac00c373e53bb9ac593d61429 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Tue, 2 Mar 2021 14:28:53 +0000 Subject: refactor: re-use add_turbo_fish function --- crates/ide_assists/src/handlers/add_turbo_fish.rs | 138 +++++++++++++- .../src/handlers/add_type_ascription.rs | 198 --------------------- 2 files changed, 137 insertions(+), 199 deletions(-) delete mode 100644 crates/ide_assists/src/handlers/add_type_ascription.rs (limited to 'crates/ide_assists/src/handlers') diff --git a/crates/ide_assists/src/handlers/add_turbo_fish.rs b/crates/ide_assists/src/handlers/add_turbo_fish.rs index 8e9ea4fad..f18e3edf9 100644 --- a/crates/ide_assists/src/handlers/add_turbo_fish.rs +++ b/crates/ide_assists/src/handlers/add_turbo_fish.rs @@ -31,11 +31,13 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<( return None; } mark::hit!(add_turbo_fish_after_call); + mark::hit!(add_type_ascription_after_call); arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT) })?; let next_token = ident.next_token()?; if next_token.kind() == T![::] { mark::hit!(add_turbo_fish_one_fish_is_enough); + mark::hit!(add_type_ascription_turbofished); return None; } let name_ref = ast::NameRef::cast(ident.parent())?; @@ -50,8 +52,27 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<( let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); if generics.is_empty() { mark::hit!(add_turbo_fish_non_generic); + mark::hit!(add_type_ascription_non_generic); return None; } + + if let Some(let_stmt) = ctx.find_node_at_offset::() { + if let_stmt.colon_token().is_none() { + let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end(); + acc.add( + AssistId("add_type_ascription", AssistKind::RefactorRewrite), + "Add `: _` before assignment operator", + ident.text_range(), + |builder| match ctx.config.snippet_cap { + Some(cap) => builder.insert_snippet(cap, type_pos, ": ${0:_}"), + None => builder.insert(type_pos, ": _"), + }, + )? + } else { + mark::hit!(add_type_ascription_already_typed); + } + } + acc.add( AssistId("add_turbo_fish", AssistKind::RefactorRewrite), "Add `::<>`", @@ -65,7 +86,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<( #[cfg(test)] mod tests { - use crate::tests::{check_assist, check_assist_not_applicable}; + use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable}; use super::*; use test_utils::mark; @@ -158,6 +179,121 @@ fn make() -> () {} fn main() { make$0(); } +"#, + ); + } + + #[test] + fn add_type_ascription_function() { + check_assist_by_label( + add_turbo_fish, + r#" +fn make() -> T {} +fn main() { + let x = make$0(); +} +"#, + r#" +fn make() -> T {} +fn main() { + let x: ${0:_} = make(); +} +"#, + "Add `: _` before assignment operator", + ); + } + + #[test] + fn add_type_ascription_after_call() { + mark::check!(add_type_ascription_after_call); + check_assist_by_label( + add_turbo_fish, + r#" +fn make() -> T {} +fn main() { + let x = make()$0; +} +"#, + r#" +fn make() -> T {} +fn main() { + let x: ${0:_} = make(); +} +"#, + "Add `: _` before assignment operator", + ); + } + + #[test] + fn add_type_ascription_method() { + check_assist_by_label( + add_turbo_fish, + r#" +struct S; +impl S { + fn make(&self) -> T {} +} +fn main() { + let x = S.make$0(); +} +"#, + r#" +struct S; +impl S { + fn make(&self) -> T {} +} +fn main() { + let x: ${0:_} = S.make(); +} +"#, + "Add `: _` before assignment operator", + ); + } + + #[test] + fn add_type_ascription_turbofished() { + mark::check!(add_type_ascription_turbofished); + check_assist_not_applicable( + add_turbo_fish, + r#" +fn make() -> T {} +fn main() { + let x = make$0::<()>(); +} +"#, + ); + } + + #[test] + fn add_type_ascription_already_typed() { + mark::check!(add_type_ascription_already_typed); + check_assist( + add_turbo_fish, + r#" +fn make() -> T {} +fn main() { + let x: () = make$0(); +} +"#, + r#" +fn make() -> T {} +fn main() { + let x: () = make::<${0:_}>(); +} +"#, + ); + } + + #[test] + fn add_type_ascription_non_generic() { + mark::check!(add_type_ascription_non_generic); + check_assist_not_applicable( + add_turbo_fish, + r#" +fn make() -> () {} +fn main() { + let x = make$0(); +} "#, ); } diff --git a/crates/ide_assists/src/handlers/add_type_ascription.rs b/crates/ide_assists/src/handlers/add_type_ascription.rs deleted file mode 100644 index 060c326bf..000000000 --- a/crates/ide_assists/src/handlers/add_type_ascription.rs +++ /dev/null @@ -1,198 +0,0 @@ -use ide_db::defs::{Definition, NameRefClass}; -use syntax::{ast, AstNode, SyntaxKind, T}; -use test_utils::mark; - -use crate::{ - assist_context::{AssistContext, Assists}, - AssistId, AssistKind, -}; - -// Assist: add_type_ascription -// -// Adds `: _` before the assignment operator to prompt the user for a type. -// -// ``` -// fn make() -> T { todo!() } -// fn main() { -// let x = make$0(); -// } -// ``` -// -> -// ``` -// fn make() -> T { todo!() } -// fn main() { -// let x: ${0:_} = make(); -// } -// ``` -pub(crate) fn add_type_ascription(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { - let let_stmt = ctx.find_node_at_offset::()?; - if let_stmt.colon_token().is_some() { - mark::hit!(add_type_ascription_already_typed); - return None; - } - let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end(); - - let ident = ctx.find_token_syntax_at_offset(SyntaxKind::IDENT).or_else(|| { - let arg_list = ctx.find_node_at_offset::()?; - if arg_list.args().count() > 0 { - return None; - } - mark::hit!(add_type_ascription_after_call); - arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT) - })?; - let next_token = ident.next_token()?; - if next_token.kind() == T![::] { - mark::hit!(add_type_ascription_turbofished); - return None; - } - let name_ref = ast::NameRef::cast(ident.parent())?; - let def = match NameRefClass::classify(&ctx.sema, &name_ref)? { - NameRefClass::Definition(def) => def, - NameRefClass::ExternCrate(_) | 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() { - mark::hit!(add_type_ascription_non_generic); - return None; - } - acc.add( - AssistId("add_type_ascription", AssistKind::RefactorRewrite), - "Add `: _` before assignment operator", - ident.text_range(), - |builder| match ctx.config.snippet_cap { - Some(cap) => builder.insert_snippet(cap, type_pos, ": ${0:_}"), - None => builder.insert(type_pos, ": _"), - }, - ) -} - -#[cfg(test)] -mod tests { - use crate::tests::{check_assist, check_assist_not_applicable}; - - use super::*; - use test_utils::mark; - - #[test] - fn add_type_ascription_function() { - check_assist( - add_type_ascription, - r#" -fn make() -> T {} -fn main() { - let x = make$0(); -} -"#, - r#" -fn make() -> T {} -fn main() { - let x: ${0:_} = make(); -} -"#, - ); - } - - #[test] - fn add_type_ascription_after_call() { - mark::check!(add_type_ascription_after_call); - check_assist( - add_type_ascription, - r#" -fn make() -> T {} -fn main() { - let x = make()$0; -} -"#, - r#" -fn make() -> T {} -fn main() { - let x: ${0:_} = make(); -} -"#, - ); - } - - #[test] - fn add_type_ascription_method() { - check_assist( - add_type_ascription, - r#" -struct S; -impl S { - fn make(&self) -> T {} -} -fn main() { - let x = S.make$0(); -} -"#, - r#" -struct S; -impl S { - fn make(&self) -> T {} -} -fn main() { - let x: ${0:_} = S.make(); -} -"#, - ); - } - - #[test] - fn add_type_ascription_turbofished() { - mark::check!(add_type_ascription_turbofished); - check_assist_not_applicable( - add_type_ascription, - r#" -fn make() -> T {} -fn main() { - let x = make$0::<()>(); -} -"#, - ); - } - - #[test] - fn add_type_ascription_already_typed() { - mark::check!(add_type_ascription_already_typed); - check_assist_not_applicable( - add_type_ascription, - r#" -fn make() -> T {} -fn main() { - let x: () = make$0(); -} -"#, - ); - } - - #[test] - fn add_type_ascription_non_generic() { - mark::check!(add_type_ascription_non_generic); - check_assist_not_applicable( - add_type_ascription, - r#" -fn make() -> () {} -fn main() { - let x = make$0(); -} -"#, - ); - } - - #[test] - fn add_type_ascription_no_let() { - check_assist_not_applicable( - add_type_ascription, - r#" -fn make() -> T {} -fn main() { - make$0(); -} -"#, - ); - } -} -- cgit v1.2.3 From 2c3c728e0aea90f80b6d066f97e59663371e91c5 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Tue, 2 Mar 2021 15:26:36 +0000 Subject: chore: remove redundant tests --- crates/ide_assists/src/handlers/add_turbo_fish.rs | 30 ----------------------- 1 file changed, 30 deletions(-) (limited to 'crates/ide_assists/src/handlers') diff --git a/crates/ide_assists/src/handlers/add_turbo_fish.rs b/crates/ide_assists/src/handlers/add_turbo_fish.rs index f18e3edf9..a08b55ebb 100644 --- a/crates/ide_assists/src/handlers/add_turbo_fish.rs +++ b/crates/ide_assists/src/handlers/add_turbo_fish.rs @@ -37,7 +37,6 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<( let next_token = ident.next_token()?; if next_token.kind() == T![::] { mark::hit!(add_turbo_fish_one_fish_is_enough); - mark::hit!(add_type_ascription_turbofished); return None; } let name_ref = ast::NameRef::cast(ident.parent())?; @@ -52,7 +51,6 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<( let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); if generics.is_empty() { mark::hit!(add_turbo_fish_non_generic); - mark::hit!(add_type_ascription_non_generic); return None; } @@ -250,20 +248,6 @@ fn main() { ); } - #[test] - fn add_type_ascription_turbofished() { - mark::check!(add_type_ascription_turbofished); - check_assist_not_applicable( - add_turbo_fish, - r#" -fn make() -> T {} -fn main() { - let x = make$0::<()>(); -} -"#, - ); - } - #[test] fn add_type_ascription_already_typed() { mark::check!(add_type_ascription_already_typed); @@ -280,20 +264,6 @@ fn make() -> T {} fn main() { let x: () = make::<${0:_}>(); } -"#, - ); - } - - #[test] - fn add_type_ascription_non_generic() { - mark::check!(add_type_ascription_non_generic); - check_assist_not_applicable( - add_turbo_fish, - r#" -fn make() -> () {} -fn main() { - let x = make$0(); -} "#, ); } -- cgit v1.2.3