Skip to content

Commit 8d1d5cd

Browse files
committed
Auto merge of #18015 - ChayimFriedman2:flip-comma-attribute, r=Veykril
Handle attributes correctly in "Flip comma" Attributes often contain path followed by a token tree (e.g. `align(2)`), and the previous code handled them as two separate items, which led to results such as `#[repr(alignC, (2))]`. An alternative is to just make the assist unavailable in attributes, like we do in macros. But contrary to macros, attributes often have a fixed form, so this seems useful. Fixes #18013.
2 parents 715e9ff + 5c14f13 commit 8d1d5cd

File tree

1 file changed

+61
-3
lines changed

1 file changed

+61
-3
lines changed

crates/ide-assists/src/handlers/flip_comma.rs

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use syntax::{algo::non_trivia_sibling, Direction, SyntaxKind, T};
1+
use ide_db::base_db::SourceDatabase;
2+
use syntax::TextSize;
3+
use syntax::{
4+
algo::non_trivia_sibling, ast, AstNode, Direction, SyntaxKind, SyntaxToken, TextRange, T,
5+
};
26

37
use crate::{AssistContext, AssistId, AssistKind, Assists};
48

@@ -21,6 +25,8 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
2125
let comma = ctx.find_token_syntax_at_offset(T![,])?;
2226
let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?;
2327
let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?;
28+
let (mut prev_text, mut next_text) = (prev.to_string(), next.to_string());
29+
let (mut prev_range, mut next_range) = (prev.text_range(), next.text_range());
2430

2531
// Don't apply a "flip" in case of a last comma
2632
// that typically comes before punctuation
@@ -34,17 +40,55 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
3440
return None;
3541
}
3642

43+
if let Some(parent) = comma.parent().and_then(ast::TokenTree::cast) {
44+
// An attribute. It often contains a path followed by a token tree (e.g. `align(2)`), so we have
45+
// to be smarter.
46+
let prev_start =
47+
match comma.siblings_with_tokens(Direction::Prev).skip(1).find(|it| it.kind() == T![,])
48+
{
49+
Some(it) => position_after_token(it.as_token().unwrap()),
50+
None => position_after_token(&parent.left_delimiter_token()?),
51+
};
52+
let prev_end = prev.text_range().end();
53+
let next_start = next.text_range().start();
54+
let next_end =
55+
match comma.siblings_with_tokens(Direction::Next).skip(1).find(|it| it.kind() == T![,])
56+
{
57+
Some(it) => position_before_token(it.as_token().unwrap()),
58+
None => position_before_token(&parent.right_delimiter_token()?),
59+
};
60+
prev_range = TextRange::new(prev_start, prev_end);
61+
next_range = TextRange::new(next_start, next_end);
62+
let file_text = ctx.db().file_text(ctx.file_id().file_id());
63+
prev_text = file_text[prev_range].to_owned();
64+
next_text = file_text[next_range].to_owned();
65+
}
66+
3767
acc.add(
3868
AssistId("flip_comma", AssistKind::RefactorRewrite),
3969
"Flip comma",
4070
comma.text_range(),
4171
|edit| {
42-
edit.replace(prev.text_range(), next.to_string());
43-
edit.replace(next.text_range(), prev.to_string());
72+
edit.replace(prev_range, next_text);
73+
edit.replace(next_range, prev_text);
4474
},
4575
)
4676
}
4777

78+
fn position_before_token(token: &SyntaxToken) -> TextSize {
79+
match non_trivia_sibling(token.clone().into(), Direction::Prev) {
80+
Some(prev_token) => prev_token.text_range().end(),
81+
None => token.text_range().start(),
82+
}
83+
}
84+
85+
fn position_after_token(token: &SyntaxToken) -> TextSize {
86+
match non_trivia_sibling(token.clone().into(), Direction::Next) {
87+
Some(prev_token) => prev_token.text_range().start(),
88+
None => token.text_range().end(),
89+
}
90+
}
91+
4892
#[cfg(test)]
4993
mod tests {
5094
use super::*;
@@ -89,4 +133,18 @@ mod tests {
89133
// See https://github.com/rust-lang/rust-analyzer/issues/7693
90134
check_assist_not_applicable(flip_comma, r#"bar!(a,$0 b)"#);
91135
}
136+
137+
#[test]
138+
fn flip_comma_attribute() {
139+
check_assist(
140+
flip_comma,
141+
r#"#[repr(align(2),$0 C)] struct Foo;"#,
142+
r#"#[repr(C, align(2))] struct Foo;"#,
143+
);
144+
check_assist(
145+
flip_comma,
146+
r#"#[foo(bar, baz(1 + 1),$0 qux, other)] struct Foo;"#,
147+
r#"#[foo(bar, qux, baz(1 + 1), other)] struct Foo;"#,
148+
);
149+
}
92150
}

0 commit comments

Comments
 (0)