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
Chayim Refael Friedman 5 weeks ago
parent fed32aa · parent db9a4db · commit 3c327c7
-rw-r--r--crates/ide-assists/src/handlers/convert_let_else_to_match.rs57
-rw-r--r--crates/ide-assists/src/handlers/destructure_tuple_binding.rs6
-rw-r--r--crates/syntax/src/ast/edit.rs68
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs128
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);
- }
}