Skip to content

Commit 77a4803

Browse files
committed
Revisit comment placement in ast and formatting
1 parent a7f9549 commit 77a4803

File tree

4 files changed

+215
-12
lines changed

4 files changed

+215
-12
lines changed

crates/ruff_python_formatter/resources/test/fixtures/black/cases/import_comments.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,14 @@
66
from x import a as b, b as c # comment
77

88
# ensure intermixed end- and own-line comments are all preserved
9-
# and at least kept in their original order, if not their original
10-
# positions within the import statement
9+
from x import ( # one
10+
# two
11+
a # three
12+
# four
13+
, # five
14+
# six
15+
) # seven
16+
1117
from x import ( # alpha
1218
# bravo
1319
a # charlie

crates/ruff_python_formatter/src/comments/placement.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,10 @@ fn handle_enclosed_comment<'a>(
311311
AnyNodeRef::StmtClassDef(class_def) => {
312312
handle_leading_class_with_decorators_comment(comment, class_def)
313313
}
314-
AnyNodeRef::StmtImportFrom(import_from) => handle_import_from_comment(comment, import_from),
314+
AnyNodeRef::StmtImportFrom(import_from) => {
315+
handle_import_from_comment(comment, import_from, source)
316+
}
317+
AnyNodeRef::Alias(alias) => handle_alias_comment(comment, alias, source),
315318
AnyNodeRef::StmtWith(with_) => handle_with_comment(comment, with_),
316319
AnyNodeRef::ExprCall(_) => handle_call_comment(comment),
317320
AnyNodeRef::ExprStringLiteral(_) => match comment.enclosing_parent() {
@@ -1936,6 +1939,7 @@ fn handle_bracketed_end_of_line_comment<'a>(
19361939
fn handle_import_from_comment<'a>(
19371940
comment: DecoratedComment<'a>,
19381941
import_from: &'a ast::StmtImportFrom,
1942+
source: &str,
19391943
) -> CommentPlacement<'a> {
19401944
// The comment needs to be on the same line, but before the first member. For example, we want
19411945
// to treat this as a dangling comment:
@@ -1963,10 +1967,48 @@ fn handle_import_from_comment<'a>(
19631967
{
19641968
CommentPlacement::dangling(comment.enclosing_node(), comment)
19651969
} else {
1966-
CommentPlacement::Default(comment)
1970+
if let Some(SimpleToken {
1971+
kind: SimpleTokenKind::Comma,
1972+
..
1973+
}) = SimpleTokenizer::starts_at(comment.start(), source)
1974+
.skip_trivia()
1975+
.next()
1976+
{
1977+
// treat comments before the comma as dangling, after as trailing (default)
1978+
if let Some(AnyNodeRef::Alias(alias)) = comment.preceding_node() {
1979+
CommentPlacement::dangling(alias, comment)
1980+
} else {
1981+
CommentPlacement::Default(comment)
1982+
}
1983+
} else {
1984+
CommentPlacement::Default(comment)
1985+
}
19671986
}
19681987
}
19691988

1989+
fn handle_alias_comment<'a>(
1990+
comment: DecoratedComment<'a>,
1991+
alias: &'a ruff_python_ast::Alias,
1992+
source: &str,
1993+
) -> CommentPlacement<'a> {
1994+
if let Some(asname) = &alias.asname {
1995+
if let Some(SimpleToken {
1996+
kind: SimpleTokenKind::As,
1997+
range: as_range,
1998+
}) = SimpleTokenizer::starts_at(alias.name.end(), source)
1999+
.skip_trivia()
2000+
.next()
2001+
{
2002+
return if comment.start() < as_range.start() {
2003+
CommentPlacement::trailing(&alias.name, comment)
2004+
} else {
2005+
CommentPlacement::leading(asname, comment)
2006+
};
2007+
}
2008+
}
2009+
CommentPlacement::Default(comment)
2010+
}
2011+
19702012
/// Attach an enclosed end-of-line comment to a [`ast::StmtWith`].
19712013
///
19722014
/// For example, given:

