Implement "Extract type alias" assist

This commit is contained in:
Jonas Schievink 2021-03-26 19:39:20 +01:00
parent 4ecaad98e0
commit e39979aa91
3 changed files with 186 additions and 0 deletions

View file

@ -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::<ast::Item>()?.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<T>() {}
fn f() {
generic::<$0()$0>();
}
",
r#"
fn generic<T>() {}
type Type = ();
fn f() {
generic::<Type>();
}
"#,
);
}
#[test]
fn test_inner_type_arg() {
check_assist(
extract_type_alias,
r"
struct Vec<T> {}
struct S {
v: Vec<Vec<$0Vec<u8>$0>>,
}
",
r#"
struct Vec<T> {}
type Type = Vec<u8>;
struct S {
v: Vec<Vec<Type>>,
}
"#,
);
}
#[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,),
}
"#,
);
}
}

View file

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

View file

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