Migrate remove_dbg assist to use SyntaxEditor

This commit is contained in:
Hayashi Mikihiro 2025-07-10 18:42:18 +09:00
parent e9968fc555
commit c79e24c1f1

View file

@ -1,8 +1,9 @@
use itertools::Itertools; use itertools::Itertools;
use syntax::{ use syntax::{
Edition, NodeOrToken, SyntaxElement, T, TextRange, TextSize, Edition, NodeOrToken, SyntaxNode, SyntaxToken, T,
ast::{self, AstNode, AstToken, make}, ast::{self, AstNode, make},
match_ast, ted, match_ast,
syntax_editor::{Position, SyntaxEditor},
}; };
use crate::{AssistContext, AssistId, Assists}; use crate::{AssistContext, AssistId, Assists};
@ -40,21 +41,23 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
let replacements = let replacements =
macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::<Vec<_>>(); macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::<Vec<_>>();
let target = replacements
acc.add( .iter()
AssistId::quick_fix("remove_dbg"), .flat_map(|(node_or_token, _)| node_or_token.iter())
"Remove dbg!()", .map(|t| t.text_range())
replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range))?, .reduce(|acc, range| acc.cover(range))?;
|builder| { acc.add(AssistId::quick_fix("remove_dbg"), "Remove dbg!()", target, |builder| {
let mut editor = builder.make_editor(ctx.source_file().syntax());
for (range, expr) in replacements { for (range, expr) in replacements {
if let Some(expr) = expr { if let Some(expr) = expr {
builder.replace(range, expr.to_string()); editor.insert(Position::before(range[0].clone()), expr.syntax().clone_for_update());
} else { }
builder.delete(range); for node_or_token in range {
editor.delete(node_or_token);
} }
} }
}, builder.add_file_edits(ctx.vfs_file_id(), editor);
) })
} }
/// Returns `None` when either /// Returns `None` when either
@ -63,7 +66,9 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
/// - (`macro_expr` has no parent - is that possible?) /// - (`macro_expr` has no parent - is that possible?)
/// ///
/// Returns `Some(_, None)` when the macro call should just be removed. /// Returns `Some(_, None)` when the macro call should just be removed.
fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Option<ast::Expr>)> { fn compute_dbg_replacement(
macro_expr: ast::MacroExpr,
) -> Option<(Vec<NodeOrToken<SyntaxNode, SyntaxToken>>, Option<ast::Expr>)> {
let macro_call = macro_expr.macro_call()?; let macro_call = macro_expr.macro_call()?;
let tt = macro_call.token_tree()?; let tt = macro_call.token_tree()?;
let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?); let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?);
@ -88,22 +93,22 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
match_ast! { match_ast! {
match parent { match parent {
ast::StmtList(_) => { ast::StmtList(_) => {
let range = macro_expr.syntax().text_range(); let mut replace = vec![macro_expr.syntax().clone().into()];
let range = match whitespace_start(macro_expr.syntax().prev_sibling_or_token()) { if let Some(prev_sibling) = macro_expr.syntax().prev_sibling_or_token()
Some(start) => range.cover_offset(start), && prev_sibling.kind() == syntax::SyntaxKind::WHITESPACE {
None => range, replace.push(prev_sibling);
}; }
(range, None) (replace, None)
}, },
ast::ExprStmt(it) => { ast::ExprStmt(it) => {
let range = it.syntax().text_range(); let mut replace = vec![it.syntax().clone().into()];
let range = match whitespace_start(it.syntax().prev_sibling_or_token()) { if let Some(prev_sibling) = it.syntax().prev_sibling_or_token()
Some(start) => range.cover_offset(start), && prev_sibling.kind() == syntax::SyntaxKind::WHITESPACE {
None => range, replace.push(prev_sibling);
}; }
(range, None) (replace, None)
}, },
_ => (macro_call.syntax().text_range(), Some(make::ext::expr_unit())), _ => (vec![macro_call.syntax().clone().into()], Some(make::ext::expr_unit())),
} }
} }
} }
@ -147,13 +152,13 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
}; };
let expr = replace_nested_dbgs(expr.clone()); let expr = replace_nested_dbgs(expr.clone());
let expr = if wrap { make::expr_paren(expr).into() } else { expr.clone_subtree() }; let expr = if wrap { make::expr_paren(expr).into() } else { expr.clone_subtree() };
(macro_call.syntax().text_range(), Some(expr)) (vec![macro_call.syntax().clone().into()], Some(expr))
} }
// dbg!(expr0, expr1, ...) // dbg!(expr0, expr1, ...)
exprs => { exprs => {
let exprs = exprs.iter().cloned().map(replace_nested_dbgs); let exprs = exprs.iter().cloned().map(replace_nested_dbgs);
let expr = make::expr_tuple(exprs); let expr = make::expr_tuple(exprs);
(macro_call.syntax().text_range(), Some(expr.into())) (vec![macro_call.syntax().clone().into()], Some(expr.into()))
} }
}) })
} }
@ -178,8 +183,8 @@ fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr {
return replaced; return replaced;
} }
let expanded = expanded.clone_for_update(); let expanded = expanded.clone_subtree();
let mut editor = SyntaxEditor::new(expanded.syntax().clone());
// We need to collect to avoid mutation during traversal. // We need to collect to avoid mutation during traversal.
let macro_exprs: Vec<_> = let macro_exprs: Vec<_> =
expanded.syntax().descendants().filter_map(ast::MacroExpr::cast).collect(); expanded.syntax().descendants().filter_map(ast::MacroExpr::cast).collect();
@ -191,17 +196,13 @@ fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr {
}; };
if let Some(expr) = expr_opt { if let Some(expr) = expr_opt {
ted::replace(mac.syntax(), expr.syntax().clone_for_update()); editor.replace(mac.syntax(), expr.syntax().clone_for_update());
} else { } else {
ted::remove(mac.syntax()); editor.delete(mac.syntax());
} }
} }
let expanded_syntax = editor.finish().new_root().clone();
expanded ast::Expr::cast(expanded_syntax).unwrap()
}
fn whitespace_start(it: Option<SyntaxElement>) -> Option<TextSize> {
Some(it?.into_token().and_then(ast::Whitespace::cast)?.syntax().text_range().start())
} }
#[cfg(test)] #[cfg(test)]