{"payload":{"feedbackUrl":"https://github.com/orgs/community/discussions/53140","repo":{"id":24516823,"defaultBranch":"main","name":"dart_style","ownerLogin":"dart-lang","currentUserCanPush":false,"isFork":false,"isEmpty":false,"createdAt":"2014-09-26T22:04:21.000Z","ownerAvatar":"https://avatars.githubusercontent.com/u/1609975?v=4","public":true,"private":false,"isOrgOwned":true},"refInfo":{"name":"","listCacheKey":"v0:1716325574.0","currentOid":""},"activityList":{"items":[{"before":"59836020add0a07f043018435da29608f9327c78","after":null,"ref":"refs/heads/invalidate-by-constraint","pushedAt":"2024-05-21T21:06:14.000Z","pushType":"branch_deletion","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"}},{"before":"8a236c0b3422ef88e8ab83d5bb9a8b4c2b82ddea","after":"3f825f6c198a26d828aa7d8d739d6e5a6b34b1fd","ref":"refs/heads/main","pushedAt":"2024-05-21T21:06:13.000Z","pushType":"pr_merge","commitsCount":1,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Invalidate eagerly by newline constraint (#1495)\n\nPropagate newline constrains eagerly when binding states in a Solution.\r\n\r\nOne of the main ways the formatter defines its formatting style is by having constraints that prohibit newlines in some child pieces when a parent is in a given state.\r\n\r\nThe two simplest examples (which cover a large amount of code) is that when an InfixPiece or a ListPiece is in State.unsplit, they don't allow any newlines in any of their children. That constraint effectively creates the desired style which is that a split inside an infix operand or list element forces the surrounding expression to split.\r\n\r\nWhenever we bind a parent piece to some state, we can now ask it if it allows any of its children to contain newlines. For any given child, if the answer is \"no\" then:\r\n\r\n- If the child is already bound to a state that contains newlines, then we know this solution is a dead end. It and every solution you can ever derive from it will contain an invalid newline.\r\n\r\n- If the child isn't bound to a state yet, we can still look at all of its states and see which of them contain newlines. Any state that does can be eliminated because we'll never successfully bind the child to that state without violating this constraint.\r\n\r\n It may turn out that no states are left, in which case again we have a dead end solution.\r\n\r\n Or there may be just one valid state (usually State.unsplit), and we can immediately the child to that state and do this whole process recursively for this child.\r\n\r\n Or there may be just a couple of states left and we can at least winnow this child's states down to that list when we go to expand it later.\r\n\r\nThe end result is that even before actually formatting a solution, we can often tell if it's a dead end and discard it. If not, we can often bind a whole bunch of the bound piece's children in one go instead of having to do them one at a time and slowly formatting the interim solutions at each step.\r\n\r\nThe end result is a pretty decent improvement. Here's the micro benchmarks:\r\n\r\n```\r\nBenchmark (tall) fastest median slowest average baseline\r\n----------------------------- -------- ------- ------- ------- --------\r\nblock 0.070 0.071 0.122 0.075 92.7%\r\nchain 0.640 0.654 0.681 0.655 254.5%\r\ncollection 0.175 0.180 0.193 0.181 96.3%\r\ncollection_large 0.930 0.955 0.988 0.955 97.2%\r\nconditional 0.067 0.068 0.086 0.071 132.4%\r\ncurry 0.596 0.608 1.478 0.631 278.9%\r\nflutter_popup_menu_test 0.293 0.302 0.326 0.303 141.4%\r\nflutter_scrollbar_test 0.160 0.166 0.184 0.166 96.1%\r\nfunction_call 1.460 1.483 1.680 1.490 97.5%\r\ninfix_large 0.709 0.733 0.761 0.733 97.4%\r\ninfix_small 0.175 0.181 0.198 0.181 93.0%\r\ninterpolation 0.091 0.096 0.118 0.096 98.7%\r\nlarge 3.514 3.574 3.767 3.596 129.5%\r\ntop_level 0.146 0.150 0.182 0.152 106.7%\r\n```\r\n\r\nThere's a slight regression on some of the tiny microbenchmarks (probably because there's some overhead traversing and binding children to State.unsplit when that solution ends up winning anyway). But the improvement to the larger ones is significant.\r\n\r\nMore interesting is the overall performance. Here's formatting the Flutter repo:\r\n\r\n```\r\nCurrent formatter 10.964 ========================================\r\nThis PR 8.826 ================================\r\nShort style formatter 4.558 ================\r\n```\r\n\r\nThe current formatter is 58.43% slower than the old short style formatter.\r\n\r\nThis PR is 24.22% faster than the current formatter. It's 48.36% slower than the old formatter, but it gets the formatter 33.37% of the way to the old short style one.","shortMessageHtmlLink":"Invalidate eagerly by newline constraint (#1495)"}},{"before":"99f928c062e9df25d60b9bdceedb6b60068f897f","after":"59836020add0a07f043018435da29608f9327c78","ref":"refs/heads/invalidate-by-constraint","pushedAt":"2024-05-21T03:32:15.000Z","pushType":"push","commitsCount":1,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Fix long line.","shortMessageHtmlLink":"Fix long line."}},{"before":null,"after":"99f928c062e9df25d60b9bdceedb6b60068f897f","ref":"refs/heads/invalidate-by-constraint","pushedAt":"2024-05-21T03:30:20.000Z","pushType":"branch_creation","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Propagate newline constraints when binding states in a Solution.\n\nWhen we expand a solution, before we format, see if the newly bound\npiece's state constrains other children of that piece to certain states\nor leads them to having no valid state. This lets is quickly invalidate\nand discard a number of solutions before going through the expensive\nformat() process. It also lets us eagerly bind child pieces to states\ninstead of having to explore a bunch of states for them that we can\nalready tell will not match.","shortMessageHtmlLink":"Propagate newline constraints when binding states in a Solution."}},{"before":"976fe44d5bc21220f91b6a7c34c187cc98cbb07d","after":null,"ref":"refs/heads/revamp-function-types","pushedAt":"2024-05-20T23:40:56.000Z","pushType":"branch_deletion","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"}},{"before":"0a785297b6c89e39848cf9e576acda6d761a16ad","after":"8a236c0b3422ef88e8ab83d5bb9a8b4c2b82ddea","ref":"refs/heads/main","pushedAt":"2024-05-20T23:40:55.000Z","pushType":"pr_merge","commitsCount":1,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Rework how function types, type annotations, and typedefs are handled. (#1493)\n\nIn the process of trying to simplify the number of Piece classes, I noticed that FunctionPiece basically exists to split between the return type annotation and the rest of a function. That's pretty similar to how VariablePiece handles splitting between a variable's type annotation and name.\r\n\r\nI unified those, but then it made typedefs format funny. Looking into\r\nit, it's because typedefs have `=` but weren't using AssignPiece. Instead, they just never allowed splitting at the `=`. So I made that uniform with the rest of the style and used AssignPiece here.\r\n\r\nThat led to some weird looking code in cases like:\r\n\r\n Function(int someParameter) someVariable;\r\n\r\nIf that line doesn't fit, the formatter has to decide whether to split inside the type annotation or between the type and variable. There were different heuristics for return types followed by function names versus type annotations followed by variable names. Unifying those led to some weird output like:\r\n\r\n Function(\r\n int someParameter,\r\n ) Function(\r\n int someParameter,\r\n ) someVariable;\r\n\r\nThis is a variable whose type is a function that returns another function. Admittedly, no one writes code like this. Ultimately, I felt like the weirdness was from allowing the variable name to hang off the end of a split annotation. In most places in the style, if an inner construct splits, the outer one does too.\r\n\r\nSo I changed that. If a type annotation splits, then we also split after the type annotation too. That means after a return type before a function name, or after a variable type before a variable. So instead of allowing:\r\n\r\n SomeGenericClass<\r\n LongTypeArgument,\r\n AnotherLongTypeArgument\r\n > variable;\r\n\r\nThe split in the type argument list forces the variable name to split too:\r\n\r\n SomeGenericClass<\r\n LongTypeArgument,\r\n AnotherLongTypeArgument\r\n >\r\n variable;\r\n\r\nI think I like how this looks a little more but I'm not sure. In practice, it doesn't matter much because it's rare for a type annotation to be long enough to split, but it does happen. For what it's worth, it's consistent with metadata on variables. If the metadata splits, we also split before the variable too:\r\n\r\n @SomeMetadata(\r\n 'annotation argument',\r\n 'another argument',\r\n )\r\n variable;\r\n\r\nThoughts?","shortMessageHtmlLink":"Rework how function types, type annotations, and typedefs are handled. ("}},{"before":"4f0d88a3b67172bc61eb9c34d36c6a9ee87b6d49","after":null,"ref":"refs/heads/multiple-expand-pieces","pushedAt":"2024-05-20T22:22:20.000Z","pushType":"branch_deletion","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"}},{"before":"d2b88fc1bbf06e27eb379d47b7d1f90062e307d7","after":"0a785297b6c89e39848cf9e576acda6d761a16ad","ref":"refs/heads/main","pushedAt":"2024-05-20T22:22:19.000Z","pushType":"pr_merge","commitsCount":1,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Use all unsolved pieces on the offending line as expand pieces. (#1494)\n\nThe solver works by incrementally building a up a solution by binding pieces to states one at a time. To avoid wasting time exploring solutions that are pointless, it only looks at unbound pieces in play when the first line containing overflow characters or an invalid newline as written.\r\n\r\nBefore this PR, it only looked at the *first* unbound piece on that line. Often, the first piece on the line is not the one that actually needs to split. For example, in:\r\n\r\n```dart\r\n// |\r\nvariable = function(argument);\r\n```\r\n\r\nHere, the first piece on the overflowing line is the AssignPiece for the `=`, but the piece we actually want to split at is the ListPiece for the argument list.\r\n\r\nTo handle that, the solver currently tries binding the first piece to all values, including State.unsplit, even though that's effectively the value the current solution used, since unbound pieces behave like they have State.unsplit. The only reason it makes a new solution and binds the piece to State.unsplit is that if that piece turns out to *not* be the one that needs to split, we can now find the *next* piece on that same line. Now that the first piece is *bound* to State.unsplit, the second piece will be the first *unbound* one.\r\n\r\nBut the end result is that we end up generating a lot of more or less redundant solutions that just bind a bunch of pieces to State.unsplit and then produce the exact same formatted result.\r\n\r\nInstead, this PR collects *all* of the unbound pieces on the first overflowing line. When it expands, it expands them all, but *doesn't* bind any of them to State.unsplit. The old formatter works the same way, but it wasn't clear to me that doing so was important for perf. It is!\r\n\r\n```\r\n\r\nBenchmark (tall) fastest median slowest average baseline\r\n----------------------------- -------- ------- ------- ------- --------\r\nblock 0.065 0.067 0.131 0.070 96.3%\r\nchain 1.515 1.540 1.629 1.547 172.2%\r\ncollection 0.169 0.173 0.189 0.175 98.6%\r\ncollection_large 0.896 0.926 0.959 0.925 96.5%\r\nconditional 0.088 0.089 0.104 0.091 179.9%\r\ncurry 1.651 1.667 1.689 1.668 147.9%\r\nflutter_popup_menu_test 0.409 0.422 0.448 0.423 116.8%\r\nflutter_scrollbar_test 0.154 0.159 0.176 0.159 94.2%\r\nfunction_call 1.428 1.457 1.625 1.463 98.1%\r\ninfix_large 0.680 0.702 0.776 0.708 144.4%\r\ninfix_small 0.163 0.167 0.193 0.169 97.5%\r\ninterpolation 0.090 0.093 0.120 0.094 107.0%\r\nlarge 4.552 4.631 4.987 4.650 90.6%\r\ntop_level 0.180 0.183 0.214 0.185 135.0%\r\n```\r\n\r\nMy goal is to get the new formatter as fast as the old one on a real-world corpus. Here's the results for formatting the Flutter repo:\r\n\r\n```\r\nCurrent formatter 15.890 ========================================\r\nOptimized 14.309 ====================================\r\nOld formatter 7.131 =================\r\nThe current formatter is 55.12% slower than the old formatter.\r\nThe optimized is 11.05% faster than the current formatter.\r\nThe optimized is 50.16% slower than the old formatter.\r\nThe optimization gets the formatter 18.05% of the way to the old one.\r\n```\r\n\r\nSo not a huge improvement, but a big step in the right direction.","shortMessageHtmlLink":"Use all unsolved pieces on the offending line as expand pieces. (#1494)"}},{"before":"0249ea667e2787aaee7188adfa2d7a85f7c808f7","after":null,"ref":"refs/heads/dependabot/pub/dart_flutter_team_lints-3.1.0","pushedAt":"2024-05-20T18:58:44.000Z","pushType":"branch_deletion","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"}},{"before":"f8c285f3d1a13795588ce135833b2a75c957a9c0","after":"d2b88fc1bbf06e27eb379d47b7d1f90062e307d7","ref":"refs/heads/main","pushedAt":"2024-05-20T18:58:43.000Z","pushType":"pr_merge","commitsCount":1,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Bump dart_flutter_team_lints from 2.1.1 to 3.1.0 (#1487)\n\n* Bump dart_flutter_team_lints from 2.1.1 to 3.1.0\r\n\r\nBumps [dart_flutter_team_lints](https://github.com/dart-lang/ecosystem/tree/main/pkgs) from 2.1.1 to 3.1.0.\r\n- [Release notes](https://github.com/dart-lang/ecosystem/releases)\r\n- [Commits](https://github.com/dart-lang/ecosystem/commits/dart_flutter_team_lints-v3.1.0/pkgs)\r\n\r\n---\r\nupdated-dependencies:\r\n- dependency-name: dart_flutter_team_lints\r\n dependency-type: direct:production\r\n update-type: version-update:semver-major\r\n...\r\n\r\nSigned-off-by: dependabot[bot] \r\n\r\n* Remove unneeded library names.\r\n\r\n---------\r\n\r\nSigned-off-by: dependabot[bot] \r\nCo-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>\r\nCo-authored-by: Robert Nystrom ","shortMessageHtmlLink":"Bump dart_flutter_team_lints from 2.1.1 to 3.1.0 (#1487)"}},{"before":"52db4dfc85f44bfef375354253ef024271c2fec7","after":"0249ea667e2787aaee7188adfa2d7a85f7c808f7","ref":"refs/heads/dependabot/pub/dart_flutter_team_lints-3.1.0","pushedAt":"2024-05-20T18:23:06.000Z","pushType":"push","commitsCount":1,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Remove unneeded library names.","shortMessageHtmlLink":"Remove unneeded library names."}},{"before":"de001bf8cf99568d72c38d9376d0efcf948ee297","after":null,"ref":"refs/heads/unneeded-pieces","pushedAt":"2024-05-20T17:48:57.000Z","pushType":"branch_deletion","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"}},{"before":"0160142b3444e956936388010ac2bb1294dda8b9","after":"f8c285f3d1a13795588ce135833b2a75c957a9c0","ref":"refs/heads/main","pushedAt":"2024-05-20T17:48:56.000Z","pushType":"pr_merge","commitsCount":1,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Remove some unnecessary Piece classes. (#1491)\n\nRemove some unnecessary Piece classes.\r\n\r\nIn exploring various optimizations, it's clear that I'll probably need to make\r\nsome changes to how all of the Piece classes work. The more of those classes\r\nthere are, the harder that work is.\r\n\r\nFortunately, there are a few Piece classes that don't really need to exist. Most\r\nwere created before we had AdjacentPiece or the ability to insert spaces between\r\npieces.\r\n\r\nThis change:\r\n\r\n* Removes ClausePiece which it turns out behaves exactly like InfixPiece (and\r\n then renames ClausesPiece to ClausePiece).\r\n\r\n* Removes TryPiece. All it did was write its children with some spaces between\r\n them.\r\n\r\n* Removes PostfixPiece. It was just like InfixPiece but without a first operand.\r\n But it's only used by imports, where we can use the import header as the first\r\n operand and just use InfixPiece.\r\n\r\n* Remove AdjacentStringsPiece. It's just an InfixPiece that always splits.\r\n\r\n* Get rid of CaseStatementPiece. Once you hoist out the optionality of the\r\n guard, it's just an InfixPiece.\r\n\r\n* Reorganize ForPiece. Make that Piece just for the header part of a for\r\n statement or element. (We can almost get rid of this entirely and use\r\n AdjacentPiece, but it does some indentation that isn't otherwise supported.)\r\n \r\n Then to handle the body, we either write it inline if it's a block (the most\r\n common), or we use IfPiece, which has the same logic. Rename IfPiece to\r\n ControlFlowPiece to emphasize its more general use. It was already used for\r\n while statements, so this makes sense.","shortMessageHtmlLink":"Remove some unnecessary Piece classes. (#1491)"}},{"before":null,"after":"4f0d88a3b67172bc61eb9c34d36c6a9ee87b6d49","ref":"refs/heads/multiple-expand-pieces","pushedAt":"2024-05-18T05:22:52.000Z","pushType":"branch_creation","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Use all unsolved pieces on the offending line as expand pieces.\n\nThe solver works by incrementally building a up a solution by binding pieces to states one at a time. To avoid wasting time exploring solutions that are pointless, it only looks at unbound pieces in play when the first line containing overflow characters or an invalid newline as written.\n\nBefore this PR, it only looked at the *first* unbound piece on that line. Often, the first piece on the line is not the one that actually needs to split. For example, in:\n\n```dart\n// |\nvariable = function(argument);\n```\n\nHere, the first piece on the overflowing line is the AssignPiece for the `=`, but the piece we actually want to split at is the ListPiece for the argument list.\n\nTo handle that, the solver currently tries binding the first piece to all values, including State.unsplit, even though that's effectively the value the current solution used, since unbound pieces behave like they have State.unsplit. The only reason it makes a new solution and binds the piece to State.unsplit is that if that piece turns out to *not* be the one that needs to split, we can now find the *next* piece on that same line. Now that the first piece is *bound* to State.unsplit, the second piece will be the first *unbound* one.\n\nBut the end result is that we end up generating a lot of more or less redundant solutions that just bind a bunch of pieces to State.unsplit and then produce the exact same formatted result.\n\nInstead, this PR collects *all* of the unbound pieces on the first overflowing line. When it expands, it expands them all, but *doesn't* bind any of them to State.unsplit. The old formatter works the same way, but it wasn't clear to me that doing so was important for perf. It is!\n\n```\n\nBenchmark (tall) fastest median slowest average baseline\n----------------------------- -------- ------- ------- ------- --------\nblock 0.065 0.067 0.131 0.070 96.3%\nchain 1.515 1.540 1.629 1.547 172.2%\ncollection 0.169 0.173 0.189 0.175 98.6%\ncollection_large 0.896 0.926 0.959 0.925 96.5%\nconditional 0.088 0.089 0.104 0.091 179.9%\ncurry 1.651 1.667 1.689 1.668 147.9%\nflutter_popup_menu_test 0.409 0.422 0.448 0.423 116.8%\nflutter_scrollbar_test 0.154 0.159 0.176 0.159 94.2%\nfunction_call 1.428 1.457 1.625 1.463 98.1%\ninfix_large 0.680 0.702 0.776 0.708 144.4%\ninfix_small 0.163 0.167 0.193 0.169 97.5%\ninterpolation 0.090 0.093 0.120 0.094 107.0%\nlarge 4.552 4.631 4.987 4.650 90.6%\ntop_level 0.180 0.183 0.214 0.185 135.0%\n```\n\nMy goal is to get the new formatter as fast as the old one on a real-world corpus. Here's the results for formatting the Flutter repo:\n\n```\nCurrent formatter 15.890 ========================================\nOptimized 14.309 ====================================\nOld formatter 7.131 =================\nThe current formatter is 55.12% slower than the old formatter.\nThe optimized is 11.05% faster than the current formatter.\nThe optimized is 50.16% slower than the old formatter.\nThe optimization gets the formatter 18.05% of the way to the old one.\n```\n\nSo not a huge improvement, but a big step in the right direction.","shortMessageHtmlLink":"Use all unsolved pieces on the offending line as expand pieces."}},{"before":"25fa6adf5f8d3a52b0244720e5d94cd7a879c19e","after":null,"ref":"refs/heads/instrument-short-style","pushedAt":"2024-05-17T22:29:51.000Z","pushType":"branch_deletion","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"}},{"before":"6641bde54a9e8ed2f7091aad5596398b05a78bcf","after":"0160142b3444e956936388010ac2bb1294dda8b9","ref":"refs/heads/main","pushedAt":"2024-05-17T22:29:50.000Z","pushType":"pr_merge","commitsCount":1,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Add some profiling instrumentation to the old formatter. (#1492)\n\nThis helps comparing the different phases in the two implementations\r\nagainst each other.\r\n\r\nAlso, I added support to benchmark/directory.dart to run in tall or\r\nshort style.","shortMessageHtmlLink":"Add some profiling instrumentation to the old formatter. (#1492)"}},{"before":null,"after":"976fe44d5bc21220f91b6a7c34c187cc98cbb07d","ref":"refs/heads/revamp-function-types","pushedAt":"2024-05-17T22:28:13.000Z","pushType":"branch_creation","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Rework how function types, type annotations, and typedefs are handled.\n\nIn the process of trying to simplify the number of Piece classes, I noticed that FunctionPiece basically exists to split between the return type annotation and the rest of a function. That's pretty similar to how VariablePiece handles splitting between a variable's type annotation and name.\n\nI unified those, but then it made typedefs format funny. Looking into\nit, it's because typedefs have `=` but weren't using AssignPiece. Instead, they just never allowed splitting at the `=`. So I made that uniform with the rest of the style and used AssignPiece here.\n\nThat led to some weird looking code in cases like:\n\n Function(int someParameter) someVariable;\n\nIf that line doesn't fit, the formatter has to decide whether to split inside the type annotation or between the type and variable. There were different heuristics for return types followed by function names versus type annotations followed by variable names. Unifying those led to some weird output like:\n\n Function(\n int someParameter,\n ) Function(\n int someParameter,\n ) someVariable;\n\nThis is a variable whose type is a function that returns another function. Admittedly, no one writes code like this. Ultimately, I felt like the weirdness was from allowing the variable name to hang off the end of a split annotation. In most places in the style, if an inner construct splits, the outer one does too.\n\nSo I changed that. If a type annotation splits, then we also split after the type annotation too. That means after a return type before a function name, or after a variable type before a variable. So instead of allowing:\n\n SomeGenericClass<\n LongTypeArgument,\n AnotherLongTypeArgument\n > variable;\n\nThe split in the type argument list forces the variable name to split too:\n\n SomeGenericClass<\n LongTypeArgument,\n AnotherLongTypeArgument\n >\n variable;\n\nI think I like how this looks a little more but I'm not sure. In practice, it doesn't matter much because it's rare for a type annotation to be long enough to split, but it does happen. For what it's worth, it's consistent with metadata on variables. If the metadata splits, we also split before the variable too:\n\n @SomeMetadata(\n 'annotation argument',\n 'another argument',\n )\n variable;\n\nThoughts?","shortMessageHtmlLink":"Rework how function types, type annotations, and typedefs are handled."}},{"before":null,"after":"25fa6adf5f8d3a52b0244720e5d94cd7a879c19e","ref":"refs/heads/instrument-short-style","pushedAt":"2024-05-17T22:02:24.000Z","pushType":"branch_creation","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Add some profiling instrumentation to the old formatter.\n\nThis helps comparing the different phases in the two implementations\nagainst each other.\n\nAlso, I added support to benchmark/directory.dart to run in tall or\nshort style.","shortMessageHtmlLink":"Add some profiling instrumentation to the old formatter."}},{"before":null,"after":"de001bf8cf99568d72c38d9376d0efcf948ee297","ref":"refs/heads/unneeded-pieces","pushedAt":"2024-05-17T21:58:11.000Z","pushType":"branch_creation","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Reorganize ForPiece.\n\nMake that Piece just for the header part of a for statement or element.\n(We can almost get rid of this entirely and use AdjacentPiece, but it\ndoes some indentation that isn't otherwise supported.)\n\nThen to handle the body, we either write it inline if it's a block (the\nmost common), or we use IfPiece, which has the same logic. Rename\nIfPiece to ControlFlowPiece to emphasize its more general use. It was\nalready used for while statements, so this makes sense.","shortMessageHtmlLink":"Reorganize ForPiece."}},{"before":"64f24944e8f88dd3227c0d0e345a03d5d1eb83e5","after":null,"ref":"refs/heads/conditional-chain","pushedAt":"2024-05-16T00:15:59.000Z","pushType":"branch_deletion","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"}},{"before":"ce04d3834bc85b97b6cbcdec051e07a530a76c0d","after":"6641bde54a9e8ed2f7091aad5596398b05a78bcf","ref":"refs/heads/main","pushedAt":"2024-05-16T00:15:58.000Z","pushType":"pr_merge","commitsCount":1,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Flatten chained conditionals. (#1489)\n\nWhen testing the new formatter on a large corpus, I noticed the solver\r\nwould get stuck on large conditional chains because (unfortunately), we\r\ncan't separately format the else clause of a split conditional\r\nexpression. By merging long conditional chains into a single InfixPiece,\r\nwe can separately format all but the very last dangling else clause.\r\n\r\nWhile I was at it, I also put a hard cap in the number of solutions the\r\nsolver will try in case it still gets stuck. The old formatter has a\r\nsimilar limit. It's rare for real-world code to hit this limit in the\r\nnew solver, but it's better than getting totally stuck when it happens.","shortMessageHtmlLink":"Flatten chained conditionals. (#1489)"}},{"before":"9d268da05099886058bb3922744a4d5f07e555e3","after":null,"ref":"refs/heads/subtree-merge-test","pushedAt":"2024-05-16T00:15:04.000Z","pushType":"branch_deletion","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"}},{"before":"93d2068e5e8a1abe795d2b6245d36ac5110c35f3","after":"ce04d3834bc85b97b6cbcdec051e07a530a76c0d","ref":"refs/heads/main","pushedAt":"2024-05-16T00:15:03.000Z","pushType":"pr_merge","commitsCount":1,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Add test that tickles subtree merging. (#1488)\n\nAn important optimization in the formatter is that it will format a\r\nsubtree of the Piece tree separately and weave the result back into the\r\nparent Solution when possible. Part of that process is adding in any\r\nbound states, overflow characters, and costs determined in the subtree.\r\n\r\nSurprisingly, those rarely actually come into play in terms of affecting\r\nthe outermost winning solution. I'm not sure exactly why, but if you\r\njust merge in the subtree solution's text and discard the bound states,\r\noverflow, and cost... all the tests still pass.\r\n\r\nBut after testing on a large corpus, it turns out that in more complex\r\nreal-world examples, it *is* important to copy that data back over. So\r\nI grabbed an example whose formatting was affected and added this as a\r\nsort of regression test.","shortMessageHtmlLink":"Add test that tickles subtree merging. (#1488)"}},{"before":"5ddd749a7d24e201eb4b30defd02f5f6e8ce1879","after":"93d2068e5e8a1abe795d2b6245d36ac5110c35f3","ref":"refs/heads/main","pushedAt":"2024-05-15T20:50:54.000Z","pushType":"pr_merge","commitsCount":1,"pusher":{"login":"scheglov","name":"Konstantin Scheglov","path":"/scheglov","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/384794?s=80&v=4"},"commit":{"message":"Require analyzer 6.5.0 and stop using deprecated nodes. (#1490)\n\n* Require analyzer 6.5.0 and stop using deprecated nodes.\r\n\r\n* Update CHANGELOG.md\r\n\r\n* Replace SDK 3.0.0 with 3.4.0 in two places.","shortMessageHtmlLink":"Require analyzer 6.5.0 and stop using deprecated nodes. (#1490)"}},{"before":null,"after":"64f24944e8f88dd3227c0d0e345a03d5d1eb83e5","ref":"refs/heads/conditional-chain","pushedAt":"2024-05-15T19:55:59.000Z","pushType":"branch_creation","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Flatten chained conditionals.\n\nWhen testing the new formatter on a large corpus, I noticed the solver\nwould get stuck on large conditional chains because (unfortunately), we\ncan't separately format the else clause of a split conditional\nexpression. By merging long conditional chains into a single InfixPiece,\nwe can separately format all but the very last dangling else clause.\n\nWhile I was at it, I also put a hard cap in the number of solutions the\nsolver will try in case it still gets stuck. The old formatter has a\nsimilar limit. It's rare for real-world code to hit this limit in the\nnew solver, but it's better than getting totally stuck when it happens.","shortMessageHtmlLink":"Flatten chained conditionals."}},{"before":null,"after":"9d268da05099886058bb3922744a4d5f07e555e3","ref":"refs/heads/subtree-merge-test","pushedAt":"2024-05-15T18:44:16.000Z","pushType":"branch_creation","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Add test that tickles subtree merging.\n\nAn important optimization in the formatter is that it will format a\nsubtree of the Piece tree separately and weave the result back into the\nparent Solution when possible. Part of that process is adding in any\nbound states, overflow characters, and costs determined in the subtree.\n\nSurprisingly, those rarely actually come into play in terms of affecting\nthe outermost winning solution. I'm not sure exactly why, but if you\njust merge in the subtree solution's text and discard the bound states,\noverflow, and cost... all the tests still pass.\n\nBut after testing on a large corpus, it turns out that in more complex\nreal-world examples, it *is* important to copy that data back over. So\nI grabbed an example whose formatting was affected and added this as a\nsort of regression test.","shortMessageHtmlLink":"Add test that tickles subtree merging."}},{"before":"43634ec6e8620ec29675acb688c0319c61bf961a","after":null,"ref":"refs/heads/dependabot/pub/dart_flutter_team_lints-3.0.0","pushedAt":"2024-05-15T13:31:33.000Z","pushType":"branch_deletion","commitsCount":0,"pusher":{"login":"dependabot[bot]","name":null,"path":"/apps/dependabot","primaryAvatarUrl":"https://avatars.githubusercontent.com/in/29110?s=80&v=4"}},{"before":null,"after":"52db4dfc85f44bfef375354253ef024271c2fec7","ref":"refs/heads/dependabot/pub/dart_flutter_team_lints-3.1.0","pushedAt":"2024-05-15T13:31:29.000Z","pushType":"branch_creation","commitsCount":0,"pusher":{"login":"dependabot[bot]","name":null,"path":"/apps/dependabot","primaryAvatarUrl":"https://avatars.githubusercontent.com/in/29110?s=80&v=4"},"commit":{"message":"Bump dart_flutter_team_lints from 2.1.1 to 3.1.0\n\nBumps [dart_flutter_team_lints](https://github.com/dart-lang/ecosystem/tree/main/pkgs) from 2.1.1 to 3.1.0.\n- [Release notes](https://github.com/dart-lang/ecosystem/releases)\n- [Commits](https://github.com/dart-lang/ecosystem/commits/dart_flutter_team_lints-v3.1.0/pkgs)\n\n---\nupdated-dependencies:\n- dependency-name: dart_flutter_team_lints\n dependency-type: direct:production\n update-type: version-update:semver-major\n...\n\nSigned-off-by: dependabot[bot] ","shortMessageHtmlLink":"Bump dart_flutter_team_lints from 2.1.1 to 3.1.0"}},{"before":"4355c1c356697110c6a2b1a6473a559ba2747677","after":null,"ref":"refs/heads/aot-benchmark","pushedAt":"2024-05-15T03:04:05.000Z","pushType":"branch_deletion","commitsCount":0,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"}},{"before":"25dcdbbfed86bf49db9e46c37ce3743dc38c309f","after":"5ddd749a7d24e201eb4b30defd02f5f6e8ce1879","ref":"refs/heads/main","pushedAt":"2024-05-15T03:04:04.000Z","pushType":"pr_merge","commitsCount":1,"pusher":{"login":"munificent","name":"Bob Nystrom","path":"/munificent","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/46275?s=80&v=4"},"commit":{"message":"Support running benchmarks in AOT mode. (#1486)\n\nSupport running benchmarks in AOT mode.\r\n\r\nMany of the optimizations I'm trying have a relatively small\r\nimprovement. That can make it hard to tell if they are actually an\r\nimprovement if there is enough variance in the runs that they are lost\r\nin the noise.\r\n\r\nAOT builds don't need warm-up time and seem to have generally lower\r\nvariance than JIT runs. Also, they are how the shipped formatter is\r\nrun in `dart format`. So this adds support to the two benchmark scripts\r\nfor running themselves in AOT snapshot mode.\r\n\r\nI added support for this directly to the scripts instead of just\r\nmanually compiling and running them on the command line (which works\r\nfine) because this way it's less error-prone. I don't have to remember\r\nto re-compile the AOT snapshot and risk using an old build which is an\r\neasy mistake to make when the only behavioral difference is (possibly)\r\nperformance.\r\n\r\nAlso, tweaked the profiler output a little.","shortMessageHtmlLink":"Support running benchmarks in AOT mode. (#1486)"}}],"hasNextPage":true,"hasPreviousPage":false,"activityType":"all","actor":null,"timePeriod":"all","sort":"DESC","perPage":30,"cursor":"djE6ks8AAAAEUEmiVwA","startCursor":null,"endCursor":null}},"title":"Activity ยท dart-lang/dart_style"}