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 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