Skip to content
Closed
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
8 changes: 8 additions & 0 deletions crates/ruff_python_ast/src/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2313,6 +2313,14 @@ impl Parameters {
&& self.vararg.is_none()
&& self.kwarg.is_none()
}

pub fn len(&self) -> usize {
self.posonlyargs.len()
+ self.args.len()
+ usize::from(self.vararg.is_some())
+ self.kwonlyargs.len()
+ usize::from(self.kwarg.is_some())
}
}

/// An alternative type of AST `arg`. This is used for each function argument that might have a default value.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"preview": "disabled"
},
{
"preview": "enabled"
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ def f(
*x: x
)

(
lambda
# comment
*x,
**y: x
)

(
lambda
# comment 1
Expand All @@ -135,13 +142,32 @@ def f(
x
)

(
lambda
# comment 1
*
# comment 2
x,
**y:
# comment 3
x
)

(
lambda # comment 1
* # comment 2
x: # comment 3
x
)

(
lambda # comment 1
* # comment 2
x,
y: # comment 3
x
)

lambda *x\
:x

Expand Down Expand Up @@ -196,10 +222,111 @@ def f(
x
)

(
lambda # 1
# 2
x, # 3
# 4
y
: # 5
# 6
x
)

(
lambda
x,
# comment
y:
z
)


# Leading
lambda x: (
lambda y: lambda z: x
+ y
+ y
+ y
+ y
+ y
+ y
+ y
+ y
+ y
+ y
+ y
+ y
+ y
+ y
+ y
+ y
+ y
+ y
+ y
+ y
+ y
+ z # Trailing
) # Trailing


# Leading
lambda x: lambda y: lambda z: [
x,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
y,
z
] # Trailing
# Trailing

lambda self, araa, kkkwargs=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(*args, **kwargs), e=1, f=2, g=2: d

# Regression tests for https://github.com/astral-sh/ruff/issues/8179
def a():
return b(
c,
d,
e,
f=lambda self, *args, **kwargs: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(
*args, **kwargs
),
)

def a():
return b(
c,
d,
e,
f=lambda self, araa, kkkwargs,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
args,kwargs,
e=1, f=2, g=2: d,
g = 10
)

8 changes: 8 additions & 0 deletions crates/ruff_python_formatter/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ impl<'a> PyFormatContext<'a> {
pub(crate) fn comments(&self) -> &Comments<'a> {
&self.comments
}

pub(crate) const fn is_preview(&self) -> bool {
self.options.preview().is_enabled()
}

pub(crate) const fn is_stable(&self) -> bool {
!self.is_preview()
}
}

impl FormatContext for PyFormatContext<'_> {
Expand Down
62 changes: 43 additions & 19 deletions crates/ruff_python_formatter/src/expression/expr_lambda.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use ruff_formatter::write;
use ruff_formatter::{format_args, write};
use ruff_python_ast::AnyNodeRef;
use ruff_python_ast::ExprLambda;
use ruff_text_size::Ranged;

use crate::comments::{dangling_comments, SourceComment};
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
use crate::comments::{dangling_comments, leading_comments, SourceComment};
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses, Parenthesize};
use crate::expression::{has_own_parentheses, maybe_parenthesize_expression};
use crate::other::parameters::ParametersParentheses;
use crate::prelude::*;

