ruff/docs
Dylan 4e1cf5747a
Fluent formatting of method chains (#21369)
This PR implements a modification (in preview) to fluent formatting for
method chains: We break _at_ the first call instead of _after_.

For example, we have the following diff between `main` and this PR (with
`line-length=8` so I don't have to stretch out the text):

```diff
 x = (
-    df.merge()
+    df
+    .merge()
     .groupby()
     .agg()
     .filter()
 )
```

## Explanation of current implementation

Recall that we traverse the AST to apply formatting. A method chain,
while read left-to-right, is stored in the AST "in reverse". So if we
start with something like

```python
a.b.c.d().e.f()
```

then the first syntax node we meet is essentially `.f()`. So we have to
peek ahead. And we actually _already_ do this in our current fluent
formatting logic: we peek ahead to count how many calls we have in the
chain to see whether we should be using fluent formatting or now.

In this implementation, we actually _record_ this number inside the enum
for `CallChainLayout`. That is, we make the variant `Fluent` hold an
`AttributeState`. This state can either be:

- The number of call-like attributes preceding the current attribute
- The state `FirstCallOrSubscript` which means we are at the first
call-like attribute in the chain (reading from left to right)
- The state `BeforeFirstCallOrSubscript` which means we are in the
"first group" of attributes, preceding that first call.

In our example, here's what it looks like at each attribute:

```
a.b.c.d().e.f @ Fluent(CallsOrSubscriptsPreceding(1))
a.b.c.d().e @ Fluent(CallsOrSubscriptsPreceding(1))
a.b.c.d @ Fluent(FirstCallOrSubscript)
a.b.c @ Fluent(BeforeFirstCallOrSubscript)
a.b @ Fluent(BeforeFirstCallOrSubscript)
```

Now, as we descend down from the parent expression, we pass along this
little piece of state and modify it as we go to track where we are. This
state doesn't do anything except when we are in `FirstCallOrSubscript`,
in which case we add a soft line break.

Closes #8598

---------

Co-authored-by: Brent Westbrook <36778786+ntBre@users.noreply.github.com>
2025-12-15 09:29:50 -06:00
..
.overrides Use | for page separator in meta titles (#13221) 2024-09-03 00:46:15 +00:00
assets Point docs to Astral favicon (#13219) 2024-09-02 20:11:39 -04:00
editors Limit `eglot-format` hook to eglot-managed Python buffers (#21459) 2025-11-17 13:52:31 +00:00
formatter Document known lambda formatting deviations from Black (#21954) 2025-12-12 12:57:09 -05:00
js Improvements to documentation (#12712) 2024-08-12 07:17:32 +00:00
stylesheets Fix copy and line separator colors in dark mode (#19630) 2025-07-30 15:08:31 +01:00
.gitignore Add docs for Ruff language server (#12344) 2024-07-18 17:41:43 +05:30
configuration.md Document `*.pyw` is included by default in preview (#21885) 2025-12-10 16:43:55 +00:00
faq.md Show partial fixability indicator in statistics output (#21513) 2025-11-27 18:03:36 +01:00
formatter.md Fluent formatting of method chains (#21369) 2025-12-15 09:29:50 -06:00
installation.md Use uv consistently throughout the documentation (#15302) 2025-01-07 14:43:25 +00:00
integrations.md Prepare 0.14.9 release (#21927) 2025-12-11 13:17:52 -08:00
linter.md Document range suppressions, reorganize suppression docs (#21884) 2025-12-11 11:16:36 -08:00
preview.md Fix minor punctuation errors (#16228) 2025-02-18 12:24:57 +00:00
requirements.txt Update mkdocs-material to 9.7.0 (Insiders now free) (#21797) 2025-12-05 08:53:08 +01:00
tutorial.md Prepare 0.14.9 release (#21927) 2025-12-11 13:17:52 -08:00
versioning.md Ignore deprecated rules unless selected by exact code (#20167) 2025-09-10 09:00:27 -04:00