Unnamed repository; edit this file 'description' to name the repository.
| -rw-r--r-- | crates/ide-assists/src/handlers/add_turbo_fish.rs | 13 | ||||
| -rw-r--r-- | crates/ide-assists/src/handlers/convert_bool_to_enum.rs | 89 | ||||
| -rw-r--r-- | crates/ide-assists/src/handlers/extract_type_alias.rs | 27 | ||||
| -rw-r--r-- | crates/ide-assists/src/handlers/generate_default_from_new.rs | 13 | ||||
| -rw-r--r-- | crates/ide-assists/src/handlers/generate_fn_type_alias.rs | 20 | ||||
| -rw-r--r-- | crates/ide-assists/src/handlers/move_bounds.rs | 53 | ||||
| -rw-r--r-- | crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs | 15 | ||||
| -rw-r--r-- | crates/syntax/src/ast/syntax_factory/constructors.rs | 70 | ||||
| -rw-r--r-- | crates/syntax/src/syntax_editor.rs | 2 | ||||
| -rw-r--r-- | crates/syntax/src/syntax_editor/edits.rs | 101 |
10 files changed, 296 insertions, 107 deletions
diff --git a/crates/ide-assists/src/handlers/add_turbo_fish.rs b/crates/ide-assists/src/handlers/add_turbo_fish.rs index be13b04873..c5e722d87e 100644 --- a/crates/ide-assists/src/handlers/add_turbo_fish.rs +++ b/crates/ide-assists/src/handlers/add_turbo_fish.rs @@ -2,7 +2,7 @@ use either::Either; use ide_db::defs::{Definition, NameRefClass}; use syntax::{ AstNode, - ast::{self, HasArgList, HasGenericArgs, make, syntax_factory::SyntaxFactory}, + ast::{self, HasArgList, HasGenericArgs, syntax_factory::SyntaxFactory}, syntax_editor::Position, }; @@ -94,20 +94,21 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti ident.text_range(), |builder| { let mut editor = builder.make_editor(let_stmt.syntax()); + let make = SyntaxFactory::without_mappings(); if let_stmt.semicolon_token().is_none() { editor.insert( Position::last_child_of(let_stmt.syntax()), - make::tokens::semicolon(), + make.token(syntax::SyntaxKind::SEMICOLON), ); } - let placeholder_ty = make::ty_placeholder().clone_for_update(); + let placeholder_ty = make.ty_placeholder(); if let Some(pat) = let_stmt.pat() { let elements = vec![ - make::token(syntax::SyntaxKind::COLON).into(), - make::token(syntax::SyntaxKind::WHITESPACE).into(), + make.token(syntax::SyntaxKind::COLON).into(), + make.whitespace(" ").into(), placeholder_ty.syntax().clone().into(), ]; editor.insert_all(Position::after(pat.syntax()), elements); @@ -188,7 +189,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti /// This will create a turbofish generic arg list corresponding to the number of arguments fn get_fish_head(make: &SyntaxFactory, number_of_arguments: usize) -> ast::GenericArgList { - let args = (0..number_of_arguments).map(|_| make::type_arg(make::ty_placeholder()).into()); + let args = (0..number_of_arguments).map(|_| make.type_arg(make.ty_placeholder()).into()); make.generic_arg_list(args, true) } diff --git a/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/crates/ide-assists/src/handlers/convert_bool_to_enum.rs index 434fbbae05..9347798100 100644 --- a/crates/ide-assists/src/handlers/convert_bool_to_enum.rs +++ b/crates/ide-assists/src/handlers/convert_bool_to_enum.rs @@ -12,9 +12,10 @@ use ide_db::{ }; use itertools::Itertools; use syntax::ast::edit::AstNodeEdit; +use syntax::ast::syntax_factory::SyntaxFactory; use syntax::{ AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, - ast::{self, HasName, edit::IndentLevel, make}, + ast::{self, HasName, edit::IndentLevel}, }; use crate::{ @@ -62,19 +63,28 @@ pub(crate) fn convert_bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_>) - "Convert boolean to enum", target, |edit| { + let make = SyntaxFactory::without_mappings(); if let Some(ty) = &ty_annotation { cov_mark::hit!(replaces_ty_annotation); edit.replace(ty.syntax().text_range(), "Bool"); } if let Some(initializer) = initializer { - replace_bool_expr(edit, initializer); + replace_bool_expr(edit, initializer, &make); } let usages = definition.usages(&ctx.sema).all(); - add_enum_def(edit, ctx, &usages, target_node, &target_module); + add_enum_def(edit, ctx, &usages, target_node, &target_module, &make); let mut delayed_mutations = Vec::new(); - replace_usages(edit, ctx, usages, definition, &target_module, &mut delayed_mutations); + replace_usages( + edit, + ctx, + usages, + definition, + &target_module, + &mut delayed_mutations, + &make, + ); for (scope, path) in delayed_mutations { insert_use(&scope, path, &ctx.config.insert_use); } @@ -168,16 +178,16 @@ fn find_bool_node(ctx: &AssistContext<'_>) -> Option<BoolNodeData> { } } -fn replace_bool_expr(edit: &mut SourceChangeBuilder, expr: ast::Expr) { +fn replace_bool_expr(edit: &mut SourceChangeBuilder, expr: ast::Expr, make: &SyntaxFactory) { let expr_range = expr.syntax().text_range(); - let enum_expr = bool_expr_to_enum_expr(expr); + let enum_expr = bool_expr_to_enum_expr(expr, make); edit.replace(expr_range, enum_expr.syntax().text()) } /// Converts an expression of type `bool` to one of the new enum type. -fn bool_expr_to_enum_expr(expr: ast::Expr) -> ast::Expr { - let true_expr = make::expr_path(make::path_from_text("Bool::True")); - let false_expr = make::expr_path(make::path_from_text("Bool::False")); +fn bool_expr_to_enum_expr(expr: ast::Expr, make: &SyntaxFactory) -> ast::Expr { + let true_expr = make.expr_path(make.path_from_text("Bool::True")); + let false_expr = make.expr_path(make.path_from_text("Bool::False")); if let ast::Expr::Literal(literal) = &expr { match literal.kind() { @@ -186,10 +196,10 @@ fn bool_expr_to_enum_expr(expr: ast::Expr) -> ast::Expr { _ => expr, } } else { - make::expr_if( + make.expr_if( expr, - make::tail_only_block_expr(true_expr), - Some(ast::ElseBranch::Block(make::tail_only_block_expr(false_expr))), + make.tail_only_block_expr(true_expr), + Some(ast::ElseBranch::Block(make.tail_only_block_expr(false_expr))), ) .into() } @@ -203,11 +213,13 @@ fn replace_usages( target_definition: Definition, target_module: &hir::Module, delayed_mutations: &mut Vec<(ImportScope, ast::Path)>, + make: &SyntaxFactory, ) { for (file_id, references) in usages { edit.edit_file(file_id.file_id(ctx.db())); - let refs_with_imports = augment_references_with_imports(ctx, references, target_module); + let refs_with_imports = + augment_references_with_imports(ctx, references, target_module, make); refs_with_imports.into_iter().rev().for_each( |FileReferenceWithImport { range, name, import_data }| { @@ -224,12 +236,13 @@ fn replace_usages( target_definition, target_module, delayed_mutations, + make, ) } } else if let Some(initializer) = find_assignment_usage(&name) { cov_mark::hit!(replaces_assignment); - replace_bool_expr(edit, initializer); + replace_bool_expr(edit, initializer, make); } else if let Some((prefix_expr, inner_expr)) = find_negated_usage(&name) { cov_mark::hit!(replaces_negation); @@ -247,7 +260,7 @@ fn replace_usages( { cov_mark::hit!(replaces_record_expr); - let enum_expr = bool_expr_to_enum_expr(initializer); + let enum_expr = bool_expr_to_enum_expr(initializer, make); utils::replace_record_field_expr(ctx, edit, record_field, enum_expr); } else if let Some(pat) = find_record_pat_field_usage(&name) { match pat { @@ -263,6 +276,7 @@ fn replace_usages( target_definition, target_module, delayed_mutations, + make, ) } } @@ -272,14 +286,14 @@ fn replace_usages( if let Some(expr) = literal_pat.literal().and_then(|literal| { literal.syntax().ancestors().find_map(ast::Expr::cast) }) { - replace_bool_expr(edit, expr); + replace_bool_expr(edit, expr, make); } } _ => (), } } else if let Some((ty_annotation, initializer)) = find_assoc_const_usage(&name) { edit.replace(ty_annotation.syntax().text_range(), "Bool"); - replace_bool_expr(edit, initializer); + replace_bool_expr(edit, initializer, make); } else if let Some(receiver) = find_method_call_expr_usage(&name) { edit.replace( receiver.syntax().text_range(), @@ -296,10 +310,10 @@ fn replace_usages( ctx, edit, record_field, - make::expr_bin_op( + make.expr_bin_op( expr, ast::BinaryOp::CmpOp(ast::CmpOp::Eq { negated: false }), - make::expr_path(make::path_from_text("Bool::True")), + make.expr_path(make.path_from_text("Bool::True")), ), ); } else { @@ -327,6 +341,7 @@ fn augment_references_with_imports( ctx: &AssistContext<'_>, references: Vec<FileReference>, target_module: &hir::Module, + make: &SyntaxFactory, ) -> Vec<FileReferenceWithImport> { let mut visited_modules = FxHashSet::default(); @@ -357,9 +372,9 @@ fn augment_references_with_imports( cfg, ) .map(|mod_path| { - make::path_concat( + make.path_concat( mod_path_to_ast(&mod_path, edition), - make::path_from_text("Bool"), + make.path_from_text("Bool"), ) })?; @@ -458,6 +473,7 @@ fn add_enum_def( usages: &UsageSearchResult, target_node: SyntaxNode, target_module: &hir::Module, + make: &SyntaxFactory, ) -> Option<()> { let insert_before = node_to_insert_before(target_node); @@ -482,7 +498,7 @@ fn add_enum_def( .any(|module| module.nearest_non_block_module(ctx.db()) != *target_module); let indent = IndentLevel::from_node(&insert_before); - let enum_def = make_bool_enum(make_enum_pub).reset_indent().indent(indent); + let enum_def = make_bool_enum(make_enum_pub, make).reset_indent().indent(indent); edit.insert( insert_before.text_range().start(), @@ -504,31 +520,30 @@ fn node_to_insert_before(target_node: SyntaxNode) -> SyntaxNode { .unwrap_or(target_node) } -fn make_bool_enum(make_pub: bool) -> ast::Enum { - let derive_eq = make::attr_outer(make::meta_token_tree( - make::ext::ident_path("derive"), - make::token_tree( +fn make_bool_enum(make_pub: bool, make: &SyntaxFactory) -> ast::Enum { + let derive_eq = make.attr_outer(make.meta_token_tree( + make.ident_path("derive"), + make.token_tree( T!['('], vec![ - NodeOrToken::Token(make::tokens::ident("PartialEq")), - NodeOrToken::Token(make::token(T![,])), - NodeOrToken::Token(make::tokens::single_space()), - NodeOrToken::Token(make::tokens::ident("Eq")), + NodeOrToken::Token(make.ident("PartialEq")), + NodeOrToken::Token(make.token(T![,])), + NodeOrToken::Token(make.whitespace(" ")), + NodeOrToken::Token(make.ident("Eq")), ], ), )); - make::enum_( + make.enum_( [derive_eq], - if make_pub { Some(make::visibility_pub()) } else { None }, - make::name("Bool"), + if make_pub { Some(make.visibility_pub()) } else { None }, + make.name("Bool"), None, None, - make::variant_list(vec![ - make::variant(None, make::name("True"), None, None), - make::variant(None, make::name("False"), None, None), + make.variant_list(vec![ + make.variant(None, make.name("True"), None, None), + make.variant(None, make.name("False"), None, None), ]), ) - .clone_for_update() } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/extract_type_alias.rs b/crates/ide-assists/src/handlers/extract_type_alias.rs index 769bbd976a..e4fdac27f4 100644 --- a/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -2,7 +2,10 @@ use either::Either; use hir::HirDisplay; use ide_db::syntax_helpers::node_ext::walk_ty; use syntax::{ - ast::{self, AstNode, HasGenericArgs, HasGenericParams, HasName, edit::IndentLevel, make}, + ast::{ + self, AstNode, HasGenericArgs, HasGenericParams, HasName, edit::IndentLevel, + syntax_factory::SyntaxFactory, + }, syntax_editor, }; @@ -43,10 +46,9 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> let resolved_ty = ctx.sema.resolve_type(&ty)?; let resolved_ty = if !resolved_ty.contains_unknown() { let module = ctx.sema.scope(ty.syntax())?.module(); - let resolved_ty = resolved_ty.display_source_code(ctx.db(), module.into(), false).ok()?; - make::ty(&resolved_ty) + resolved_ty.display_source_code(ctx.db(), module.into(), false).ok()? } else { - ty.clone() + ty.to_string() }; acc.add( @@ -55,6 +57,9 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> target, |builder| { let mut edit = builder.make_editor(node); + let make = SyntaxFactory::without_mappings(); + + let resolved_ty = make.ty(&resolved_ty); let mut known_generics = match item.generic_param_list() { Some(it) => it.generic_params().collect(), @@ -68,22 +73,20 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> } let generics = collect_used_generics(&ty, &known_generics); let generic_params = - generics.map(|it| make::generic_param_list(it.into_iter().cloned())); + generics.map(|it| make.generic_param_list(it.into_iter().cloned())); // Replace original type with the alias let ty_args = generic_params.as_ref().map(|it| it.to_generic_args().generic_args()); let new_ty = if let Some(ty_args) = ty_args { - make::generic_ty_path_segment(make::name_ref("Type"), ty_args) + make.generic_ty_path_segment(make.name_ref("Type"), ty_args) } else { - make::path_segment(make::name_ref("Type")) - } - .clone_for_update(); + make.path_segment(make.name_ref("Type")) + }; edit.replace(ty.syntax(), new_ty.syntax()); // Insert new alias let ty_alias = - make::ty_alias(None, "Type", generic_params, None, None, Some((resolved_ty, None))) - .clone_for_update(); + make.ty_alias(None, "Type", generic_params, None, None, Some((resolved_ty, None))); if let Some(cap) = ctx.config.snippet_cap && let Some(name) = ty_alias.name() @@ -96,7 +99,7 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> syntax_editor::Position::before(node), vec![ ty_alias.syntax().clone().into(), - make::tokens::whitespace(&format!("\n\n{indent}")).into(), + make.whitespace(&format!("\n\n{indent}")).into(), ], ); diff --git a/crates/ide-assists/src/handlers/generate_default_from_new.rs b/crates/ide-assists/src/handlers/generate_default_from_new.rs index 48400d436a..485184723b 100644 --- a/crates/ide-assists/src/handlers/generate_default_from_new.rs +++ b/crates/ide-assists/src/handlers/generate_default_from_new.rs @@ -2,7 +2,7 @@ use ide_db::famous_defs::FamousDefs; use stdx::format_to; use syntax::{ AstNode, - ast::{self, HasGenericParams, HasName, HasTypeBounds, Impl, make}, + ast::{self, HasGenericParams, HasName, HasTypeBounds, Impl, syntax_factory::SyntaxFactory}, }; use crate::{ @@ -72,7 +72,9 @@ pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext<' let default_code = " fn default() -> Self { Self::new() }"; - let code = generate_trait_impl_text_from_impl(&impl_, self_ty, "Default", default_code); + let make = SyntaxFactory::without_mappings(); + let code = + generate_trait_impl_text_from_impl(&impl_, self_ty, "Default", default_code, &make); builder.insert(insert_location.end(), code); }, ) @@ -84,6 +86,7 @@ fn generate_trait_impl_text_from_impl( self_ty: ast::Type, trait_text: &str, code: &str, + make: &SyntaxFactory, ) -> String { let generic_params = impl_.generic_param_list().map(|generic_params| { let lifetime_params = @@ -92,18 +95,18 @@ fn generate_trait_impl_text_from_impl( // remove defaults since they can't be specified in impls let param = match param { ast::TypeOrConstParam::Type(param) => { - let param = make::type_param(param.name()?, param.type_bound_list()); + let param = make.type_param(param.name()?, param.type_bound_list()); ast::GenericParam::TypeParam(param) } ast::TypeOrConstParam::Const(param) => { - let param = make::const_param(param.name()?, param.ty()?); + let param = make.const_param(param.name()?, param.ty()?); ast::GenericParam::ConstParam(param) } }; Some(param) }); - make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params)) + make.generic_param_list(itertools::chain(lifetime_params, ty_or_const_params)) }); let mut buf = String::with_capacity(code.len()); diff --git a/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/crates/ide-assists/src/handlers/generate_fn_type_alias.rs index 7fd94b4bed..6bcbd9b0cc 100644 --- a/crates/ide-assists/src/handlers/generate_fn_type_alias.rs +++ b/crates/ide-assists/src/handlers/generate_fn_type_alias.rs @@ -2,7 +2,7 @@ use either::Either; use ide_db::assists::{AssistId, GroupLabel}; use syntax::{ AstNode, - ast::{self, HasGenericParams, HasName, edit::IndentLevel, make}, + ast::{self, HasGenericParams, HasName, edit::IndentLevel, syntax_factory::SyntaxFactory}, syntax_editor, }; @@ -56,6 +56,7 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) func_node.syntax().text_range(), |builder| { let mut edit = builder.make_editor(func); + let make = SyntaxFactory::without_mappings(); let alias_name = format!("{}Fn", stdx::to_camel_case(&name.to_string())); @@ -68,24 +69,24 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) let is_mut = self_ty.is_mutable_reference(); if let Some(adt) = self_ty.strip_references().as_adt() { - let inner_type = make::ty(adt.name(ctx.db()).as_str()); + let inner_type = make.ty(adt.name(ctx.db()).as_str()); let ast_self_ty = - if is_ref { make::ty_ref(inner_type, is_mut) } else { inner_type }; + if is_ref { make.ty_ref(inner_type, is_mut) } else { inner_type }; - fn_params_vec.push(make::unnamed_param(ast_self_ty)); + fn_params_vec.push(make.unnamed_param(ast_self_ty)); } } fn_params_vec.extend(param_list.params().filter_map(|p| match style { ParamStyle::Named => Some(p), - ParamStyle::Unnamed => p.ty().map(make::unnamed_param), + ParamStyle::Unnamed => p.ty().map(|ty| make.unnamed_param(ty)), })); let generic_params = func_node.generic_param_list(); let is_unsafe = func_node.unsafe_token().is_some(); - let ty = make::ty_fn_ptr( + let ty = make.ty_fn_ptr( is_unsafe, func_node.abi(), fn_params_vec.into_iter(), @@ -93,22 +94,21 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ); // Insert new alias - let ty_alias = make::ty_alias( + let ty_alias = make.ty_alias( None, &alias_name, generic_params, None, None, Some((ast::Type::FnPtrType(ty), None)), - ) - .clone_for_update(); + ); let indent = IndentLevel::from_node(insertion_node); edit.insert_all( syntax_editor::Position::before(insertion_node), vec![ ty_alias.syntax().clone().into(), - make::tokens::whitespace(&format!("\n\n{indent}")).into(), + make.whitespace(&format!("\n\n{indent}")).into(), ], ); diff --git a/crates/ide-assists/src/handlers/move_bounds.rs b/crates/ide-assists/src/handlers/move_bounds.rs index e5425abab0..79b8bd5d3d 100644 --- a/crates/ide-assists/src/handlers/move_bounds.rs +++ b/crates/ide-assists/src/handlers/move_bounds.rs @@ -1,11 +1,8 @@ use either::Either; use syntax::{ - ast::{ - self, AstNode, HasName, HasTypeBounds, - edit_in_place::{GenericParamsOwnerEdit, Removable}, - make, - }, + ast::{self, AstNode, HasName, HasTypeBounds, syntax_factory::SyntaxFactory}, match_ast, + syntax_editor::{GetOrCreateWhereClause, Removable}, }; use crate::{AssistContext, AssistId, Assists}; @@ -47,18 +44,23 @@ pub(crate) fn move_bounds_to_where_clause( AssistId::refactor_rewrite("move_bounds_to_where_clause"), "Move to where clause", target, - |edit| { - let type_param_list = edit.make_mut(type_param_list); - let parent = edit.make_syntax_mut(parent); - - let where_clause: ast::WhereClause = match_ast! { - match parent { - ast::Fn(it) => it.get_or_create_where_clause(), - ast::Trait(it) => it.get_or_create_where_clause(), - ast::Impl(it) => it.get_or_create_where_clause(), - ast::Enum(it) => it.get_or_create_where_clause(), - ast::Struct(it) => it.get_or_create_where_clause(), - ast::TypeAlias(it) => it.get_or_create_where_clause(), + |builder| { + let mut edit = builder.make_editor(&parent); + let make = SyntaxFactory::without_mappings(); + + let new_preds: Vec<ast::WherePred> = type_param_list + .generic_params() + .filter_map(|param| build_predicate(param, &make)) + .collect(); + + match_ast! { + match (&parent) { + ast::Fn(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()), + ast::Trait(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()), + ast::Impl(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()), + ast::Enum(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()), + ast::Struct(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()), + ast::TypeAlias(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()), _ => return, } }; @@ -70,25 +72,22 @@ pub(crate) fn move_bounds_to_where_clause( ast::GenericParam::ConstParam(_) => continue, }; if let Some(tbl) = param.type_bound_list() { - if let Some(predicate) = build_predicate(generic_param) { - where_clause.add_predicate(predicate) - } - tbl.remove() + tbl.remove(&mut edit); } } + + builder.add_file_edits(ctx.vfs_file_id(), edit); }, ) } -fn build_predicate(param: ast::GenericParam) -> Option<ast::WherePred> { +fn build_predicate(param: ast::GenericParam, make: &SyntaxFactory) -> Option<ast::WherePred> { let target = match ¶m { - ast::GenericParam::TypeParam(t) => { - Either::Right(make::ty_path(make::ext::ident_path(&t.name()?.to_string()))) - } + ast::GenericParam::TypeParam(t) => Either::Right(make.ty(&t.name()?.to_string())), ast::GenericParam::LifetimeParam(l) => Either::Left(l.lifetime()?), ast::GenericParam::ConstParam(_) => return None, }; - let predicate = make::where_pred( + let predicate = make.where_pred( target, match param { ast::GenericParam::TypeParam(t) => t.type_bound_list()?, @@ -97,7 +96,7 @@ fn build_predicate(param: ast::GenericParam) -> Option<ast::WherePred> { } .bounds(), ); - Some(predicate.clone_for_update()) + Some(predicate) } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index df7057835c..018642a047 100644 --- a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -5,9 +5,10 @@ use ide_db::{ defs::Definition, search::{SearchScope, UsageSearchResult}, }; +use syntax::ast::syntax_factory::SyntaxFactory; use syntax::{ AstNode, - ast::{self, HasGenericParams, HasName, HasTypeBounds, Name, NameLike, PathType, make}, + ast::{self, HasGenericParams, HasName, HasTypeBounds, Name, NameLike, PathType}, match_ast, }; @@ -72,6 +73,7 @@ pub(crate) fn replace_named_generic_with_impl( target, |edit| { let mut editor = edit.make_editor(type_param.syntax()); + let make = SyntaxFactory::without_mappings(); // remove trait from generic param list if let Some(generic_params) = fn_.generic_param_list() { @@ -83,17 +85,14 @@ pub(crate) fn replace_named_generic_with_impl( if params.is_empty() { editor.delete(generic_params.syntax()); } else { - let new_generic_param_list = make::generic_param_list(params); - editor.replace( - generic_params.syntax(), - new_generic_param_list.syntax().clone_for_update(), - ); + let new_generic_param_list = make.generic_param_list(params); + editor.replace(generic_params.syntax(), new_generic_param_list.syntax()); } } - let new_bounds = make::impl_trait_type(type_bound_list); + let new_bounds = make.impl_trait_type(type_bound_list); for path_type in path_types_to_replace.iter().rev() { - editor.replace(path_type.syntax(), new_bounds.clone_for_update().syntax()); + editor.replace(path_type.syntax(), new_bounds.syntax()); } edit.add_file_edits(ctx.vfs_file_id(), editor); }, diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs index 27182191c3..50fe565380 100644 --- a/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -1,9 +1,11 @@ //! Wrappers over [`make`] constructors +use either::Either; + use crate::{ AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, ast::{ self, HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, - HasTypeBounds, HasVisibility, RangeItem, make, + HasTypeBounds, HasVisibility, Param, RangeItem, make, }, syntax_editor::SyntaxMappingBuilder, }; @@ -97,6 +99,52 @@ impl SyntaxFactory { make::struct_(visibility, strukt_name, generic_param_list, field_list).clone_for_update() } + pub fn enum_( + &self, + attrs: impl IntoIterator<Item = ast::Attr>, + visibility: Option<ast::Visibility>, + enum_name: ast::Name, + generic_param_list: Option<ast::GenericParamList>, + where_clause: Option<ast::WhereClause>, + variant_list: ast::VariantList, + ) -> ast::Enum { + make::enum_(attrs, visibility, enum_name, generic_param_list, where_clause, variant_list) + .clone_for_update() + } + + pub fn unnamed_param(&self, ty: ast::Type) -> ast::Param { + make::unnamed_param(ty).clone_for_update() + } + + pub fn ty_fn_ptr<I: Iterator<Item = Param>>( + &self, + is_unsafe: bool, + abi: Option<ast::Abi>, + params: I, + ret_type: Option<ast::RetType>, + ) -> ast::FnPtrType { + make::ty_fn_ptr(is_unsafe, abi, params, ret_type).clone_for_update() + } + + pub fn where_pred( + &self, + path: Either<ast::Lifetime, ast::Type>, + bounds: impl IntoIterator<Item = ast::TypeBound>, + ) -> ast::WherePred { + make::where_pred(path, bounds).clone_for_update() + } + + pub fn where_clause( + &self, + predicates: impl IntoIterator<Item = ast::WherePred>, + ) -> ast::WhereClause { + make::where_clause(predicates).clone_for_update() + } + + pub fn impl_trait_type(&self, bounds: ast::TypeBoundList) -> ast::ImplTraitType { + make::impl_trait_type(bounds).clone_for_update() + } + pub fn expr_field(&self, receiver: ast::Expr, field: &str) -> ast::FieldExpr { let ast::Expr::FieldExpr(ast) = make::expr_field(receiver.clone(), field).clone_for_update() @@ -287,6 +335,26 @@ impl SyntaxFactory { ast } + pub fn generic_ty_path_segment( + &self, + name_ref: ast::NameRef, + generic_args: impl IntoIterator<Item = ast::GenericArg>, + ) -> ast::PathSegment { + make::generic_ty_path_segment(name_ref, generic_args).clone_for_update() + } + + pub fn tail_only_block_expr(&self, tail_expr: ast::Expr) -> ast::BlockExpr { + make::tail_only_block_expr(tail_expr) + } + + pub fn expr_bin_op(&self, lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::Expr { + make::expr_bin_op(lhs, op, rhs) + } + + pub fn ty_placeholder(&self) -> ast::Type { + make::ty_placeholder().clone_for_update() + } + pub fn path_segment_generics( &self, name_ref: ast::NameRef, diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs index 5683d891be..e6937e4d0f 100644 --- a/crates/syntax/src/syntax_editor.rs +++ b/crates/syntax/src/syntax_editor.rs @@ -20,7 +20,7 @@ mod edit_algo; mod edits; mod mapping; -pub use edits::Removable; +pub use edits::{GetOrCreateWhereClause, Removable}; pub use mapping::{SyntaxMapping, SyntaxMappingBuilder}; #[derive(Debug)] diff --git a/crates/syntax/src/syntax_editor/edits.rs b/crates/syntax/src/syntax_editor/edits.rs index 9090f7c9eb..ad08928923 100644 --- a/crates/syntax/src/syntax_editor/edits.rs +++ b/crates/syntax/src/syntax_editor/edits.rs @@ -10,6 +10,107 @@ use crate::{ syntax_editor::{Position, SyntaxEditor}, }; +pub trait GetOrCreateWhereClause: ast::HasGenericParams { + fn where_clause_position(&self) -> Option<Position>; + + fn get_or_create_where_clause( + &self, + editor: &mut SyntaxEditor, + make: &SyntaxFactory, + new_preds: impl Iterator<Item = ast::WherePred>, + ) { + let existing = self.where_clause(); + let all_preds: Vec<_> = + existing.iter().flat_map(|wc| wc.predicates()).chain(new_preds).collect(); + let new_where = make.where_clause(all_preds); + + if let Some(existing) = &existing { + editor.replace(existing.syntax(), new_where.syntax()); + } else if let Some(pos) = self.where_clause_position() { + editor.insert_all( + pos, + vec![make.whitespace(" ").into(), new_where.syntax().clone().into()], + ); + } + } +} + +impl GetOrCreateWhereClause for ast::Fn { + fn where_clause_position(&self) -> Option<Position> { + if let Some(ty) = self.ret_type() { + Some(Position::after(ty.syntax())) + } else if let Some(param_list) = self.param_list() { + Some(Position::after(param_list.syntax())) + } else { + Some(Position::last_child_of(self.syntax())) + } + } +} + +impl GetOrCreateWhereClause for ast::Impl { + fn where_clause_position(&self) -> Option<Position> { + if let Some(ty) = self.self_ty() { + Some(Position::after(ty.syntax())) + } else { + Some(Position::last_child_of(self.syntax())) + } + } +} + +impl GetOrCreateWhereClause for ast::Trait { + fn where_clause_position(&self) -> Option<Position> { + if let Some(gpl) = self.generic_param_list() { + Some(Position::after(gpl.syntax())) + } else if let Some(name) = self.name() { + Some(Position::after(name.syntax())) + } else { + Some(Position::last_child_of(self.syntax())) + } + } +} + +impl GetOrCreateWhereClause for ast::TypeAlias { + fn where_clause_position(&self) -> Option<Position> { + if let Some(gpl) = self.generic_param_list() { + Some(Position::after(gpl.syntax())) + } else if let Some(name) = self.name() { + Some(Position::after(name.syntax())) + } else { + Some(Position::last_child_of(self.syntax())) + } + } +} + +impl GetOrCreateWhereClause for ast::Struct { + fn where_clause_position(&self) -> Option<Position> { + let tfl = self.field_list().and_then(|fl| match fl { + ast::FieldList::RecordFieldList(_) => None, + ast::FieldList::TupleFieldList(it) => Some(it), + }); + if let Some(tfl) = tfl { + Some(Position::after(tfl.syntax())) + } else if let Some(gpl) = self.generic_param_list() { + Some(Position::after(gpl.syntax())) + } else if let Some(name) = self.name() { + Some(Position::after(name.syntax())) + } else { + Some(Position::last_child_of(self.syntax())) + } + } +} + +impl GetOrCreateWhereClause for ast::Enum { + fn where_clause_position(&self) -> Option<Position> { + if let Some(gpl) = self.generic_param_list() { + Some(Position::after(gpl.syntax())) + } else if let Some(name) = self.name() { + Some(Position::after(name.syntax())) + } else { + Some(Position::last_child_of(self.syntax())) + } + } +} + impl SyntaxEditor { /// Adds a new generic param to the function using `SyntaxEditor` pub fn add_generic_param(&mut self, function: &Fn, new_param: GenericParam) { |