Expand All @@ -25,31 +26,49 @@ impl FormatNodeRule<ExprLambda> for FormatExprLambda {
write!(f, [token("lambda")])?;

if let Some(parameters) = parameters {
// In this context, a dangling comment can either be a comment between the `lambda` the
// In this context, a dangling comment can either be a comment between the `lambda` and the
// parameters, or a comment between the parameters and the body.
let (dangling_before_parameters, dangling_after_parameters) = dangling
.split_at(dangling.partition_point(|comment| comment.end() < parameters.start()));

if dangling_before_parameters.is_empty() {
write!(f, [space()])?;
} else {
write!(f, [dangling_comments(dangling_before_parameters)])?;
}

write!(
f,
[parameters
.format()
.with_options(ParametersParentheses::Never)]
)?;
group(&format_with(|f: &mut PyFormatter| {
if f.context().node_level().is_parenthesized()
&& (parameters.len() > 1 || !dangling_before_parameters.is_empty())
{
let end_of_line_start = dangling_before_parameters
.partition_point(|comment| comment.line_position().is_end_of_line());
let (same_line_comments, own_line_comments) =
dangling_before_parameters.split_at(end_of_line_start);

write!(f, [token(":")])?;
dangling_comments(same_line_comments).fmt(f)?;

if dangling_after_parameters.is_empty() {
write!(f, [space()])?;
} else {
write!(f, [dangling_comments(dangling_after_parameters)])?;
}
soft_block_indent(&format_args![
leading_comments(own_line_comments),
parameters
.format()
.with_options(ParametersParentheses::Never),
])
.fmt(f)
} else {
parameters
.format()
.with_options(ParametersParentheses::Never)
.fmt(f)
}?;

token(":").fmt(f)?;

if dangling_after_parameters.is_empty() {
space().fmt(f)
} else {
dangling_comments(dangling_after_parameters).fmt(f)
}
}))
.fmt(f)?;
} else {
write!(f, [token(":")])?;

Expand All @@ -61,7 +80,12 @@ impl FormatNodeRule<ExprLambda> for FormatExprLambda {
}
}

write!(f, [body.format()])
// Avoid parenthesizing lists, dictionaries, etc.
if f.context().is_stable() || has_own_parentheses(body, f.context()).is_some() {
body.format().fmt(f)
} else {
maybe_parenthesize_expression(body, item, Parenthesize::IfBreaksOrIfRequired).fmt(f)
}
}

fn fmt_dangling_comments(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ impl NeedsParentheses for ExprNamedExpr {
|| parent.is_stmt_delete()
|| parent.is_stmt_for()
|| parent.is_stmt_function_def()
|| parent.is_expr_lambda()
{
OptionalParentheses::Always
} else {
Expand Down
2 changes: 1 addition & 1 deletion crates/ruff_python_formatter/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl PyFormatOptions {
self.line_ending
}

pub fn preview(&self) -> PreviewMode {
pub const fn preview(&self) -> PreviewMode {
self.preview
}

Expand Down
22 changes: 13 additions & 9 deletions crates/ruff_python_formatter/src/other/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,15 @@ impl FormatNodeRule<Parameters> for FormatParameters {
dangling.split_at(parenthesis_comments_end);

let format_inner = format_with(|f: &mut PyFormatter| {
let separator = format_with(|f| write!(f, [token(","), soft_line_break_or_space()]));
let separator = format_with(|f: &mut PyFormatter| {
token(",").fmt(f)?;

if f.context().node_level().is_parenthesized() {
soft_line_break_or_space().fmt(f)
} else {
space().fmt(f)
}
});
let mut joiner = f.join_with(separator);
let mut last_node: Option<AnyNodeRef> = None;

Expand Down Expand Up @@ -232,23 +240,19 @@ impl FormatNodeRule<Parameters> for FormatParameters {
Ok(())
});

let mut f = WithNodeLevel::new(NodeLevel::ParenthesizedExpression, f);

let num_parameters = posonlyargs.len()
+ args.len()
+ usize::from(vararg.is_some())
+ kwonlyargs.len()
+ usize::from(kwarg.is_some());
let num_parameters = item.len();

if self.parentheses == ParametersParentheses::Never {
write!(f, [group(&format_inner), dangling_comments(dangling)])
write!(f, [format_inner, dangling_comments(dangling)])
} else if num_parameters == 0 {
let mut f = WithNodeLevel::new(NodeLevel::ParenthesizedExpression, f);
// No parameters, format any dangling comments between `()`
write!(f, [empty_parenthesized("(", dangling, ")")])
} else {
// Intentionally avoid `parenthesized`, which groups the entire formatted contents.
// We want parameters to be grouped alongside return types, one level up, so we
// format them "inline" here.
let mut f = WithNodeLevel::new(NodeLevel::ParenthesizedExpression, f);
write!(
f,
[
Expand Down
Loading