about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Barsky <me@davidbarsky.com>2024-12-05 18:42:27 +0000
committerGitHub <noreply@github.com>2024-12-05 18:42:27 +0000
commit6ef7f8e01409884d160769d6a62ba3d82dfb0207 (patch)
tree612d6b70114d0f8d36e84f67eefd866f046adba8
parentf31547dc0e1f22228f744266d331185e4dcb73ed (diff)
parent0a99a9f05f3c13fbb4fc972b6d7e6d868af14beb (diff)
downloadrust-6ef7f8e01409884d160769d6a62ba3d82dfb0207.tar.gz
rust-6ef7f8e01409884d160769d6a62ba3d82dfb0207.zip
Merge pull request #18483 from tareknaser/syntax_factory_introduce_named_generic
Migrate `introduce_named_generic` Assist to Use `SyntaxFactory`
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs49
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs28
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs1
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs72
4 files changed, 123 insertions, 27 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs
index bf6ac1719f3..8c276415bb1 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs
@@ -1,9 +1,6 @@
 use ide_db::syntax_helpers::suggest_name;
 use itertools::Itertools;
-use syntax::{
-    ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams, HasName},
-    ted,
-};
+use syntax::ast::{self, syntax_factory::SyntaxFactory, AstNode, HasGenericParams, HasName};
 
 use crate::{AssistContext, AssistId, AssistKind, Assists};
 
@@ -25,42 +22,42 @@ pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_>
 
     let type_bound_list = impl_trait_type.type_bound_list()?;
 
+    let make = SyntaxFactory::new();
     let target = fn_.syntax().text_range();
     acc.add(
         AssistId("introduce_named_generic", AssistKind::RefactorRewrite),
         "Replace impl trait with generic",
         target,
-        |edit| {
-            let impl_trait_type = edit.make_mut(impl_trait_type);
-            let fn_ = edit.make_mut(fn_);
-            let fn_generic_param_list = fn_.get_or_create_generic_param_list();
-
-            let existing_names = fn_generic_param_list
-                .generic_params()
-                .flat_map(|param| match param {
-                    ast::GenericParam::TypeParam(t) => t.name().map(|name| name.to_string()),
-                    p => Some(p.to_string()),
-                })
-                .collect_vec();
+        |builder| {
+            let mut editor = builder.make_editor(fn_.syntax());
+
+            let existing_names = match fn_.generic_param_list() {
+                Some(generic_param_list) => generic_param_list
+                    .generic_params()
+                    .flat_map(|param| match param {
+                        ast::GenericParam::TypeParam(t) => t.name().map(|name| name.to_string()),
+                        p => Some(p.to_string()),
+                    })
+                    .collect_vec(),
+                None => Vec::new(),
+            };
             let type_param_name = suggest_name::NameGenerator::new_with_names(
                 existing_names.iter().map(|s| s.as_str()),
             )
             .for_impl_trait_as_generic(&impl_trait_type);
 
-            let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list))
-                .clone_for_update();
-            let new_ty = make::ty(&type_param_name).clone_for_update();
+            let type_param = make.type_param(make.name(&type_param_name), Some(type_bound_list));
+            let new_ty = make.ty(&type_param_name);
 
-            ted::replace(impl_trait_type.syntax(), new_ty.syntax());
-            fn_generic_param_list.add_generic_param(type_param.into());
+            editor.replace(impl_trait_type.syntax(), new_ty.syntax());
+            editor.add_generic_param(&fn_, type_param.clone().into());
 
             if let Some(cap) = ctx.config.snippet_cap {
-                if let Some(generic_param) =
-                    fn_.generic_param_list().and_then(|it| it.generic_params().last())
-                {
-                    edit.add_tabstop_before(cap, generic_param);
-                }
+                editor.add_annotation(type_param.syntax(), builder.make_tabstop_before(cap));
             }
+
+            editor.add_mappings(make.finish_with_mappings());
+            builder.add_file_edits(ctx.file_id(), editor);
         },
     )
 }
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
index 9f88add0f78..35c467a1e8c 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -2,7 +2,7 @@
 use itertools::Itertools;
 
 use crate::{
-    ast::{self, make, HasName},
+    ast::{self, make, HasName, HasTypeBounds},
     syntax_editor::SyntaxMappingBuilder,
     AstNode,
 };
