Merge pull request #20137 from Hmikihiro/migrate-wrap_unwrap_cfg_attr-assist-to-syntaxeditor
Some checks are pending
metrics / build_metrics (push) Waiting to run
metrics / other_metrics (diesel-1.4.8) (push) Blocked by required conditions
metrics / other_metrics (hyper-0.14.18) (push) Blocked by required conditions
metrics / other_metrics (ripgrep-13.0.0) (push) Blocked by required conditions
metrics / other_metrics (self) (push) Blocked by required conditions
metrics / other_metrics (webrender-2022) (push) Blocked by required conditions
metrics / generate_final_metrics (push) Blocked by required conditions
rustdoc / rustdoc (push) Waiting to run

Migrate `wrap_unwrap_cfg_attr` Assist to use `SyntaxEditor`
This commit is contained in:
Laurențiu Nicola 2025-07-02 05:24:36 +00:00 committed by GitHub
commit 11d45c8813
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 85 additions and 42 deletions

View file

@ -2,8 +2,7 @@ use ide_db::source_change::SourceChangeBuilder;
use itertools::Itertools;
use syntax::{
NodeOrToken, SyntaxToken, T, TextRange, algo,
ast::{self, AstNode, make},
ted::{self, Position},
ast::{self, AstNode, make, syntax_factory::SyntaxFactory},
};
use crate::{AssistContext, AssistId, Assists};
@ -173,40 +172,45 @@ fn wrap_derive(
}
}
let handle_source_change = |edit: &mut SourceChangeBuilder| {
let new_derive = make::attr_outer(make::meta_token_tree(
make::ext::ident_path("derive"),
make::token_tree(T!['('], new_derive),
))
.clone_for_update();
let meta = make::meta_token_tree(
make::ext::ident_path("cfg_attr"),
make::token_tree(
let make = SyntaxFactory::with_mappings();
let mut editor = edit.make_editor(attr.syntax());
let new_derive = make.attr_outer(
make.meta_token_tree(make.ident_path("derive"), make.token_tree(T!['('], new_derive)),
);
let meta = make.meta_token_tree(
make.ident_path("cfg_attr"),
make.token_tree(
T!['('],
vec![
NodeOrToken::Token(make::token(T![,])),
NodeOrToken::Token(make::tokens::whitespace(" ")),
NodeOrToken::Token(make::tokens::ident("derive")),
NodeOrToken::Node(make::token_tree(T!['('], cfg_derive_tokens)),
NodeOrToken::Token(make.token(T![,])),
NodeOrToken::Token(make.whitespace(" ")),
NodeOrToken::Token(make.ident("derive")),
NodeOrToken::Node(make.token_tree(T!['('], cfg_derive_tokens)),
],
),
);
// Remove the derive attribute
let edit_attr = edit.make_syntax_mut(attr.syntax().clone());
ted::replace(edit_attr, new_derive.syntax().clone());
let cfg_attr = make::attr_outer(meta).clone_for_update();
ted::insert_all_raw(
Position::after(new_derive.syntax().clone()),
vec![make::tokens::whitespace("\n").into(), cfg_attr.syntax().clone().into()],
let cfg_attr = make.attr_outer(meta);
editor.replace_with_many(
attr.syntax(),
vec![
new_derive.syntax().clone().into(),
make.whitespace("\n").into(),
cfg_attr.syntax().clone().into(),
],
);
if let Some(snippet_cap) = ctx.config.snippet_cap {
if let Some(first_meta) =
cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token())
{
edit.add_tabstop_after_token(snippet_cap, first_meta)
let tabstop = edit.make_tabstop_after(snippet_cap);
editor.add_annotation(first_meta, tabstop);
}
}
editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(ctx.vfs_file_id(), editor);
};
acc.add(
@ -221,10 +225,10 @@ fn wrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>, attr: ast::Attr) ->
let range = attr.syntax().text_range();
let path = attr.path()?;
let handle_source_change = |edit: &mut SourceChangeBuilder| {
let mut raw_tokens = vec![
NodeOrToken::Token(make::token(T![,])),
NodeOrToken::Token(make::tokens::whitespace(" ")),
];
let make = SyntaxFactory::with_mappings();
let mut editor = edit.make_editor(attr.syntax());
let mut raw_tokens =
vec![NodeOrToken::Token(make.token(T![,])), NodeOrToken::Token(make.whitespace(" "))];
path.syntax().descendants_with_tokens().for_each(|it| {
if let NodeOrToken::Token(token) = it {
raw_tokens.push(NodeOrToken::Token(token));
@ -232,9 +236,9 @@ fn wrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>, attr: ast::Attr) ->
});
if let Some(meta) = attr.meta() {
if let (Some(eq), Some(expr)) = (meta.eq_token(), meta.expr()) {
raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" ")));
raw_tokens.push(NodeOrToken::Token(make.whitespace(" ")));
raw_tokens.push(NodeOrToken::Token(eq));
raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" ")));
raw_tokens.push(NodeOrToken::Token(make.whitespace(" ")));
expr.syntax().descendants_with_tokens().for_each(|it| {
if let NodeOrToken::Token(token) = it {
@ -245,26 +249,24 @@ fn wrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>, attr: ast::Attr) ->
raw_tokens.extend(tt.token_trees_and_tokens());
}
}
let meta = make::meta_token_tree(
make::ext::ident_path("cfg_attr"),
make::token_tree(T!['('], raw_tokens),
);
let cfg_attr = if attr.excl_token().is_some() {
make::attr_inner(meta)
} else {
make::attr_outer(meta)
}
.clone_for_update();
let attr_syntax = edit.make_syntax_mut(attr.syntax().clone());
ted::replace(attr_syntax, cfg_attr.syntax());
let meta =
make.meta_token_tree(make.ident_path("cfg_attr"), make.token_tree(T!['('], raw_tokens));
let cfg_attr =
if attr.excl_token().is_some() { make.attr_inner(meta) } else { make.attr_outer(meta) };
editor.replace(attr.syntax(), cfg_attr.syntax());
if let Some(snippet_cap) = ctx.config.snippet_cap {
if let Some(first_meta) =
cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token())
{
edit.add_tabstop_after_token(snippet_cap, first_meta)
let tabstop = edit.make_tabstop_after(snippet_cap);
editor.add_annotation(first_meta, tabstop);
}
}
editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(ctx.vfs_file_id(), editor);
};
acc.add(
AssistId::refactor("wrap_unwrap_cfg_attr"),

View file

@ -1212,6 +1212,43 @@ impl SyntaxFactory {
ast
}
pub fn attr_outer(&self, meta: ast::Meta) -> ast::Attr {
let ast = make::attr_outer(meta.clone()).clone_for_update();
if let Some(mut mapping) = self.mappings() {
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
builder.map_node(meta.syntax().clone(), ast.meta().unwrap().syntax().clone());
builder.finish(&mut mapping);
}
ast
}
pub fn attr_inner(&self, meta: ast::Meta) -> ast::Attr {
let ast = make::attr_inner(meta.clone()).clone_for_update();
if let Some(mut mapping) = self.mappings() {
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
builder.map_node(meta.syntax().clone(), ast.meta().unwrap().syntax().clone());
builder.finish(&mut mapping);
}
ast
}
pub fn meta_token_tree(&self, path: ast::Path, tt: ast::TokenTree) -> ast::Meta {
let ast = make::meta_token_tree(path.clone(), tt.clone()).clone_for_update();
if let Some(mut mapping) = self.mappings() {
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone());
builder.map_node(tt.syntax().clone(), ast.token_tree().unwrap().syntax().clone());
builder.finish(&mut mapping);
}
ast
}
pub fn token_tree(
&self,
delimiter: SyntaxKind,
@ -1242,6 +1279,10 @@ impl SyntaxFactory {
pub fn whitespace(&self, text: &str) -> SyntaxToken {
make::tokens::whitespace(text)
}
pub fn ident(&self, text: &str) -> SyntaxToken {
make::tokens::ident(text)
}
}
// `ext` constructors