From 7259cc82f362bd1cd00f47ff3ffc71be769162b6 Mon Sep 17 00:00:00 2001 From: Aleksei Sidorov Date: Thu, 3 Sep 2020 14:46:28 +0300 Subject: Resolve most of corner cases --- .../handlers/replace_impl_trait_with_generic.rs | 110 +++++++++++++++++++-- crates/syntax/src/ast/edit.rs | 26 ++++- 2 files changed, 124 insertions(+), 12 deletions(-) (limited to 'crates') diff --git a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs index 8af2d16dd..5b0d5d971 100644 --- a/crates/assists/src/handlers/replace_impl_trait_with_generic.rs +++ b/crates/assists/src/handlers/replace_impl_trait_with_generic.rs @@ -13,9 +13,6 @@ pub(crate) fn replace_impl_trait_with_generic( let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?; let type_fn = type_param.syntax().ancestors().nth(2).and_then(ast::Fn::cast)?; - let generic_param_list = - type_fn.generic_param_list().unwrap_or_else(|| make::generic_param_list(None)); - let impl_trait_ty = type_impl_trait .syntax() .descendants() @@ -31,11 +28,16 @@ pub(crate) fn replace_impl_trait_with_generic( target, |edit| { let generic_letter = impl_trait_ty[..1].to_string(); - edit.replace_ast::(type_impl_trait.into(), make::ty(&generic_letter)); - let new_params = generic_param_list - .append_param(make::generic_param(generic_letter, Some(impl_trait_ty))); - let new_type_fn = type_fn.replace_descendant(generic_param_list, new_params); + let generic_param_list = type_fn + .generic_param_list() + .unwrap_or_else(|| make::generic_param_list(None)) + .append_param(make::generic_param(generic_letter.clone(), Some(impl_trait_ty))); + + let new_type_fn = type_fn + .replace_descendant::(type_impl_trait.into(), make::ty(&generic_letter)) + .with_generic_params(generic_param_list); + edit.replace_ast(type_fn.clone(), new_type_fn); }, ) @@ -48,7 +50,7 @@ mod tests { use crate::tests::check_assist; #[test] - fn replace_with_generic_params() { + fn replace_impl_trait_with_generic_params() { check_assist( replace_impl_trait_with_generic, r#" @@ -59,4 +61,96 @@ mod tests { "#, ); } + + #[test] + fn replace_impl_trait_without_generic_params() { + check_assist( + replace_impl_trait_with_generic, + r#" + fn foo(bar: <|>impl Bar) {} + "#, + r#" + fn foo(bar: B) {} + "#, + ); + } + + #[test] + fn replace_two_impl_trait_with_generic_params() { + check_assist( + replace_impl_trait_with_generic, + r#" + fn foo(foo: impl Foo, bar: <|>impl Bar) {} + "#, + r#" + fn foo(foo: impl Foo, bar: B) {} + "#, + ); + } + + #[test] + fn replace_impl_trait_with_empty_generic_params() { + check_assist( + replace_impl_trait_with_generic, + r#" + fn foo<>(bar: <|>impl Bar) {} + "#, + r#" + fn foo(bar: B) {} + "#, + ); + } + + #[test] + fn replace_impl_trait_with_empty_multiline_generic_params() { + // FIXME: It would be more correct to place the generic parameter + // on the next line after the left angle. + check_assist( + replace_impl_trait_with_generic, + r#" + fn foo< + >(bar: <|>impl Bar) {} + "#, + r#" + fn foo(bar: B) {} + "#, + ); + } + + #[test] + #[ignore = "This case is very rare but there is no simple solutions to fix it."] + fn replace_impl_trait_with_exist_generic_letter() { + check_assist( + replace_impl_trait_with_generic, + r#" + fn foo(bar: <|>impl Bar) {} + "#, + r#" + fn foo(bar: C) {} + "#, + ); + } + + #[test] + fn replace_impl_trait_with_multiline_generic_params() { + check_assist( + replace_impl_trait_with_generic, + r#" + fn foo< + G: Foo, + F, + H, + >(bar: <|>impl Bar) {} + "#, + r#" + fn foo< + G: Foo, + F, + H, + B: Bar, + >(bar: B) {} + "#, + ); + } } diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 1ccb4de6a..68987dbf6 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs @@ -13,7 +13,7 @@ use crate::{ ast::{ self, make::{self, tokens}, - AstNode, TypeBoundsOwner, + AstNode, GenericParamsOwner, NameOwner, TypeBoundsOwner, }, AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind, SyntaxKind::{ATTR, COMMENT, WHITESPACE}, @@ -46,6 +46,19 @@ impl ast::Fn { to_insert.push(body.syntax().clone().into()); self.replace_children(single_node(old_body_or_semi), to_insert) } + + #[must_use] + pub fn with_generic_params(&self, generic_args: ast::GenericParamList) -> ast::Fn { + if let Some(old) = self.generic_param_list() { + return self.replace_descendant(old, generic_args); + } + + let anchor = self.name().expect("The function must have a name").syntax().clone(); + + let mut to_insert: ArrayVec<[SyntaxElement; 1]> = ArrayVec::new(); + to_insert.push(generic_args.syntax().clone().into()); + self.insert_children(InsertPosition::After(anchor.into()), to_insert) + } } fn make_multiline(node: N) -> N @@ -461,14 +474,17 @@ impl ast::MatchArmList { impl ast::GenericParamList { #[must_use] - pub fn append_params(&self, params: impl IntoIterator) -> Self { + pub fn append_params( + &self, + params: impl IntoIterator, + ) -> ast::GenericParamList { let mut res = self.clone(); params.into_iter().for_each(|it| res = res.append_param(it)); res } #[must_use] - pub fn append_param(&self, item: ast::GenericParam) -> Self { + pub fn append_param(&self, item: ast::GenericParam) -> ast::GenericParamList { let is_multiline = self.syntax().text().contains_char('\n'); let ws; let space = if is_multiline { @@ -482,7 +498,9 @@ impl ast::GenericParamList { }; let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new(); - to_insert.push(space.into()); + if self.generic_params().next().is_some() { + to_insert.push(space.into()); + } to_insert.push(item.syntax().clone().into()); to_insert.push(make::token(T![,]).into()); -- cgit v1.2.3