Commit da2d0fa
authored
Smarter handling of "block" and "headline" formatting. (#1687)
* Start adding support for "shape" parent constraints.
Currently, a parent can only indicate whether a child can contain newlines or not when determining whether a solution is invalid. This
works fine for most pieces, but isn't sophisticated enough to allow fixing #1465 and #1466 where we want to say that the child can have newlines but only in certain styles.
This is a step towards supporting those more sophisticated constraints. It doesn't change the underlying semantics at all, but it replaces the basic boolean newlines yes/no, with a Set<Shape>.
* Make indentation semantic.
Instead of pushing a raw integer for how much indentation a piece adds, use an enum with different values for each kind of indentation.
Then, instead of a separate "canCollapse" parameter, have some logic that understands that certain pairs of semantic indentation can be merged. The hope is that this should then simplify some of the complex indentation handling in AssignPiece (and maybe elsewhere).
There's no behavioral change or deep refactoring in this commit. It just replaces the int indentation and "canCollapse" parameter with an enum.
Note that the old short style code also has a class named Indent which has static constants for various indentation levels. The tall code also used that same class. Now it uses its own Indent class which is an actual enum. That's why much of the change in this commit is just removing an import. The existing uses of Indent silently switch over to the new enum in code_writer.dart.
* Use indentation merging to handle flattened indentation in assignments.
It looks nice if we align binary operands when the first operand is on its own line, which is often the case after `=`, `:`, and `=>`:
```dart
variable =
operand +
another;
```
This is also true for adjacent strings and conditional expressions.
Previously, we had some somewhat hacky code that looked at the AST node surrounding an expression to determine if it should indent itself or not. This removes that in favor of using the new merging ability of Indent. We use a separate Indent type for assignment and infix operators and then merge those two together.
This simplifies a bunch of things and lends itself to more uniform formatting since every AST node that lowers to a piece using Indent.infix will get this behavior. In fact, this commit highlighted that `is` and `as` expressions currently *aren't* formatted the same way as other binary operators, which I think was just an oversight.
This commit still formats them differently, but I'll probably change that later. For now, it just leaves TODOs.
This commit does change the formatter behavior very slightly, but only in obscure edge cases.
* Add support for block shapes and use that for block formatting.
This is (hopefully) the big commit. It's largely a refactoring but also has some style changes. Most of the style changes in places where the prior formatting was unintentional or a compromise to the old architecture.
The changes in this commit are:
- Add a new "block" shape. Pieces that split into delimited lists (collection literals, blocks, argument lists) have block shape.
- Change AssignPiece, CasePiece, ChainPiece, and ForInPiece to use that for handling their block-formatted child pieces. Previous commits already let a piece constrain its children to a subset of the allowed shapes, and now we use that to let them say not just whether not a newline is allowed, but what resulting shape the child must have.
- Add some machinery to CodeWriter to control the shape of the resulting piece when a newline is written. Coming up with a nice API for that is pretty hard, but I think what I have here works OK. There is also some machinery to determine how a child piece's shape affects the resulting shape of the parent. It isn't used heavily yet but will probably be used more in future changes to tweak the style for more complex nested expressions.
This commit has two negative consequences:
- Perf is regressed roughly 15% across the board. That's not great, but it's not catastrophic. To avoid a new pathological corner case, I added a hack where nested `=>` expressions (which occur when someone really wants to write Haskell in Dart) always force the outer ones to split. Without that, curried code gets catastrophically slow. I have some ideas on how to get some performance back but I'll save that for later.
- Some "headline"-shaped output is no longer supported. It's always been a goal of the formatter to support output like:
```dart
variable = target
.method()
.another();
```
Prior to this commit, that *sort of* worked for most expressions, but really only incidentally and you could run into places where it should work but didn't. Moving to shape-based constraints makes it uniformly *not* work. In a future commit, I plan to add another "headline" shape that will get code like this working again, reliably.
On the plus side, the behavior of the formatter is overall more consistent with this change.
* Fix double indentation of const pattern.
* Get "headline"-style formatting working.
We now allow some non-block shaped pieces on the right side of an assignment without splitting if it those pieces are "headline"-shaped. That means they have a single first line that makes sense on the `=` line followed by other subordinate code. There are three kinds of expressions that support that:
```dart
// Conditionals:
variable = condition
? thenBranch
: elseBranch;
// Split method chains:
variable = target
.method()
.another();
// (Also with unsplit properties):
variable = target.some.props
.method()
.another();
// Multi-line strings:
variable = '''
A very long string
that is multiple lines.''';
```1 parent 71ac085 commit da2d0fa
File tree
88 files changed
+1331
-1172
lines changed- benchmark/case
- lib/src
- back_end
- front_end
- piece
- test/tall
- expression
- function
- invocation
- pattern
- regression
- 0000
- 0100
- 0200
- 0300
- 0400
- 0500
- 0600
- 0700
- 0800
- 0900
- 1000
- 1100
- 1200
- 1400
- 1500
- 1600
- other
- variable
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
88 files changed
+1331
-1172
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
5 | 37 | | |
6 | 38 | | |
7 | 39 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
| 2 | + | |
2 | 3 | | |
3 | 4 | | |
4 | 5 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
754 | 754 | | |
755 | 755 | | |
756 | 756 | | |
757 | | - | |
758 | | - | |
759 | | - | |
760 | | - | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
761 | 760 | | |
762 | | - | |
763 | | - | |
764 | | - | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
765 | 764 | | |
766 | 765 | | |
767 | 766 | | |
| |||
974 | 973 | | |
975 | 974 | | |
976 | 975 | | |
977 | | - | |
978 | | - | |
| 976 | + | |
979 | 977 | | |
980 | 978 | | |
981 | 979 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
162 | 162 | | |
163 | 163 | | |
164 | 164 | | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
165 | 185 | | |
166 | 186 | | |
167 | 187 | | |
| |||
187 | 207 | | |
188 | 208 | | |
189 | 209 | | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
190 | 213 | | |
191 | 214 | | |
192 | 215 | | |
| |||
375 | 398 | | |
376 | 399 | | |
377 | 400 | | |
378 | | - | |
379 | | - | |
| 401 | + | |
| 402 | + | |
380 | 403 | | |
381 | | - | |
382 | | - | |
383 | | - | |
384 | | - | |
385 | | - | |
386 | | - | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
387 | 408 | | |
388 | | - | |
389 | | - | |
| 409 | + | |
390 | 410 | | |
391 | | - | |
392 | | - | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
393 | 416 | | |
394 | | - | |
395 | | - | |
396 | | - | |
| 417 | + | |
| 418 | + | |
397 | 419 | | |
398 | | - | |
399 | | - | |
| 420 | + | |
| 421 | + | |
400 | 422 | | |
401 | 423 | | |
402 | 424 | | |
| |||
411 | 433 | | |
412 | 434 | | |
413 | 435 | | |
414 | | - | |
415 | | - | |
416 | | - | |
417 | | - | |
418 | | - | |
419 | | - | |
420 | | - | |
421 | | - | |
422 | | - | |
423 | | - | |
424 | | - | |
425 | | - | |
426 | | - | |
427 | | - | |
428 | | - | |
429 | 436 | | |
430 | 437 | | |
431 | 438 | | |
432 | 439 | | |
433 | 440 | | |
434 | 441 | | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
435 | 445 | | |
436 | 446 | | |
437 | 447 | | |
| |||
0 commit comments