Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@
f'{(abc:=10)}'

f"This is a really long string, but just make sure that you reflow fstrings {
2+2:d
}"
2+2:d}"
f"This is a really long string, but just make sure that you reflow fstrings correctly {2+2:d}"

f"{2+2=}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,23 +278,21 @@

# Combine conversion flags with format specifiers
x = f"{x = !s
:>0

}"
# This is interesting. There can be a comment after the format specifier but only if it's
# on it's own line. Refer to https://github.com/astral-sh/ruff/pull/7787 for more details.
# We'll format is as trailing comments.
x = f"{x !s
:>0
# comment 21
}"
:>0}"

x = f"{
x!s:>{
0
# comment 21-2
}}"

f"{1
# comment 21-3
:}"

f"{1 # comment 21-4
:} a"


x = f"""
{ # comment 22
Expand All @@ -311,14 +309,14 @@
"""

# Mix of various features.
f"{ # comment 26
f"""{ # comment 26
foo # after foo
:>{
x # after x
}
# comment 27
# comment 28
} woah {x}"
} woah {x}"""


f"""{foo
Expand All @@ -332,8 +330,7 @@
f"{
# comment 31
foo
:>
}"
:>}"

# Assignment statement

Expand Down Expand Up @@ -487,13 +484,11 @@

# This is not a multiline f-string even though it has a newline after the format specifier.
aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeee{
a:.3f
}moreeeeeeeeeeeeeeeeeetest" # comment
a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment

aaaaaaaaaaaaaaaaaa = (
f"testeeeeeeeeeeeeeeeeeeeeeeeee{
a:.3f
}moreeeeeeeeeeeeeeeeeetest" # comment
a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment
)

# The newline is only considered when it's a tripled-quoted f-string.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,23 +274,21 @@

# Combine conversion flags with format specifiers
x = t"{x = !s
:>0

}"
# This is interesting. There can be a comment after the format specifier but only if it's
# on it's own line. Refer to https://github.com/astral-sh/ruff/pull/7787 for more details.
# We'll format is as trailing comments.
x = t"{x !s
:>0
# comment 21
}"
:>0}"

x = f"{
x!s:>{
0
# comment 21-2
}}"

f"{1
# comment 21-3
:}"

f"{1 # comment 21-4
:} a"

x = t"""
{ # comment 22
x = :.0{y # comment 23
Expand All @@ -306,14 +304,14 @@
"""

# Mix of various features.
t"{ # comment 26
t"""{ # comment 26
foo # after foo
:>{
x # after x
}
# comment 27
# comment 28
} woah {x}"
} woah {x}"""

# Assignment statement

Expand Down Expand Up @@ -467,13 +465,11 @@

# This is not a multiline t-string even though it has a newline after the format specifier.
aaaaaaaaaaaaaaaaaa = t"testeeeeeeeeeeeeeeeeeeeeeeeee{
a:.3f
}moreeeeeeeeeeeeeeeeeetest" # comment
a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment

aaaaaaaaaaaaaaaaaa = (
t"testeeeeeeeeeeeeeeeeeeeeeeeee{
a:.3f
}moreeeeeeeeeeeeeeeeeetest" # comment
a:.3f}moreeeeeeeeeeeeeeeeeetest" # comment
)

# The newline is only considered when it's a tripled-quoted t-string.
Expand Down
33 changes: 12 additions & 21 deletions crates/ruff_python_formatter/src/comments/placement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,27 +323,18 @@ fn handle_enclosed_comment<'a>(
AnyNodeRef::TString(tstring) => CommentPlacement::dangling(tstring, comment),
AnyNodeRef::InterpolatedElement(element) => {
if let Some(preceding) = comment.preceding_node() {
if comment.line_position().is_own_line() && element.format_spec.is_some() {
return if comment.following_node().is_some() {
// Own line comment before format specifier
// ```py
// aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {
// aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd
// # comment
// :.3f} cccccccccc"""
// ```
CommentPlacement::trailing(preceding, comment)
} else {
// TODO: This can be removed once format specifiers with a newline are a syntax error.
// This is to handle cases like:
// ```py
// x = f"{x !s
// :>0
// # comment 21
// }"
// ```
CommentPlacement::trailing(element, comment)
};
// Own line comment before format specifier
// ```py
// aaaaaaaaaaa = f"""asaaaaaaaaaaaaaaaa {
// aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd
// # comment
// :.3f} cccccccccc"""
// ```
if comment.line_position().is_own_line()
&& element.format_spec.is_some()
&& comment.following_node().is_some()
{
return CommentPlacement::trailing(preceding, comment);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use ruff_python_ast::{
};
use ruff_text_size::{Ranged, TextSlice};

use crate::comments::{dangling_open_parenthesis_comments, trailing_comments};
use crate::comments::dangling_open_parenthesis_comments;
use crate::context::{
InterpolatedStringState, NodeLevel, WithInterpolatedStringState, WithNodeLevel,
};
Expand Down Expand Up @@ -203,7 +203,7 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
// # comment 27
// :test}"
// ```
if comments.has_trailing_own_line(expression) {
if comments.has_trailing(expression) {
soft_line_break().fmt(f)?;
}

Expand All @@ -214,31 +214,6 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
}
}

