From e39979aa91c8c08219e35a74ae5aa7aa5d8bc4d6 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Fri, 26 Mar 2021 19:39:20 +0100 Subject: Implement "Extract type alias" assist --- .../ide_assists/src/handlers/extract_type_alias.rs | 165 +++++++++++++++++++++ crates/ide_assists/src/lib.rs | 2 + crates/ide_assists/src/tests/generated.rs | 19 +++ 3 files changed, 186 insertions(+) create mode 100644 crates/ide_assists/src/handlers/extract_type_alias.rs (limited to 'crates/ide_assists/src') diff --git a/crates/ide_assists/src/handlers/extract_type_alias.rs b/crates/ide_assists/src/handlers/extract_type_alias.rs new file mode 100644 index 000000000..771868234 --- /dev/null +++ b/crates/ide_assists/src/handlers/extract_type_alias.rs @@ -0,0 +1,165 @@ +use syntax::{ + ast::{self, AstNode}, + SyntaxKind, +}; + +use crate::{AssistContext, AssistId, AssistKind, Assists}; + +// Assist: extract_type_alias +// +// Extracts the selected type as a type alias. +// +// ``` +// struct S { +// field: $0(u8, u8, u8)$0, +// } +// ``` +// -> +// ``` +// type Type = (u8, u8, u8); +// +// struct S { +// field: Type, +// } +// ``` +pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + if ctx.frange.range.is_empty() { + return None; + } + + let node = match ctx.covering_element() { + syntax::NodeOrToken::Node(node) => node, + syntax::NodeOrToken::Token(tok) => tok.parent()?, + }; + let range = node.text_range(); + let mut type_like_node = None; + for node in node.ancestors() { + if node.text_range() != range { + break; + } + + let kind = node.kind(); + if ast::Type::can_cast(kind) || kind == SyntaxKind::TYPE_ARG { + type_like_node = Some(node); + break; + } + } + + let node = type_like_node?; + + let insert = ctx.find_node_at_offset::()?.syntax().text_range().start(); + let target = node.text_range(); + + acc.add( + AssistId("extract_type_alias", AssistKind::RefactorExtract), + "Extract type as type alias", + target, + |builder| { + builder.edit_file(ctx.frange.file_id); + // FIXME: add snippet support + builder.replace(target, "Type"); + builder.insert(insert, format!("type Type = {};\n\n", node)); + }, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn test_not_applicable_without_selection() { + check_assist_not_applicable( + extract_type_alias, + r" +struct S { + field: $0(u8, u8, u8), +} + ", + ); + } + + #[test] + fn test_simple_types() { + check_assist( + extract_type_alias, + r" +struct S { + field: $0u8$0, +} + ", + r#" +type Type = u8; + +struct S { + field: Type, +} + "#, + ); + } + + #[test] + fn test_generic_type_arg() { + check_assist( + extract_type_alias, + r" +fn generic() {} + +fn f() { + generic::<$0()$0>(); +} + ", + r#" +fn generic() {} + +type Type = (); + +fn f() { + generic::(); +} + "#, + ); + } + + #[test] + fn test_inner_type_arg() { + check_assist( + extract_type_alias, + r" +struct Vec {} +struct S { + v: Vec$0>>, +} + ", + r#" +struct Vec {} +type Type = Vec; + +struct S { + v: Vec>, +} + "#, + ); + } + + #[test] + fn test_extract_inner_type() { + check_assist( + extract_type_alias, + r" +struct S { + field: ($0u8$0,), +} + ", + r#" +type Type = u8; + +struct S { + field: (Type,), +} + "#, + ); + } +} diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index 8c068a6c0..3d1dcef4c 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs @@ -121,6 +121,7 @@ mod handlers { mod expand_glob_import; mod extract_function; mod extract_struct_from_enum_variant; + mod extract_type_alias; mod extract_variable; mod fill_match_arms; mod fix_visibility; @@ -187,6 +188,7 @@ mod handlers { early_return::convert_to_guarded_return, expand_glob_import::expand_glob_import, extract_struct_from_enum_variant::extract_struct_from_enum_variant, + extract_type_alias::extract_type_alias, fill_match_arms::fill_match_arms, fix_visibility::fix_visibility, flip_binexpr::flip_binexpr, diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 736027ff0..6bb65e6bc 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs @@ -328,6 +328,25 @@ enum A { One(One) } ) } +#[test] +fn doctest_extract_type_alias() { + check_doc_test( + "extract_type_alias", + r#####" +struct S { + field: $0(u8, u8, u8)$0, +} +"#####, + r#####" +type Type = (u8, u8, u8); + +struct S { + field: Type, +} +"#####, + ) +} + #[test] fn doctest_extract_variable() { check_doc_test( -- cgit v1.2.3 From b494e479202ae3da2745ef4ff05db9da0c8427a0 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sat, 27 Mar 2021 18:51:06 +0100 Subject: Snippet support in extract_type_alias --- .../ide_assists/src/handlers/extract_type_alias.rs | 37 ++++++++++++++-------- crates/ide_assists/src/tests/generated.rs | 4 +-- 2 files changed, 26 insertions(+), 15 deletions(-) (limited to 'crates/ide_assists/src') diff --git a/crates/ide_assists/src/handlers/extract_type_alias.rs b/crates/ide_assists/src/handlers/extract_type_alias.rs index 771868234..171b87918 100644 --- a/crates/ide_assists/src/handlers/extract_type_alias.rs +++ b/crates/ide_assists/src/handlers/extract_type_alias.rs @@ -16,10 +16,10 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // ``` // -> // ``` -// type Type = (u8, u8, u8); +// type ${0:Type} = (u8, u8, u8); // // struct S { -// field: Type, +// field: ${0:Type}, // } // ``` pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { @@ -56,9 +56,20 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Opti target, |builder| { builder.edit_file(ctx.frange.file_id); - // FIXME: add snippet support - builder.replace(target, "Type"); - builder.insert(insert, format!("type Type = {};\n\n", node)); + match ctx.config.snippet_cap { + Some(cap) => { + builder.replace_snippet(cap, target, "${0:Type}"); + builder.insert_snippet( + cap, + insert, + format!("type ${{0:Type}} = {};\n\n", node), + ); + } + None => { + builder.replace(target, "Type"); + builder.insert(insert, format!("type Type = {};\n\n", node)); + } + } }, ) } @@ -91,10 +102,10 @@ struct S { } ", r#" -type Type = u8; +type ${0:Type} = u8; struct S { - field: Type, + field: ${0:Type}, } "#, ); @@ -114,10 +125,10 @@ fn f() { r#" fn generic() {} -type Type = (); +type ${0:Type} = (); fn f() { - generic::(); + generic::<${0:Type}>(); } "#, ); @@ -135,10 +146,10 @@ struct S { ", r#" struct Vec {} -type Type = Vec; +type ${0:Type} = Vec; struct S { - v: Vec>, + v: Vec>, } "#, ); @@ -154,10 +165,10 @@ struct S { } ", r#" -type Type = u8; +type ${0:Type} = u8; struct S { - field: (Type,), + field: (${0:Type},), } "#, ); diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 6bb65e6bc..becd640b1 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs @@ -338,10 +338,10 @@ struct S { } "#####, r#####" -type Type = (u8, u8, u8); +type ${0:Type} = (u8, u8, u8); struct S { - field: Type, + field: ${0:Type}, } "#####, ) -- cgit v1.2.3 From 8c1092455e6b110a28ff27e76a7e21e48fd0a038 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 29 Mar 2021 13:17:49 +0200 Subject: Use `find_node_at_range` --- crates/ide_assists/src/assist_context.rs | 5 +++- .../ide_assists/src/handlers/extract_type_alias.rs | 28 +++------------------- 2 files changed, 7 insertions(+), 26 deletions(-) (limited to 'crates/ide_assists/src') diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs index 1482d37f8..8714e4978 100644 --- a/crates/ide_assists/src/assist_context.rs +++ b/crates/ide_assists/src/assist_context.rs @@ -13,7 +13,7 @@ use ide_db::{ RootDatabase, }; use syntax::{ - algo::{self, find_node_at_offset, SyntaxRewriter}, + algo::{self, find_node_at_offset, find_node_at_range, SyntaxRewriter}, AstNode, AstToken, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize, TokenAtOffset, }; @@ -89,6 +89,9 @@ impl<'a> AssistContext<'a> { pub(crate) fn find_node_at_offset(&self) -> Option { find_node_at_offset(self.source_file.syntax(), self.offset()) } + pub(crate) fn find_node_at_range(&self) -> Option { + find_node_at_range(self.source_file.syntax(), self.frange.range) + } pub(crate) fn find_node_at_offset_with_descend(&self) -> Option { self.sema.find_node_at_offset_with_descend(self.source_file.syntax(), self.offset()) } diff --git a/crates/ide_assists/src/handlers/extract_type_alias.rs b/crates/ide_assists/src/handlers/extract_type_alias.rs index 171b87918..f75d35462 100644 --- a/crates/ide_assists/src/handlers/extract_type_alias.rs +++ b/crates/ide_assists/src/handlers/extract_type_alias.rs @@ -1,7 +1,4 @@ -use syntax::{ - ast::{self, AstNode}, - SyntaxKind, -}; +use syntax::ast::{self, AstNode}; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -27,28 +24,9 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Opti return None; } - let node = match ctx.covering_element() { - syntax::NodeOrToken::Node(node) => node, - syntax::NodeOrToken::Token(tok) => tok.parent()?, - }; - let range = node.text_range(); - let mut type_like_node = None; - for node in node.ancestors() { - if node.text_range() != range { - break; - } - - let kind = node.kind(); - if ast::Type::can_cast(kind) || kind == SyntaxKind::TYPE_ARG { - type_like_node = Some(node); - break; - } - } - - let node = type_like_node?; - + let node = ctx.find_node_at_range::()?; let insert = ctx.find_node_at_offset::()?.syntax().text_range().start(); - let target = node.text_range(); + let target = node.syntax().text_range(); acc.add( AssistId("extract_type_alias", AssistKind::RefactorExtract), -- cgit v1.2.3 From 3c6c1c99b41e2a3c9a31b0d4c73d660399255cba Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 29 Mar 2021 13:23:07 +0200 Subject: Don't use snippets --- .../ide_assists/src/handlers/extract_type_alias.rs | 29 +++++++++------------- crates/ide_assists/src/tests/generated.rs | 4 +-- 2 files changed, 14 insertions(+), 19 deletions(-) (limited to 'crates/ide_assists/src') diff --git a/crates/ide_assists/src/handlers/extract_type_alias.rs b/crates/ide_assists/src/handlers/extract_type_alias.rs index f75d35462..442a209b9 100644 --- a/crates/ide_assists/src/handlers/extract_type_alias.rs +++ b/crates/ide_assists/src/handlers/extract_type_alias.rs @@ -13,10 +13,10 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // ``` // -> // ``` -// type ${0:Type} = (u8, u8, u8); +// type $0Type = (u8, u8, u8); // // struct S { -// field: ${0:Type}, +// field: Type, // } // ``` pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { @@ -34,17 +34,12 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Opti target, |builder| { builder.edit_file(ctx.frange.file_id); + builder.replace(target, "Type"); match ctx.config.snippet_cap { Some(cap) => { - builder.replace_snippet(cap, target, "${0:Type}"); - builder.insert_snippet( - cap, - insert, - format!("type ${{0:Type}} = {};\n\n", node), - ); + builder.insert_snippet(cap, insert, format!("type $0Type = {};\n\n", node)); } None => { - builder.replace(target, "Type"); builder.insert(insert, format!("type Type = {};\n\n", node)); } } @@ -80,10 +75,10 @@ struct S { } ", r#" -type ${0:Type} = u8; +type $0Type = u8; struct S { - field: ${0:Type}, + field: Type, } "#, ); @@ -103,10 +98,10 @@ fn f() { r#" fn generic() {} -type ${0:Type} = (); +type $0Type = (); fn f() { - generic::<${0:Type}>(); + generic::(); } "#, ); @@ -124,10 +119,10 @@ struct S { ", r#" struct Vec {} -type ${0:Type} = Vec; +type $0Type = Vec; struct S { - v: Vec>, + v: Vec>, } "#, ); @@ -143,10 +138,10 @@ struct S { } ", r#" -type ${0:Type} = u8; +type $0Type = u8; struct S { - field: (${0:Type},), + field: (Type,), } "#, ); diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index becd640b1..03b7fb366 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs @@ -338,10 +338,10 @@ struct S { } "#####, r#####" -type ${0:Type} = (u8, u8, u8); +type $0Type = (u8, u8, u8); struct S { - field: ${0:Type}, + field: Type, } "#####, ) -- cgit v1.2.3