Skip to content

Commit c18f1f5

Browse files
committed
More documentation, more test cases, more black
1 parent 2636902 commit c18f1f5

File tree

4 files changed

+139
-11
lines changed

4 files changed

+139
-11
lines changed

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,26 @@
55
from x import a as b # comment
66
from x import a as b, b as c # comment
77

8+
from x import (
9+
a, # comment
10+
)
11+
from x import (
12+
a, # comment
13+
b,
14+
)
15+
16+
# ensure comma is added
17+
from x import (
18+
a # comment
19+
)
20+
21+
# follow black style by merging cases without own-line comments
22+
from x import (
23+
a # alpha
24+
, # beta
25+
b,
26+
)
27+
828
# ensure intermixed comments are all preserved
929
from x import ( # one
1030
# two

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,25 @@ from x import a, b # comment
55
from x import a as b # comment
66
from x import a as b, b as c # comment
77

8+
from x import (
9+
a, # comment
10+
)
11+
from x import (
12+
a, # comment
13+
b,
14+
)
15+
16+
# ensure comma is added
17+
from x import (
18+
a, # comment
19+
)
20+
21+
# follow black style by merging cases without own-line comments
22+
from x import (
23+
a, # alpha # beta
24+
b,
25+
)
26+
827
# ensure intermixed comments are all preserved
928
from x import ( # one
1029
# two

crates/ruff_python_formatter/src/comments/placement.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,7 +1925,7 @@ fn handle_bracketed_end_of_line_comment<'a>(
19251925
CommentPlacement::Default(comment)
19261926
}
19271927

1928-
/// Attach an enclosed end-of-line comment to a [`ast::StmtImportFrom`].
1928+
/// Attach an enclosed comment to a [`ast::StmtImportFrom`].
19291929
///
19301930
/// For example, given:
19311931
/// ```python
@@ -1936,6 +1936,33 @@ fn handle_bracketed_end_of_line_comment<'a>(
19361936
///
19371937
/// The comment will be attached to the [`ast::StmtImportFrom`] node as a dangling comment, to
19381938
/// ensure that it remains on the same line as the [`ast::StmtImportFrom`] itself.
1939+
///
1940+
/// If the comment's preceding node is an alias, and the comment is *before* a comma:
1941+
/// ```python
1942+
/// from foo import (
1943+
/// bar as baz # comment
1944+
/// ,
1945+
/// )
1946+
/// ```
1947+
///
1948+
/// The comment will then be attached to the [`ast::Alias`] node as a dangling comment instead,
1949+
/// to ensure that it retains its position before the comma.
1950+
///
1951+
/// Otherwise, if the comment is *after* the comma or before a following alias:
1952+
/// ```python
1953+
/// from foo import (
1954+
/// bar as baz, # comment
1955+
/// )
1956+
///
1957+
/// from foo import (
1958+
/// bar,
1959+
/// # comment
1960+
/// baz,
1961+
/// )
1962+
/// ```
1963+
///
1964+
/// Then it will retain the default behavior of being attached to the relevant [`ast::Alias`] node
1965+
/// as either a leading or trailing comment.
19391966
fn handle_import_from_comment<'a>(
19401967
comment: DecoratedComment<'a>,
19411968
import_from: &'a ast::StmtImportFrom,
@@ -1974,7 +2001,7 @@ fn handle_import_from_comment<'a>(
19742001
.skip_trivia()
19752002
.next()
19762003
{
1977-
// treat comments before the comma as dangling, after as trailing (default)
2004+
// Treat comments before the comma as dangling, after as trailing (default)
19782005
if let Some(AnyNodeRef::Alias(alias)) = comment.preceding_node() {
19792006
CommentPlacement::dangling(alias, comment)
19802007
} else {
@@ -1986,6 +2013,27 @@ fn handle_import_from_comment<'a>(
19862013
}
19872014
}
19882015

2016+
/// Attach an enclosed comment to the appropriate [`ast::Identifier`] within an [`ast::Alias`].
2017+
///
2018+
/// For example:
2019+
/// ```python
2020+
/// from foo import (
2021+
/// bar # comment
2022+
/// as baz,
2023+
/// )
2024+
/// ```
2025+
///
2026+
/// Will attach the comment as a trailing comment on the first name [`ast::Identifier`].
2027+
///
2028+
/// Wheras:
2029+
/// ```python
2030+
/// from foo import (
2031+
/// bar as # comment
2032+
/// baz,
2033+
/// )
2034+
/// ```
2035+
///
2036+
/// Will attach the comment as a leading comment on the second name [`ast::Identifier`].
19892037
fn handle_alias_comment<'a>(
19902038
comment: DecoratedComment<'a>,
19912039
alias: &'a ruff_python_ast::Alias,

crates/ruff_python_formatter/src/other/alias.rs

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ impl FormatNodeRule<Alias> for FormatAlias {
1919
write!(f, [DotDelimitedIdentifier::new(name)])?;
2020

2121
let comments = f.context().comments().clone();
22+
23+
// ```python
24+
// from foo import (
25+
// bar # comment
26+
// as baz,
27+
// )
28+
// ```
2229
if comments.has_trailing(name) {
2330
write!(
2431
f,
@@ -32,8 +39,14 @@ impl FormatNodeRule<Alias> for FormatAlias {
3239
}
3340

3441
if let Some(asname) = asname {
35-
write!(f, [token("as"),])?;
42+
write!(f, [token("as")])?;
3643

44+
// ```python
45+
// from foo import (
46+
// bar as # comment
47+
// baz,
48+
// )
49+
// ```
3750
if comments.has_leading(asname) {
3851
write!(
3952
f,
@@ -49,15 +62,43 @@ impl FormatNodeRule<Alias> for FormatAlias {
4962
write!(f, [asname.format()])?;
5063
}
5164

52-
if comments.has_dangling(item) {
53-
write!(
54-
f,
55-
[
56-
trailing_comments(comments.dangling(item)),
57-
hard_line_break()
58-
]
59-
)?;
65+
// Dangling comment between alias and comma on a following line
66+
// ```python
67+
// from foo import (
68+
// bar # comment
69+
// ,
70+
// )
71+
// ```
72+
let dangling = comments.dangling(item);
73+
if !dangling.is_empty() {
74+
write!(f, [trailing_comments(comments.dangling(item))])?;
75+
76+
// Black will move the comma and merge comments if there is no own-line comment between
77+
// the alias and the comma.
78+
//
79+
// Eg:
80+
// ```python
81+
// from foo import (
82+
// bar # one
83+
// , # two
84+
// )
85+
// ```
86+
//
87+
// Will become:
88+
// ```python
89+
// from foo import (
90+
// bar, # one # two)
91+
// ```
92+
//
93+
// Only force a hard line break if an own-line dangling comment is present.
94+
if dangling
95+
.iter()
96+
.any(|comment| comment.line_position().is_own_line())
97+
{
98+
write!(f, [hard_line_break()])?;
99+
}
60100
}
101+
61102
Ok(())
62103
}
63104
}

0 commit comments

Comments
 (0)