mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-22 03:15:44 +00:00
Avoid multiline expression if format specifier is present (#11123)
## Summary This PR fixes the bug where the formatter would format an f-string and could potentially change the AST. For a triple-quoted f-string, the element can't be formatted into multiline if it has a format specifier because otherwise the newline would be treated as part of the format specifier. Given the following f-string: ```python f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc { variable:.3f} ddddddddddddddd eeeeeeee""" ``` The formatter sees that the f-string is already multiline so it assumes that it can contain line breaks i.e., broken into multiple lines. But, in this specific case we can't format it as: ```python f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc { variable:.3f } ddddddddddddddd eeeeeeee""" ``` Because the format specifier string would become ".3f\n", which is not the original string (`.3f`). If the original source code already contained a newline, they'll be preserved. For example: ```python f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc { variable:.3f } ddddddddddddddd eeeeeeee""" ``` The above will be formatted as: ```py f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable:.3f } ddddddddddddddd eeeeeeee""" ``` Note that the newline after `.3f` is part of the format specifier which needs to be preserved. The Python version is irrelevant in this case. fixes: #10040 ## Test Plan Add some test cases to verify this behavior.
This commit is contained in:
parent
16a1f3cbcc
commit
77a72ecd38
7 changed files with 220 additions and 31 deletions
|
@ -1,5 +1,5 @@
|
|||
use crate::comments::Comments;
|
||||
use crate::other::f_string::FStringContext;
|
||||
use crate::other::f_string_element::FStringExpressionElementContext;
|
||||
use crate::PyFormatOptions;
|
||||
use ruff_formatter::{Buffer, FormatContext, GroupId, IndentWidth, SourceCode};
|
||||
use ruff_python_ast::str::Quote;
|
||||
|
@ -128,13 +128,13 @@ impl Debug for PyFormatContext<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub(crate) enum FStringState {
|
||||
/// The formatter is inside an f-string expression element i.e., between the
|
||||
/// curly brace in `f"foo {x}"`.
|
||||
///
|
||||
/// The containing `FStringContext` is the surrounding f-string context.
|
||||
InsideExpressionElement(FStringContext),
|
||||
InsideExpressionElement(FStringExpressionElementContext),
|
||||
/// The formatter is outside an f-string.
|
||||
#[default]
|
||||
Outside,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue