Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22036 from Shourya742/2026-04-14-remove-set-path
Replace set path usage with set_path_with_editor
| -rw-r--r-- | crates/ide-assists/src/handlers/convert_let_else_to_match.rs | 57 | ||||
| -rw-r--r-- | crates/ide-assists/src/handlers/destructure_tuple_binding.rs | 6 | ||||
| -rw-r--r-- | crates/syntax/src/ast/edit.rs | 68 | ||||
| -rw-r--r-- | crates/syntax/src/ast/edit_in_place.rs | 128 |
4 files changed, 101 insertions, 158 deletions
diff --git a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 9a9808e270..994fb44279 100644 --- a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -3,6 +3,7 @@ use syntax::ast::RangeItem; use syntax::ast::edit::AstNodeEdit; use syntax::ast::syntax_factory::SyntaxFactory; use syntax::ast::{self, AstNode, HasName, LetStmt, Pat}; +use syntax::syntax_editor::SyntaxEditor; use crate::{AssistContext, AssistId, Assists}; @@ -25,12 +26,15 @@ use crate::{AssistContext, AssistId, Assists}; // } // ``` pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let root = ctx.source_file().syntax().clone(); + let (mut editor, _) = SyntaxEditor::new(root); // Should focus on the `else` token to trigger let let_stmt = ctx .find_token_syntax_at_offset(T![else]) .and_then(|it| it.parent()?.parent()) .or_else(|| ctx.find_token_syntax_at_offset(T![let])?.parent())?; let let_stmt = LetStmt::cast(let_stmt)?; + let make = SyntaxFactory::with_mappings(); let else_block = let_stmt.let_else()?.block_expr()?; let else_expr = if else_block.statements().next().is_none() && let Some(tail_expr) = else_block.tail_expr() @@ -45,10 +49,8 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' return None; } let pat = let_stmt.pat()?; - - let make = SyntaxFactory::with_mappings(); let mut idents = Vec::default(); - let pat_without_mut = remove_mut_and_collect_idents(&make, &pat, &mut idents)?; + let pat_without_mut = remove_mut_and_collect_idents(&make, &mut editor, &pat, &mut idents)?; let bindings = idents .into_iter() .filter_map(|ref pat| { @@ -70,7 +72,7 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' }, let_stmt.syntax().text_range(), |builder| { - let mut editor = builder.make_editor(let_stmt.syntax()); + // let mut editor = builder.make_editor(let_stmt.syntax()); let binding_paths = bindings .iter() @@ -124,6 +126,7 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' fn remove_mut_and_collect_idents( make: &SyntaxFactory, + editor: &mut SyntaxEditor, pat: &ast::Pat, acc: &mut Vec<ast::IdentPat>, ) -> Option<ast::Pat> { @@ -135,34 +138,40 @@ fn remove_mut_and_collect_idents( p.ref_token().is_some() && p.mut_token().is_some(), p.name()?, ); - if let Some(inner) = p.pat() { - non_mut_pat.set_pat(remove_mut_and_collect_idents(make, &inner, acc)); - } + let non_mut_pat = if let Some(inner) = p.pat() { + non_mut_pat.set_pat( + remove_mut_and_collect_idents(make, editor, &inner, acc), + editor, + make, + ) + } else { + non_mut_pat + }; non_mut_pat.into() } ast::Pat::BoxPat(p) => { - make.box_pat(remove_mut_and_collect_idents(make, &p.pat()?, acc)?).into() + make.box_pat(remove_mut_and_collect_idents(make, editor, &p.pat()?, acc)?).into() } ast::Pat::OrPat(p) => make .or_pat( p.pats() - .map(|pat| remove_mut_and_collect_idents(make, &pat, acc)) + .map(|pat| remove_mut_and_collect_idents(make, editor, &pat, acc)) .collect::<Option<Vec<_>>>()?, p.leading_pipe().is_some(), ) .into(), ast::Pat::ParenPat(p) => { - make.paren_pat(remove_mut_and_collect_idents(make, &p.pat()?, acc)?).into() + make.paren_pat(remove_mut_and_collect_idents(make, editor, &p.pat()?, acc)?).into() } ast::Pat::RangePat(p) => make .range_pat( if let Some(start) = p.start() { - Some(remove_mut_and_collect_idents(make, &start, acc)?) + Some(remove_mut_and_collect_idents(make, editor, &start, acc)?) } else { None }, if let Some(end) = p.end() { - Some(remove_mut_and_collect_idents(make, &end, acc)?) + Some(remove_mut_and_collect_idents(make, editor, &end, acc)?) } else { None }, @@ -175,13 +184,15 @@ fn remove_mut_and_collect_idents( p.record_pat_field_list()? .fields() .map(|field| { - remove_mut_and_collect_idents(make, &field.pat()?, acc).map(|pat| { - if let Some(name_ref) = field.name_ref() { - make.record_pat_field(name_ref, pat) - } else { - make.record_pat_field_shorthand(pat) - } - }) + remove_mut_and_collect_idents(make, editor, &field.pat()?, acc).map( + |pat| { + if let Some(name_ref) = field.name_ref() { + make.record_pat_field(name_ref, pat) + } else { + make.record_pat_field_shorthand(pat) + } + }, + ) }) .collect::<Option<Vec<_>>>()?, p.record_pat_field_list()?.rest_pat(), @@ -194,20 +205,20 @@ fn remove_mut_and_collect_idents( acc.push(ident); p.clone().into() } else { - make.ref_pat(remove_mut_and_collect_idents(make, &inner, acc)?).into() + make.ref_pat(remove_mut_and_collect_idents(make, editor, &inner, acc)?).into() } } ast::Pat::SlicePat(p) => make .slice_pat( p.pats() - .map(|pat| remove_mut_and_collect_idents(make, &pat, acc)) + .map(|pat| remove_mut_and_collect_idents(make, editor, &pat, acc)) .collect::<Option<Vec<_>>>()?, ) .into(), ast::Pat::TuplePat(p) => make .tuple_pat( p.fields() - .map(|field| remove_mut_and_collect_idents(make, &field, acc)) + .map(|field| remove_mut_and_collect_idents(make, editor, &field, acc)) .collect::<Option<Vec<_>>>()?, ) .into(), @@ -215,7 +226,7 @@ fn remove_mut_and_collect_idents( .tuple_struct_pat( p.path()?, p.fields() - .map(|field| remove_mut_and_collect_idents(make, &field, acc)) + .map(|field| remove_mut_and_collect_idents(make, editor, &field, acc)) .collect::<Option<Vec<_>>>()?, ) .into(), diff --git a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index 23c11b258c..05fa00f4e8 100644 --- a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -226,11 +226,7 @@ impl AssignmentEdit { fn apply(self, syntax_editor: &mut SyntaxEditor, syntax_mapping: &SyntaxFactory) { // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)` if self.in_sub_pattern { - self.ident_pat.set_pat_with_editor( - Some(self.tuple_pat.into()), - syntax_editor, - syntax_mapping, - ) + self.ident_pat.set_pat(Some(self.tuple_pat.into()), syntax_editor, syntax_mapping); } else if self.is_shorthand_field { syntax_editor.insert(Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax()); syntax_editor diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 23a0411ead..567bd09025 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs @@ -1,12 +1,15 @@ //! This module contains functions for editing syntax trees. As the trees are //! immutable, all function here return a fresh copy of the tree, instead of //! doing an in-place modification. +use parser::T; use std::{fmt, iter, ops}; use crate::{ - AstToken, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, - ast::{self, AstNode, make}, - syntax_editor::{SyntaxEditor, SyntaxMappingBuilder}, + AstToken, NodeOrToken, SyntaxElement, + SyntaxKind::WHITESPACE, + SyntaxNode, SyntaxToken, + ast::{self, AstNode, HasName, make}, + syntax_editor::{Position, SyntaxEditor, SyntaxMappingBuilder}, ted, }; @@ -194,6 +197,65 @@ pub trait AstNodeEdit: AstNode + Clone + Sized { impl<N: AstNode + Clone> AstNodeEdit for N {} +impl ast::IdentPat { + pub fn set_pat( + &self, + pat: Option<ast::Pat>, + syntax_editor: &mut SyntaxEditor, + syntax_factory: &SyntaxFactory, + ) -> ast::IdentPat { + match pat { + None => { + if let Some(at_token) = self.at_token() { + // Remove `@ Pat` + let start = at_token.clone().into(); + let end = self + .pat() + .map(|it| it.syntax().clone().into()) + .unwrap_or_else(|| at_token.into()); + syntax_editor.delete_all(start..=end); + + // Remove any trailing ws + if let Some(last) = + self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) + { + last.detach(); + } + } + } + Some(pat) => { + if let Some(old_pat) = self.pat() { + // Replace existing pattern + syntax_editor.replace(old_pat.syntax(), pat.syntax()) + } else if let Some(at_token) = self.at_token() { + // Have an `@` token but not a pattern yet + syntax_editor.insert(Position::after(at_token), pat.syntax()); + } else { + // Don't have an `@`, should have a name + let name = self.name().unwrap(); + let elements = vec![ + syntax_factory.whitespace(" ").into(), + syntax_factory.token(T![@]).into(), + syntax_factory.whitespace(" ").into(), + pat.syntax().clone().into(), + ]; + + if self.syntax().parent().is_none() { + let (mut local, local_self) = SyntaxEditor::with_ast_node(self); + let local_name = local_self.name().unwrap(); + local.insert_all(Position::after(local_name.syntax()), elements); + let edit = local.finish(); + return ast::IdentPat::cast(edit.new_root().clone()).unwrap(); + } else { + syntax_editor.insert_all(Position::after(name.syntax()), elements); + } + } + } + } + self.clone() + } +} + #[test] fn test_increase_indent() { let arm_list = { diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 9171987f85..157285d1b6 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs @@ -9,8 +9,7 @@ use crate::{ SyntaxKind::{ATTR, COMMENT, WHITESPACE}, SyntaxNode, SyntaxToken, algo::{self, neighbor}, - ast::{self, edit::IndentLevel, make, syntax_factory::SyntaxFactory}, - syntax_editor::{Position, SyntaxEditor}, + ast::{self, edit::IndentLevel, make}, ted, }; @@ -528,103 +527,6 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { Some(()) } -impl ast::IdentPat { - pub fn set_pat(&self, pat: Option<ast::Pat>) { - match pat { - None => { - if let Some(at_token) = self.at_token() { - // Remove `@ Pat` - let start = at_token.clone().into(); - let end = self - .pat() - .map(|it| it.syntax().clone().into()) - .unwrap_or_else(|| at_token.into()); - - ted::remove_all(start..=end); - - // Remove any trailing ws - if let Some(last) = - self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) - { - last.detach(); - } - } - } - Some(pat) => { - if let Some(old_pat) = self.pat() { - // Replace existing pattern - ted::replace(old_pat.syntax(), pat.syntax()) - } else if let Some(at_token) = self.at_token() { - // Have an `@` token but not a pattern yet - ted::insert(ted::Position::after(at_token), pat.syntax()); - } else { - // Don't have an `@`, should have a name - let name = self.name().unwrap(); - - ted::insert_all( - ted::Position::after(name.syntax()), - vec![ - make::token(T![@]).into(), - make::tokens::single_space().into(), - pat.syntax().clone().into(), - ], - ) - } - } - } - } - - pub fn set_pat_with_editor( - &self, - pat: Option<ast::Pat>, - syntax_editor: &mut SyntaxEditor, - syntax_factory: &SyntaxFactory, - ) { - match pat { - None => { - if let Some(at_token) = self.at_token() { - // Remove `@ Pat` - let start = at_token.clone().into(); - let end = self - .pat() - .map(|it| it.syntax().clone().into()) - .unwrap_or_else(|| at_token.into()); - syntax_editor.delete_all(start..=end); - - // Remove any trailing ws - if let Some(last) = - self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) - { - last.detach(); - } - } - } - Some(pat) => { - if let Some(old_pat) = self.pat() { - // Replace existing pattern - syntax_editor.replace(old_pat.syntax(), pat.syntax()) - } else if let Some(at_token) = self.at_token() { - // Have an `@` token but not a pattern yet - syntax_editor.insert(Position::after(at_token), pat.syntax()); - } else { - // Don't have an `@`, should have a name - let name = self.name().unwrap(); - - syntax_editor.insert_all( - Position::after(name.syntax()), - vec![ - syntax_factory.whitespace(" ").into(), - syntax_factory.token(T![@]).into(), - syntax_factory.whitespace(" ").into(), - pat.syntax().clone().into(), - ], - ) - } - } - } - } -} - pub trait Indent: AstNode + Clone + Sized { fn indent_level(&self) -> IndentLevel { IndentLevel::from_node(self.syntax()) @@ -674,32 +576,4 @@ mod tests { }", ); } - - #[test] - fn test_ident_pat_set_pat() { - #[track_caller] - fn check(before: &str, expected: &str, pat: Option<ast::Pat>) { - let pat = pat.map(|it| it.clone_for_update()); - - let ident_pat = ast_mut_from_text::<ast::IdentPat>(&format!("fn f() {{ {before} }}")); - ident_pat.set_pat(pat); - - let after = ast_mut_from_text::<ast::IdentPat>(&format!("fn f() {{ {expected} }}")); - assert_eq!(ident_pat.to_string(), after.to_string()); - } - - // replacing - check("let a @ _;", "let a @ ();", Some(make::tuple_pat([]).into())); - - // note: no trailing semicolon is added for the below tests since it - // seems to be picked up by the ident pat during error recovery? - - // adding - check("let a ", "let a @ ()", Some(make::tuple_pat([]).into())); - check("let a @ ", "let a @ ()", Some(make::tuple_pat([]).into())); - - // removing - check("let a @ ()", "let a", None); - check("let a @ ", "let a", None); - } } |