Skip to content

Migrate wrap_unwrap_cfg_attr Assist to use SyntaxEditor #20137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 44 additions & 42 deletions crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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(
Expand All @@ -221,20 +225,20 @@ 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));
}
});
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 {
Expand All @@ -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"),
Expand Down
41 changes: 41 additions & 0 deletions crates/syntax/src/ast/syntax_factory/constructors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down