Commit Graph

38 Commits

Author SHA1 Message Date
Micha Reiser 7dfde3b929
Update Rust toolchain to 1.89 (#19807) 2025-08-07 18:21:50 +02:00
Micha Reiser 9ae698fe30
Switch to Rust 2024 edition (#18129) 2025-05-16 13:25:28 +02:00
Micha Reiser 196e4befba
Update MSRV to 1.85 and toolchain to 1.87 (#18126) 2025-05-16 09:19:55 +02:00
Micha Reiser 73ee72b665
Join implicit concatenated strings when they fit on a line (#13663) 2024-10-24 11:52:22 +02:00
Victorien b6545ce5d6
Use `indentation` consistently (#12293) 2024-07-12 14:08:56 +02:00
StevenMia f9214f95bb
chore: remove repetitive words (#10952) 2024-04-15 12:57:48 +00:00
Micha Reiser 4f7fb566f0
Range formatting: Fix invalid syntax after parenthesizing expression (#9751) 2024-02-02 17:56:25 +01:00
Micha Reiser ce14f4dea5
Range formatting API (#9635) 2024-01-31 11:13:37 +01:00
Andrew Gallant f585e3e2dc
remove several uses of `unsafe` (#8600)
This PR removes several uses of `unsafe`. I generally limited myself to
low hanging fruit that I could see. There are still a few remaining uses
of `unsafe` that looked a bit more difficult to remove (if possible at
all). But this gets rid of a good chunk of them.

I put each `unsafe` removal into its own commit with a justification for
why I did it. So I would encourage reviewing this PR commit-by-commit.
That way, we can legislate them independently. It's no problem to drop a
commit if we feel the `unsafe` should stay in that case.
2023-11-28 09:50:03 -05:00
Micha Reiser 2c2ebf952a
Rust 1.73 (#8007) 2023-10-23 02:12:25 +00:00
Micha Reiser 192463c2fb
Allow parenthesized content exceed configured line width (#7490) 2023-09-20 08:39:25 +02:00
Micha Reiser 6a4dbd622b
Add optimized `best_fit_parenthesize` IR (#7475) 2023-09-19 06:29:05 +00:00
Micha Reiser 3336d23f48
perf: Reduce best fitting allocations (#7411) 2023-09-18 19:49:44 +00:00
Micha Reiser 2d9b39871f
Introduce `IndentWidth` (#7301) 2023-09-13 14:52:24 +02:00
Micha Reiser 5f59101811
Memoize text width (#6552) 2023-09-06 07:10:13 +00:00
Micha Reiser c05e4628b1
Introduce Token element (#7048) 2023-09-02 10:05:47 +02:00
Chris Pryer 924f10186f
Update `dynamic_text` builder doc comment (#6978) 2023-08-29 16:29:11 +02:00
konsti e615870659
Unify line size settings between ruff and the formatter (#6873) 2023-08-28 06:44:56 +00:00
Chris Pryer 039694aaed
Add `LineSuffix` reserved width (#6830)
Thanks for working on this.
2023-08-28 07:46:54 +02:00
Micha Reiser eae59cf088
Optional source map generation (#6894) 2023-08-26 18:00:43 +02:00
Micha Reiser 04a9a8dd03
Maybe parenthesize long constants and names (#6816) 2023-08-24 09:47:57 +00:00
Micha Reiser 34b2ae73b4
Extend `BestFitting` with `mode` (#6814) 2023-08-23 17:23:45 +02:00
Micha Reiser daac31d2b9
Make `Buffer::write_element` non-failable (#6613) 2023-08-16 15:13:07 +02:00
Micha Reiser 9584f613b9
Remove `allow(pedantic)` from formatter (#6549) 2023-08-14 14:02:06 +02:00
Charlie Marsh 1d8759d5df
Generalize comment-after-bracket handling to lists, sets, etc. (#6320)
## Summary

We already support preserving the end-of-line comment in calls and type
parameters, as in:

```python
foo(  # comment
    bar,
)
```

This PR adds the same behavior for lists, sets, comprehensions, etc.,
such that we preserve:

```python
[  # comment
    1,
    2,
    3,
]
```

And related cases.
2023-08-04 01:28:05 +00:00
konsti 1df7e9831b
Replace `.map_or(false, $closure)` with `.is_some_and(closure)` (#6244)
**Summary**
[Option::is_some_and](https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.is_some_and)
and
[Result::is_ok_and](https://doc.rust-lang.org/std/result/enum.Result.html#method.is_ok_and)
are new methods is rust 1.70. I find them way more readable than
`.map_or(false, ...)`.

The changes are `s/.map_or(false,/.is_some_and(/g`, then manually
switching to `is_ok_and` where the value is a Result rather than an
Option.

**Test Plan** n/a^
2023-08-01 19:29:42 +02:00
Micha Reiser 9a8ba58b4c
Remove `mode` from `BestFitting`
<!--
Thank you for contributing to Ruff! To help us out with reviewing, please consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

This PR removes the `mode` field from `BestFitting` because it is no longer used (we now use `conditional_group` and `fits_expanded).

<!-- What's the purpose of the change? What does it do, and why? -->

## Test Plan

`cargo test`

<!-- How was it tested? -->
2023-07-11 14:19:26 +02:00
Micha Reiser d30e9125eb
Extend formatter IR to support Black's expression formatting (#5596) 2023-07-11 11:20:04 +00:00
Micha Reiser 3d7411bfaf
Use trait for labels instead of `TypeId` (#5270) 2023-06-21 22:26:09 +01:00
Micha Reiser d9e59b21cd
Add BestFittingMode (#5184)
## Summary
Black supports for layouts when it comes to breaking binary expressions:

```rust
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum BinaryLayout {
    /// Put each operand on their own line if either side expands
    Default,

    /// Try to expand the left to make it fit. Add parentheses if the left or right don't fit.
    ///
    ///```python
    /// [
    ///     a,
    ///     b
    /// ] & c
    ///```
    ExpandLeft,

    /// Try to expand the right to make it fix. Add parentheses if the left or right don't fit.
    ///
    /// ```python
    /// a & [
    ///     b,
    ///     c
    /// ]
    /// ```
    ExpandRight,

    /// Both the left and right side can be expanded. Try in the following order:
    /// * expand the right side
    /// * expand the left side
    /// * expand both sides
    ///
    /// to make the expression fit
    ///
    /// ```python
    /// [
    ///     a,
    ///     b
    /// ] & [
    ///     c,
    ///     d
    /// ]
    /// ```
    ExpandRightThenLeft,
}
```

Our current implementation only handles `ExpandRight` and `Default` correctly. `ExpandLeft` turns out to be surprisingly hard. This PR adds a new `BestFittingMode` parameter to `BestFitting` to support `ExpandLeft`.

There are 3 variants that `ExpandLeft` must support:

**Variant 1**: Everything fits on the line (easy)

```python
[a, b] + c
```

**Variant 2**: Left breaks, but right fits on the line. Doesn't need parentheses

```python
[
	a,
	b
] + c
```

**Variant 3**: The left breaks, but there's still not enough space for the right hand side. Parenthesize the whole expression:

```python
(
	[
		a, 
		b
	]
	+ ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
)
```

Solving Variant 1 and 2 on their own is straightforward The printer gives us this behavior by nesting right inside of the group of left:

```
group(&format_args![
	if_group_breaks(&text("(")),
	soft_block_indent(&group(&format_args![
		left, 
		soft_line_break_or_space(), 
		op, 
		space(), 
		group(&right)
	])),
	if_group_breaks(&text(")"))
])
```

The fundamental problem is that the outer group, which adds the parentheses, always breaks if the left side breaks. That means, we end up with

```python
(
	[
		a,
		b
	] + c
)
```

which is not what we want (we only want parentheses if the right side doesn't fit). 

Okay, so nesting groups don't work because of the outer parentheses. Sequencing groups doesn't work because it results in a right-to-left breaking which is the opposite of what we want. 

Could we use best fitting? Almost! 

```
best_fitting![
	// All flat
	format_args![left, space(), op, space(), right],
	// Break left
	format_args!(group(&left).should_expand(true), space(), op, space(), right],
	// Break all
	format_args![
		text("("), 
		block_indent!(&format_args![
			left, 
			hard_line_break(), 
			op,
			space()
			right
		])
	]
]
```

I hope I managed to write this up correctly. The problem is that the printer never reaches the 3rd variant because the second variant always fits:

* The `group(&left).should_expand(true)` changes the group so that all `soft_line_breaks` are turned into hard line breaks. This is necessary because we want to test if the content fits if we break after the `[`. 
* Now, the whole idea of `best_fitting` is that you can pretend that some content fits on the line when it actually does not. The way this works is that the printer **only** tests if all the content of the variant **up to** the first line break fits on the line (we insert that line break by using `should_expand(true))`. The printer doesn't care whether the rest `a\n, b\n ] + c` all fits on (multiple?) lines. 

Why does breaking right work but not breaking the left? The difference is that we can make the decision whether to parenthesis the expression based on the left expression. We can't do this for breaking left because the decision whether to insert parentheses or not would depend on a lookahead: will the right side break. We simply don't know this yet when printing the parentheses (it would work for the right parentheses but not for the left and indent).

What we kind of want here is to tell the printer: Look, what comes here may or may not fit on a single line but we don't care. Simply test that what comes **after** fits on a line. 

This PR adds a new `BestFittingMode` that has a new `AllLines` option that gives us the desired behavior of testing all content and not just up to the first line break. 

## Test Plan

I added a new example to  `BestFitting::with_mode`
2023-06-20 18:16:01 +02:00
Zanie Adkins 14e06f9f8b
Rename `ruff_formatter::builders::BestFitting` to `FormatBestFitting` (#4841) 2023-06-04 00:13:51 +02:00
Micha Reiser 86ced3516b
Introduce `SourceCodeSlice` to reduce the size of `FormatElement` (#4622)
Introduce `SourceCodeSlice` to reduce the size of `FormatElement`
2023-05-24 15:04:52 +00:00
Micha Reiser 6943beee66
Remove source position from `FormatElement::DynamicText` (#4619) 2023-05-24 16:36:14 +02:00
Jonathan Plasse d285f5c90a
Run automatically format code blocks with Black (#3191) 2023-02-27 10:14:05 -05:00
Micha Reiser ffd8e958fc
chore: Upgrade Rust to 1.67.0 (#3125) 2023-02-22 10:03:17 -05:00
Charlie Marsh f661c90bd7
Remove dependency on `ruff_rowan` (#2875)
This PR removes the dependency on `ruff_rowan` (i.e., Rome's fork of rust-analyzer's `rowan`), and in turn, trims out a lot of code in `ruff_formatter` that isn't necessary (or isn't _yet_ necessary) to power the autoformatter.

We may end up pulling some of this back in -- TBD. For example, the autoformatter has its own comment representation right now, but we may eventually want to use the `comments.rs` data structures defined in `rome_formatter`.
2023-02-15 03:54:08 +00:00
Charlie Marsh 98ea94fdb7
Add `StaticTextSlice` kind to `FormatElement` enum (#2873)
Given our current parser abstractions, we need the ability to tell `ruff_formatter` to print a pre-defined slice from a fixed string of source code, which we've introduced here as `FormatElement::StaticTextSlice`.
2023-02-14 22:27:52 -05:00
Charlie Marsh 3ef1c2e303
Add `rome_formatter` fork as `ruff_formatter` (#2872)
The Ruff autoformatter is going to be based on an intermediate representation (IR) formatted via [Wadler's algorithm](https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf). This is architecturally similar to [Rome](https://github.com/rome/tools), Prettier, [Skip](https://github.com/skiplang/skip/blob/master/src/tools/printer/printer.sk), and others.

This PR adds a fork of the `rome_formatter` crate from [Rome](https://github.com/rome/tools), renamed here to `ruff_formatter`, which provides generic definitions for a formatter IR as well as a generic IR printer. (We've also pulled in `rome_rowan`, `rome_text_size`, and `rome_text_edit`, though some of these will be removed in future PRs.)

Why fork? `rome_formatter` contains code that's specific to Rome's AST representation (e.g., it relies on a fork of rust-analyzer's `rowan`), and we'll likely want to support different abstractions and formatting capabilities (there are already a few changes coming in future PRs). Once we've dropped `ruff_rowan` and trimmed down `ruff_formatter` to the code we currently need, it's also not a huge surface area to maintain and update.
2023-02-14 19:22:55 -05:00