Skip to content

Commit

Permalink
Add FString support to binary like formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser committed Sep 8, 2023
1 parent c260762 commit 14c775c
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ def test():
+ x
)

(
b + c + d +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
f"bbbbbb{z}bbbbbbbbbbbbbbbbbbbbbbb"
"cccccccccccccccccccccccccc"
% aaaaaaaaaaaa
+ x
)

(
b < c > d <
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ def f():
in caplog.messages
)

(
b < c > d <
f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
"cccccccccccccccccccccccccc"
% aaaaaaaaaaaa
> x
)

c = (a >
# test leading binary comment
"a" "b" * b
Expand Down
61 changes: 23 additions & 38 deletions crates/ruff_python_formatter/src/expression/binary_like.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,16 @@ use smallvec::SmallVec;

use ruff_formatter::write;
use ruff_python_ast::{
BytesConstant, Constant, Expr, ExprAttribute, ExprBinOp, ExprCompare, ExprConstant,
ExprUnaryOp, StringConstant, UnaryOp,
Constant, Expr, ExprAttribute, ExprBinOp, ExprCompare, ExprConstant, ExprUnaryOp, UnaryOp,
};

use crate::comments::{leading_comments, trailing_comments, Comments, SourceComment};
use crate::expression::expr_constant::ExprConstantLayout;
use crate::expression::parentheses::{
in_parentheses_only_group, in_parentheses_only_soft_line_break,
in_parentheses_only_soft_line_break_or_space, is_expression_parenthesized,
write_in_parentheses_only_group_end_tag, write_in_parentheses_only_group_start_tag,
};
use crate::expression::string::StringLayout;
use crate::expression::string::{AnyString, FormatString, StringLayout};
use crate::expression::OperatorPrecedence;
use crate::prelude::*;

Expand Down Expand Up @@ -197,29 +195,12 @@ impl Format<PyFormatContext<'_>> for BinaryLike<'_> {
let mut string_operands = flat_binary
.operands()
.filter_map(|(index, operand)| {
if let Expr::Constant(
constant @ ExprConstant {
value:
Constant::Str(StringConstant {
implicit_concatenated: true,
..
})
| Constant::Bytes(BytesConstant {
implicit_concatenated: true,
..
}),
..
},
) = operand.expression()
{
if is_expression_parenthesized(constant.into(), source) {
None
} else {
Some((index, constant, operand))
}
} else {
None
}
AnyString::from_expression(operand.expression())
.filter(|string| {
string.is_implicit_concatenated()
&& !is_expression_parenthesized(string.into(), source)
})
.map(|string| (index, string, operand))
})
.peekable();

Expand Down Expand Up @@ -296,11 +277,11 @@ impl Format<PyFormatContext<'_>> for BinaryLike<'_> {
f,
[
operand.leading_binary_comments().map(leading_comments),
string_constant
.format()
.with_options(ExprConstantLayout::String(
StringLayout::ImplicitConcatenatedStringInBinaryLike,
)),
leading_comments(comments.leading(&string_constant)),
FormatString::new(&string_constant).with_layout(
StringLayout::ImplicitConcatenatedStringInBinaryLike,
),
trailing_comments(comments.trailing(&string_constant)),
operand.trailing_binary_comments().map(trailing_comments),
line_suffix_boundary(),
]
Expand All @@ -311,12 +292,16 @@ impl Format<PyFormatContext<'_>> for BinaryLike<'_> {
// "a" "b" + c
// ^^^^^^^-- format the first operand of a binary expression
// ```
string_constant
.format()
.with_options(ExprConstantLayout::String(
StringLayout::ImplicitConcatenatedStringInBinaryLike,
))
.fmt(f)?;
write!(
f,
[
leading_comments(comments.leading(&string_constant)),
FormatString::new(&string_constant).with_layout(
StringLayout::ImplicitConcatenatedStringInBinaryLike
),
trailing_comments(comments.trailing(&string_constant)),
]
)?;
}

// Write the right operator and start the group for the right side (if any)
Expand Down
34 changes: 29 additions & 5 deletions crates/ruff_python_formatter/src/expression/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use bitflags::bitflags;

use ruff_formatter::{format_args, write, FormatError, FormatOptions, TabWidth};
use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::{self as ast, ExprConstant, ExprFString};
use ruff_python_ast::{self as ast, Constant, ExprConstant, ExprFString, ExpressionRef};
use ruff_python_parser::lexer::{lex_starts_at, LexicalError, LexicalErrorType};
use ruff_python_parser::{Mode, Tok};
use ruff_source_file::Locator;
Expand All @@ -24,12 +24,26 @@ enum Quoting {
Preserve,
}

#[derive(Clone, Debug)]
pub(super) enum AnyString<'a> {
Constant(&'a ExprConstant),
FString(&'a ExprFString),
}

impl<'a> AnyString<'a> {
pub(crate) fn from_expression(expression: &'a Expr) -> Option<AnyString<'a>> {
match expression {
Expr::Constant(
constant @ ExprConstant {
value: Constant::Str(_) | Constant::Bytes(_),
..
},
) => Some(AnyString::Constant(constant)),
Expr::FString(fstring) => Some(AnyString::FString(fstring)),
_ => None,
}
}

fn quoting(&self, locator: &Locator) -> Quoting {
match self {
Self::Constant(_) => Quoting::CanChange,
Expand All @@ -50,7 +64,7 @@ impl<'a> AnyString<'a> {
}

/// Returns `true` if the string is implicitly concatenated.
fn implicit_concatenated(&self) -> bool {
pub(super) fn is_implicit_concatenated(&self) -> bool {
match self {
Self::Constant(ExprConstant { value, .. }) => value.is_implicit_concatenated(),
Self::FString(ExprFString {
Expand Down Expand Up @@ -79,6 +93,15 @@ impl<'a> From<&AnyString<'a>> for AnyNodeRef<'a> {
}
}

impl<'a> From<&AnyString<'a>> for ExpressionRef<'a> {
fn from(value: &AnyString<'a>) -> Self {
match value {
AnyString::Constant(expr) => ExpressionRef::Constant(expr),
AnyString::FString(expr) => ExpressionRef::FString(expr),
}
}
}

pub(super) struct FormatString<'a> {
string: &'a AnyString<'a>,
layout: StringLayout,
Expand All @@ -97,7 +120,7 @@ pub enum StringLayout {
}

impl<'a> FormatString<'a> {
pub(super) fn new(string: &'a AnyString) -> Self {
pub(super) fn new(string: &'a AnyString<'a>) -> Self {
if let AnyString::Constant(constant) = string {
debug_assert!(constant.value.is_str() || constant.value.is_bytes());
}
Expand All @@ -117,7 +140,7 @@ impl<'a> Format<PyFormatContext<'_>> for FormatString<'a> {
fn fmt(&self, f: &mut PyFormatter) -> FormatResult<()> {
match self.layout {
StringLayout::Default => {
if self.string.implicit_concatenated() {
if self.string.is_implicit_concatenated() {
in_parentheses_only_group(&FormatStringContinuation::new(self.string)).fmt(f)
} else {
FormatStringPart::new(
Expand Down Expand Up @@ -972,9 +995,10 @@ fn format_docstring_line(

#[cfg(test)]
mod tests {
use crate::expression::string::count_indentation_like_black;
use ruff_formatter::TabWidth;

use crate::expression::string::count_indentation_like_black;

#[test]
fn test_indentation_like_black() {
let tab_width = TabWidth::try_from(8).unwrap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ self._assert_skipping(
+ x
)
(
b + c + d +
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
f"bbbbbb{z}bbbbbbbbbbbbbbbbbbbbbbb"
"cccccccccccccccccccccccccc"
% aaaaaaaaaaaa
+ x
)
(
b < c > d <
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
Expand Down Expand Up @@ -266,6 +275,12 @@ self._assert_skipping(
"cccccccccccccccccccccccccc" % aaaaaaaaaaaa + x
)
(
b + c + d + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
f"bbbbbb{z}bbbbbbbbbbbbbbbbbbbbbbb"
"cccccccccccccccccccccccccc" % aaaaaaaaaaaa + x
)
(
b < c > d < "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@ assert (
in caplog.messages
)
(
b < c > d <
f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
"cccccccccccccccccccccccccc"
% aaaaaaaaaaaa
> x
)
c = (a >
# test leading binary comment
"a" "b" * b
Expand Down Expand Up @@ -356,6 +365,12 @@ assert (
"be silently ignored by the index" in caplog.messages
)
(
b < c > d < f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
"cccccccccccccccccccccccccc" % aaaaaaaaaaaa > x
)
c = (
a >
# test leading binary comment
Expand Down

0 comments on commit 14c775c

Please sign in to comment.