Hug multiline-strings preview style (#9243)

This commit is contained in:
Micha Reiser 2024-01-10 12:47:34 +01:00 committed by GitHub
parent 6be73322da
commit ac02d3aedd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 590 additions and 215 deletions

View file

@ -17,11 +17,14 @@ use crate::context::{NodeLevel, WithNodeLevel};
use crate::expression::expr_generator_exp::is_generator_parenthesized;
use crate::expression::expr_tuple::is_tuple_parenthesized;
use crate::expression::parentheses::{
is_expression_parenthesized, optional_parentheses, parenthesized, NeedsParentheses,
OptionalParentheses, Parentheses, Parenthesize,
is_expression_parenthesized, optional_parentheses, parenthesized, HuggingStyle,
NeedsParentheses, OptionalParentheses, Parentheses, Parenthesize,
};
use crate::prelude::*;
use crate::preview::is_hug_parens_with_braces_and_square_brackets_enabled;
use crate::preview::{
is_hug_parens_with_braces_and_square_brackets_enabled, is_multiline_string_handling_enabled,
};
use crate::string::AnyString;
mod binary_like;
pub(crate) mod expr_attribute;
@ -126,7 +129,7 @@ impl FormatRule<Expr, PyFormatContext<'_>> for FormatExpr {
let node_comments = comments.leading_dangling_trailing(expression);
if !node_comments.has_leading() && !node_comments.has_trailing() {
parenthesized("(", &format_expr, ")")
.with_indent(!is_expression_huggable(expression, f.context()))
.with_hugging(is_expression_huggable(expression, f.context()))
.fmt(f)
} else {
format_with_parentheses_comments(expression, &node_comments, f)
@ -444,7 +447,7 @@ impl Format<PyFormatContext<'_>> for MaybeParenthesizeExpression<'_> {
OptionalParentheses::Never => match parenthesize {
Parenthesize::IfBreaksOrIfRequired => {
parenthesize_if_expands(&expression.format().with_options(Parentheses::Never))
.with_indent(!is_expression_huggable(expression, f.context()))
.with_indent(is_expression_huggable(expression, f.context()).is_none())
.fmt(f)
}
@ -1084,7 +1087,7 @@ pub(crate) fn has_own_parentheses(
}
/// Returns `true` if the expression can hug directly to enclosing parentheses, as in Black's
/// `hug_parens_with_braces_and_square_brackets` preview style behavior.
/// `hug_parens_with_braces_and_square_brackets` or `multiline_string_handling` preview styles behavior.
///
/// For example, in preview style, given:
/// ```python
@ -1110,11 +1113,10 @@ pub(crate) fn has_own_parentheses(
/// ]
/// )
/// ```
pub(crate) fn is_expression_huggable(expr: &Expr, context: &PyFormatContext) -> bool {
if !is_hug_parens_with_braces_and_square_brackets_enabled(context) {
return false;
}
pub(crate) fn is_expression_huggable(
expr: &Expr,
context: &PyFormatContext,
) -> Option<HuggingStyle> {
match expr {
Expr::Tuple(_)
| Expr::List(_)
@ -1122,18 +1124,14 @@ pub(crate) fn is_expression_huggable(expr: &Expr, context: &PyFormatContext) ->
| Expr::Dict(_)
| Expr::ListComp(_)
| Expr::SetComp(_)
| Expr::DictComp(_) => true,
| Expr::DictComp(_) => is_hug_parens_with_braces_and_square_brackets_enabled(context)
.then_some(HuggingStyle::Always),
Expr::Starred(ast::ExprStarred { value, .. }) => matches!(
value.as_ref(),
Expr::Tuple(_)
| Expr::List(_)
| Expr::Set(_)
| Expr::Dict(_)
| Expr::ListComp(_)
| Expr::SetComp(_)
| Expr::DictComp(_)
),
Expr::Starred(ast::ExprStarred { value, .. }) => is_expression_huggable(value, context),
Expr::StringLiteral(string) => is_huggable_string(AnyString::String(string), context),
Expr::BytesLiteral(bytes) => is_huggable_string(AnyString::Bytes(bytes), context),
Expr::FString(fstring) => is_huggable_string(AnyString::FString(fstring), context),
Expr::BoolOp(_)
| Expr::NamedExpr(_)
@ -1147,18 +1145,28 @@ pub(crate) fn is_expression_huggable(expr: &Expr, context: &PyFormatContext) ->
| Expr::YieldFrom(_)
| Expr::Compare(_)
| Expr::Call(_)
| Expr::FString(_)
| Expr::Attribute(_)
| Expr::Subscript(_)
| Expr::Name(_)
| Expr::Slice(_)
| Expr::IpyEscapeCommand(_)
| Expr::StringLiteral(_)
| Expr::BytesLiteral(_)
| Expr::NumberLiteral(_)
| Expr::BooleanLiteral(_)
| Expr::NoneLiteral(_)
| Expr::EllipsisLiteral(_) => false,
| Expr::EllipsisLiteral(_) => None,
}
}
/// Returns `true` if `string` is a multiline string that is not implicitly concatenated.
fn is_huggable_string(string: AnyString, context: &PyFormatContext) -> Option<HuggingStyle> {
if !is_multiline_string_handling_enabled(context) {
return None;
}
if !string.is_implicit_concatenated() && string.is_multiline(context.source()) {
Some(HuggingStyle::IfFirstLineFits)
} else {
None
}
}