@@ -14,6 +14,32 @@ impl SyntaxFactory {
         make::name(name).clone_for_update()
     }
 
+    pub fn ty(&self, text: &str) -> ast::Type {
+        make::ty(text).clone_for_update()
+    }
+
+    pub fn type_param(
+        &self,
+        name: ast::Name,
+        bounds: Option<ast::TypeBoundList>,
+    ) -> ast::TypeParam {
+        let ast = make::type_param(name.clone(), bounds.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone());
+            if let Some(input) = bounds {
+                builder.map_node(
+                    input.syntax().clone(),
+                    ast.type_bound_list().unwrap().syntax().clone(),
+                );
+            }
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn ident_pat(&self, ref_: bool, mut_: bool, name: ast::Name) -> ast::IdentPat {
         let ast = make::ident_pat(ref_, mut_, name.clone()).clone_for_update();
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
index 714f5a99111..7e5d0f27e0a 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
@@ -16,6 +16,7 @@ use rustc_hash::FxHashMap;
 use crate::{SyntaxElement, SyntaxNode, SyntaxToken};
 
 mod edit_algo;
+mod edits;
 mod mapping;
 
 pub use mapping::{SyntaxMapping, SyntaxMappingBuilder};
diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs
new file mode 100644
index 00000000000..73196f5cb1b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs
@@ -0,0 +1,72 @@
+//! Structural editing for ast using `SyntaxEditor`
+
+use crate::{
+    ast::make, ast::AstNode, ast::Fn, ast::GenericParam, ast::HasGenericParams, ast::HasName,
+    syntax_editor::Position, syntax_editor::SyntaxEditor, SyntaxKind,
+};
+
+impl SyntaxEditor {
+    /// Adds a new generic param to the function using `SyntaxEditor`
+    pub fn add_generic_param(&mut self, function: &Fn, new_param: GenericParam) {
+        match function.generic_param_list() {
+            Some(generic_param_list) => match generic_param_list.generic_params().last() {
+                Some(last_param) => {
+                    // There exists a generic param list and it's not empty
+                    let position = generic_param_list.r_angle_token().map_or_else(
+                        || Position::last_child_of(function.syntax()),
+                        Position::before,
+                    );
+
+                    if last_param
+                        .syntax()
+                        .next_sibling_or_token()
+                        .map_or(false, |it| it.kind() == SyntaxKind::COMMA)
+                    {
+                        self.insert(
+                            Position::after(last_param.syntax()),
+                            new_param.syntax().clone(),
+                        );
+                        self.insert(
+                            Position::after(last_param.syntax()),
+                            make::token(SyntaxKind::WHITESPACE),
+                        );
+                        self.insert(
+                            Position::after(last_param.syntax()),
+                            make::token(SyntaxKind::COMMA),
+                        );
+                    } else {
+                        let elements = vec![
+                            make::token(SyntaxKind::COMMA).into(),
+                            make::token(SyntaxKind::WHITESPACE).into(),
+                            new_param.syntax().clone().into(),
+                        ];
+                        self.insert_all(position, elements);
+                    }
+                }
+                None => {
+                    // There exists a generic param list but it's empty
+                    let position = Position::after(generic_param_list.l_angle_token().unwrap());
+                    self.insert(position, new_param.syntax());
+                }
+            },
+            None => {
+                // There was no generic param list
+                let position = if let Some(name) = function.name() {
+                    Position::after(name.syntax)
+                } else if let Some(fn_token) = function.fn_token() {
+                    Position::after(fn_token)
+                } else if let Some(param_list) = function.param_list() {
+                    Position::before(param_list.syntax)
+                } else {
+                    Position::last_child_of(function.syntax())
+                };
+                let elements = vec![
+                    make::token(SyntaxKind::L_ANGLE).into(),
+                    new_param.syntax().clone().into(),
+                    make::token(SyntaxKind::R_ANGLE).into(),
+                ];
+                self.insert_all(position, elements);
+            }
+        }
+    }
+}