ruff/crates
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
..
ruff Update MSRV to 1.90 (#21987) 2025-12-15 14:29:11 +01:00
ruff_annotate_snippets Only render hyperlinks for terminals known to support them (#21519) 2025-11-19 10:02:58 +01:00
ruff_benchmark [ty] Infer type of implicit `cls` parameter in method bodies (#21685) 2025-12-10 10:31:28 +01:00
ruff_cache Switch to Rust 2024 edition (#18129) 2025-05-16 13:25:28 +02:00
ruff_db [ty] Remove legacy `concise_message` fallback behavior (#21847) 2025-12-08 16:19:01 +00:00
ruff_dev [ty] Fix callout syntax in configuration mkdocs (#1875) (#21961) 2025-12-14 10:21:54 +01:00
ruff_diagnostics Update debug_assert which pointed at missing method (#21969) 2025-12-13 17:56:59 -05:00
ruff_formatter Update MSRV to 1.90 (#21987) 2025-12-15 14:29:11 +01:00
ruff_graph [ty] Teach `ty` the meaning of desperation (try ancestor `pyproject.toml`s as search-paths if module resolution fails) (#21745) 2025-12-03 15:04:36 -05:00
ruff_index Update Rust toolchain to 1.88 and MSRV to 1.86 (#19011) 2025-06-28 20:24:00 +02:00
ruff_linter Update MSRV to 1.90 (#21987) 2025-12-15 14:29:11 +01:00
ruff_macros Document when a rule was added (#21035) 2025-10-23 14:48:41 -04:00
ruff_memory_usage [ty] Enable LRU collection for parsed module (#21749) 2025-12-03 12:16:18 +01:00
ruff_notebook [ty] Respect notebook cell boundaries when adding an auto import (#21322) 2025-11-13 18:58:08 +01:00
ruff_options_metadata Update Rust toolchain to 1.89 (#19807) 2025-08-07 18:21:50 +02:00
ruff_python_ast Remove `BackwardsTokenizer` based `parenthesized_range` references in `ruff_linter` (#21836) 2025-12-11 13:04:57 +01:00
ruff_python_ast_integration_tests Add token based `parenthesized_ranges` implementation (#21738) 2025-12-03 08:15:17 +00:00
ruff_python_codegen Update MSRV to 1.90 (#21987) 2025-12-15 14:29:11 +01:00
ruff_python_formatter Fluent formatting of method chains (#21369) 2025-12-15 09:29:50 -06:00
ruff_python_importer Move `Token`, `TokenKind` and `Tokens` to `ruff-python-ast` (#21760) 2025-12-02 20:10:46 +01:00
ruff_python_index Move `Token`, `TokenKind` and `Tokens` to `ruff-python-ast` (#21760) 2025-12-02 20:10:46 +01:00
ruff_python_literal Switch to Rust 2024 edition (#18129) 2025-05-16 13:25:28 +02:00
ruff_python_parser Use datatest for formatter tests (#21933) 2025-12-13 08:02:22 +00:00
ruff_python_semantic Add rule to detect unnecessary class properties (#21535) 2025-11-26 09:31:22 +01:00
ruff_python_stdlib [`flake8-bugbear`] Accept immutable slice default arguments (`B008`) (#21823) 2025-12-08 14:00:43 -05:00
ruff_python_trivia Handle t-string prefixes in `SimpleTokenizer` (#20578) 2025-09-25 14:33:37 -05:00
ruff_python_trivia_integration_tests Handle t-string prefixes in `SimpleTokenizer` (#20578) 2025-09-25 14:33:37 -05:00
ruff_server apply range suppressions to filter diagnostics (#21623) 2025-12-08 16:11:59 -08:00
ruff_source_file Use `memchr` for computing line indexes (#21838) 2025-12-08 08:50:51 -05:00
ruff_text_size [ty] Fix subtraction overflow bug 2025-11-21 15:07:37 -05:00
ruff_wasm Prepare 0.14.9 release (#21927) 2025-12-11 13:17:52 -08:00
ruff_workspace `analyze`: Add option to skip over imports in `TYPE_CHECKING` blocks (#21472) 2025-11-16 12:30:24 +00:00
ty [ty] Use jemalloc on linux (#21975) 2025-12-15 16:04:34 +01:00
ty_combine [ty] Disallow std::env and io methods in most ty crates (#20046) 2025-08-22 11:13:47 -07:00
ty_completion_eval [ty] Update completion eval to include modules 2025-12-04 17:37:37 -05:00
ty_ide [ty] Add "qualify ..." code fix for undefined references (#21968) 2025-12-15 10:14:36 -05:00
ty_project Update MSRV to 1.90 (#21987) 2025-12-15 14:29:11 +01:00
ty_python_semantic [ty] Avoid stack overflow when calculating inferable typevars (#21971) 2025-12-15 10:25:33 -05:00
ty_server [ty] Add "qualify ..." code fix for undefined references (#21968) 2025-12-15 10:14:36 -05:00
ty_static [ty] improve base conda distinction from child conda (#20675) 2025-10-03 13:56:06 +00:00
ty_test [ty] Remove legacy `concise_message` fallback behavior (#21847) 2025-12-08 16:19:01 +00:00
ty_vendored [ty] Carry generic context through when converting class into `Callable` (#21798) 2025-12-05 08:57:21 -05:00
ty_wasm [ty] Add code action to ignore diagnostic on the current line (#21595) 2025-11-29 15:41:54 +01:00