// These trailing comments can only occur if the format specifier is
// present. For example,
//
// ```python
// f"{
// x:.3f
// # comment
// }"
// ```

// This can also be triggered outside of a format spec, at
// least until https://github.com/astral-sh/ruff/issues/18632 is a syntax error
// TODO(https://github.com/astral-sh/ruff/issues/18632) Remove this
// and double check if it is still necessary for the triple quoted case
// once this is a syntax error.
// ```py
// f"{
// foo
// :{x}
// # comment 28
// } woah {x}"
// ```
// Any other trailing comments are attached to the expression itself.
trailing_comments(comments.trailing(self.element)).fmt(f)?;

if conversion.is_none() && format_spec.is_none() {
bracket_spacing.fmt(f)?;
}
Expand All @@ -258,15 +233,7 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
let mut f = WithNodeLevel::new(NodeLevel::ParenthesizedExpression, f);

if self.context.is_multiline() {
// TODO: The `or comments.has_trailing...` can be removed once newlines in format specs are a syntax error.
// This is to support the following case:
// ```py
// x = f"{x !s
// :>0
// # comment 21
// }"
// ```
if format_spec.is_none() || comments.has_trailing_own_line(self.element) {
if format_spec.is_none() {
group(&format_args![
open_parenthesis_comments,
soft_block_indent(&item)
Expand All @@ -276,6 +243,7 @@ impl Format<PyFormatContext<'_>> for FormatInterpolatedElement<'_> {
// For strings ending with a format spec, don't add a newline between the end of the format spec
// and closing curly brace because that is invalid syntax for single quoted strings and
// the newline is preserved as part of the format spec for triple quoted strings.

group(&format_args![
open_parenthesis_comments,
indent(&format_args![soft_line_break(), item])
Expand Down
7 changes: 6 additions & 1 deletion crates/ruff_python_formatter/tests/fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,12 @@ fn format_file(source: &str, options: &PyFormatOptions, input_path: &Path) -> St

(Cow::Owned(without_markers), content)
} else {
let printed = format_module_source(source, options.clone()).expect("Formatting to succeed");
let printed = format_module_source(source, options.clone()).unwrap_or_else(|err| {
panic!(
"Formatting `{input_path} was expected to succeed but it failed: {err}",
input_path = input_path.display()
)
});
let formatted_code = printed.into_code();

ensure_stability_when_formatting_twice(&formatted_code, options, input_path);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,7 @@ x = f"a{2+2:=^{foo(x+y**2):something else}one more}b"
f'{(abc:=10)}'

f"This is a really long string, but just make sure that you reflow fstrings {
2+2:d
}"
2+2:d}"
f"This is a really long string, but just make sure that you reflow fstrings correctly {2+2:d}"

f"{2+2=}"
Expand Down
Loading
Loading