mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-27 12:29:28 +00:00
Call chain formatting in fluent style (#6151)
Implement fluent style/call chains. See the `call_chains.py` formatting for examples. This isn't fully like black because in `raise A from B` they allow `A` breaking can influence the formatting of `B` even if it is already multiline. Similarity index: | project | main | PR | |--------------|-------|-------| | build | ??? | 0.753 | | django | 0.991 | 0.998 | | transformers | 0.993 | 0.994 | | typeshed | 0.723 | 0.723 | | warehouse | 0.978 | 0.994 | | zulip | 0.992 | 0.994 | Call chain formatting is affected by https://github.com/astral-sh/ruff/issues/627, but i'm cutting scope here. Closes #5343 **Test Plan**: * Added a dedicated call chains test file * The ecosystem checks found some bugs * I manually check django and zulip formatting --------- Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
parent
35bdbe43a8
commit
99baad12d8
16 changed files with 917 additions and 517 deletions
|
@ -1,18 +1,29 @@
|
|||
use ruff_python_ast::{Expr, ExprSubscript};
|
||||
|
||||
use ruff_formatter::{format_args, write};
|
||||
use ruff_formatter::{format_args, write, FormatRuleWithOptions};
|
||||
use ruff_python_ast::node::{AnyNodeRef, AstNode};
|
||||
use ruff_python_ast::{Expr, ExprSubscript};
|
||||
|
||||
use crate::comments::trailing_comments;
|
||||
use crate::context::PyFormatContext;
|
||||
use crate::context::{NodeLevel, WithNodeLevel};
|
||||
use crate::expression::expr_tuple::TupleParentheses;
|
||||
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
|
||||
use crate::expression::CallChainLayout;
|
||||
use crate::prelude::*;
|
||||
use crate::FormatNodeRule;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct FormatExprSubscript;
|
||||
pub struct FormatExprSubscript {
|
||||
call_chain_layout: CallChainLayout,
|
||||
}
|
||||
|
||||
impl FormatRuleWithOptions<ExprSubscript, PyFormatContext<'_>> for FormatExprSubscript {
|
||||
type Options = CallChainLayout;
|
||||
|
||||
fn with_options(mut self, options: Self::Options) -> Self {
|
||||
self.call_chain_layout = options;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FormatNodeRule<ExprSubscript> for FormatExprSubscript {
|
||||
fn fmt_fields(&self, item: &ExprSubscript, f: &mut PyFormatter) -> FormatResult<()> {
|
||||
|
@ -23,6 +34,8 @@ impl FormatNodeRule<ExprSubscript> for FormatExprSubscript {
|
|||
ctx: _,
|
||||
} = item;
|
||||
|
||||
let call_chain_layout = self.call_chain_layout.apply_in_node(item, f);
|
||||
|
||||
let comments = f.context().comments().clone();
|
||||
let dangling_comments = comments.dangling_comments(item.as_any_node_ref());
|
||||
debug_assert!(
|
||||
|
@ -30,12 +43,19 @@ impl FormatNodeRule<ExprSubscript> for FormatExprSubscript {
|
|||
"A subscript expression can only have a single dangling comment, the one after the bracket"
|
||||
);
|
||||
|
||||
let format_value = format_with(|f| match value.as_ref() {
|
||||
Expr::Attribute(expr) => expr.format().with_options(call_chain_layout).fmt(f),
|
||||
Expr::Call(expr) => expr.format().with_options(call_chain_layout).fmt(f),
|
||||
Expr::Subscript(expr) => expr.format().with_options(call_chain_layout).fmt(f),
|
||||
_ => value.format().fmt(f),
|
||||
});
|
||||
|
||||
if let NodeLevel::Expression(Some(_)) = f.context().node_level() {
|
||||
// Enforce the optional parentheses for parenthesized values.
|
||||
let mut f = WithNodeLevel::new(NodeLevel::Expression(None), f);
|
||||
write!(f, [value.format()])?;
|
||||
write!(f, [format_value])?;
|
||||
} else {
|
||||
value.format().fmt(f)?;
|
||||
format_value.fmt(f)?;
|
||||
}
|
||||
|
||||
let format_slice = format_with(|f: &mut PyFormatter| {
|
||||
|
@ -73,10 +93,16 @@ impl NeedsParentheses for ExprSubscript {
|
|||
fn needs_parentheses(
|
||||
&self,
|
||||
_parent: AnyNodeRef,
|
||||
_context: &PyFormatContext,
|
||||
context: &PyFormatContext,
|
||||
) -> OptionalParentheses {
|
||||
{
|
||||
OptionalParentheses::Never
|
||||
if CallChainLayout::from_expression(self.into(), context.source())
|
||||
== CallChainLayout::Fluent
|
||||
{
|
||||
OptionalParentheses::Multiline
|
||||
} else {
|
||||
self.value.needs_parentheses(self.into(), context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue