use syntax::ast::{self, AstNode}; 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 $0Type = (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 = ctx.find_node_at_range::()?; let insert = ctx.find_node_at_offset::()?.syntax().text_range().start(); let target = node.syntax().text_range(); acc.add( AssistId("extract_type_alias", AssistKind::RefactorExtract), "Extract type as type alias", target, |builder| { builder.edit_file(ctx.frange.file_id); builder.replace(target, "Type"); match ctx.config.snippet_cap { Some(cap) => { builder.insert_snippet(cap, insert, format!("type $0Type = {};\n\n", node)); } None => { 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 $0Type = 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 $0Type = (); 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 $0Type = Vec; struct S { v: Vec>, } "#, ); } #[test] fn test_extract_inner_type() { check_assist( extract_type_alias, r" struct S { field: ($0u8$0,), } ", r#" type $0Type = u8; struct S { field: (Type,), } "#, ); } }