crates/ruff_python_formatter/src/other/alias.rs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,45 @@ impl FormatNodeRule<Alias> for FormatAlias {
1616
name,
1717
asname,
1818
} = item;
19-
DotDelimitedIdentifier::new(name).fmt(f)?;
19+
write!(f, [DotDelimitedIdentifier::new(name)])?;
20+
21+
let comments = f.context().comments().clone();
22+
if comments.has_trailing(name) {
23+
write!(
24+
f,
25+
[
26+
trailing_comments(comments.trailing(name)),
27+
hard_line_break()
28+
]
29+
)?;
30+
} else if asname.is_some() {
31+
write!(f, [space()])?;
32+
}
33+
2034
if let Some(asname) = asname {
21-
let comments = f.context().comments().clone();
22-
let dangling = comments.dangling(item);
35+
write!(f, [token("as"),])?;
36+
37+
if comments.has_leading(asname) {
38+
write!(
39+
f,
40+
[
41+
trailing_comments(comments.leading(asname)),
42+
hard_line_break()
43+
]
44+
)?;
45+
} else {
46+
write!(f, [space()])?;
47+
}
48+
49+
write!(f, [asname.format()])?;
50+
}
51+
52+
if comments.has_dangling(item) {
2353
write!(
2454
f,
2555
[
26-
space(),
27-
token("as"),
28-
space(),
29-
asname.format(),
30-
trailing_comments(dangling),
56+
trailing_comments(comments.dangling(item)),
57+
hard_line_break()
3158
]
3259
)?;
3360
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
---
2+
source: crates/ruff_python_formatter/tests/fixtures.rs
3+
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/cases/import_comments.py
4+
---
5+
## Input
6+
7+
```python
8+
# ensure trailing comments are preserved
9+
import x # comment
10+
from x import a # comment
11+
from x import a, b # comment
12+
from x import a as b # comment
13+
from x import a as b, b as c # comment
14+
15+
# ensure intermixed end- and own-line comments are all preserved
16+
from x import ( # one
17+
# two
18+
a # three
19+
# four
20+
, # five
21+
# six
22+
) # seven
23+
24+
from x import ( # alpha
25+
# bravo
26+
a # charlie
27+
# delta
28+
as # echo
29+
# foxtrot
30+
b # golf
31+
# hotel
32+
, # india
33+
# juliet
34+
) # kilo
35+
```
36+
37+
## Black Differences
38+
39+
```diff
40+
--- Black
41+
+++ Ruff
42+
@@ -6,15 +6,22 @@
43+
from x import a as b, b as c # comment
44+
45+
# ensure intermixed end- and own-line comments are all preserved
46+
-# and at least kept in their original order, if not their original
47+
-# positions within the import statement
48+
+from x import ( # one
49+
+ # two
50+
+ a # three
51+
+ # four
52+
+ , # five
53+
+ # six
54+
+) # seven
55+
+
56+
from x import ( # alpha
57+
# bravo
58+
- a as b, # charlie
59+
+ a # charlie
60+
# delta
61+
- # echo
62+
- # foxtrot # golf
63+
+ as # echo
64+
+ # foxtrot
65+
+ b # golf
66+
# hotel
67+
- # india
68+
+ , # india
69+
# juliet
70+
) # kilo
71+
```
72+
73+
## Ruff Output
74+
75+
```python
76+
# ensure trailing comments are preserved
77+
import x # comment
78+
from x import a # comment
79+
from x import a, b # comment
80+
from x import a as b # comment
81+
from x import a as b, b as c # comment
82+
83+
# ensure intermixed end- and own-line comments are all preserved
84+
from x import ( # one
85+
# two
86+
a # three
87+
# four
88+
, # five
89+
# six
90+
) # seven
91+
92+
from x import ( # alpha
93+
# bravo
94+
a # charlie
95+
# delta
96+
as # echo
97+
# foxtrot
98+
b # golf
99+
# hotel
100+
, # india
101+
# juliet
102+
) # kilo
103+
```
104+
105+
## Black Output
106+
107+
```python
108+
# ensure trailing comments are preserved
109+
import x # comment
110+
from x import a # comment
111+
from x import a, b # comment
112+
from x import a as b # comment
113+
from x import a as b, b as c # comment
114+
115+
# ensure intermixed end- and own-line comments are all preserved
116+
# and at least kept in their original order, if not their original
117+
# positions within the import statement
118+
from x import ( # alpha
119+
# bravo
120+
a as b, # charlie
121+
# delta
122+
# echo
123+
# foxtrot # golf
124+
# hotel
125+
# india
126+
# juliet
127+
) # kilo
128+
```

0 commit comments

Comments
 (0)