commit | 100db45075abdd66fd8788b205243e90ff0595df | [log] [tgz] |
---|---|---|
author | Bob Nystrom <rnystrom@google.com> | Thu Apr 10 07:50:32 2025 -0700 |
committer | GitHub <noreply@github.com> | Thu Apr 10 07:50:32 2025 -0700 |
tree | 18148571ad999426880d9371ca10750699aa0964 | |
parent | c7f6131466d2de2109b03288557176ef45d6f002 [diff] |
Language version all of the formatting changes since Dart 3.7. (#1707) Language version all of the formatting changes since Dart 3.7. The new formatter launch was pretty rocky, but one thing I think helped mitigate that was that the formatting style was gated on language version. Users could install the new SDK and their code wouldn't be spontaneously reformatted under them until they deliberately updated their SDK constraint to opt in to the new language version. This PR applies that same logic to the changes we've made since Dart 3.7. Any source file at language version 3.7 is formatted (almost, see below) identically to how it would be formatted with the SDK as it shipped in 3.7. Source files at language version 3.8 or higher get all of the new style changes I've landed since then. This increases the complexity of the formatter codebase a decent amount, but it's mostly just a lot of "if 3.7 do X else do Y". Tedious but not intractable. I expect to continue to language version changes like this going forward. So after Dart 3.8 ships, whatever formatter style is in that version will be locked down and style changes after that will have to support 3.7 and 3.8 fallback behavior. I expect there to be much less style churn going forward, so it's probably manageable. In cases where there are *bugs* (i.e. the formatter produces invalid syntax or rearranges code), those won't be language versioned. But most other style changes will be. It's important to make sure we don't regress and accidentally affect the formatting of older language versioned files when making changes to the formatter. To avoid that, this also expands testing to be multi-versioned. Every test is now run on multiple versions and for cases where the style differs between versions, there are different expectations for each. Fortunately, the tests run very fast, so it doesn't slow down things more than a couple of seconds. In addition to running through the formatter's own test suite and regression tests, I also tested this on a giant corpus of pub packages. I formatted them all using the shipped Dart 3.7 formatter, then using this PR with `--language-version=3.7`. Of the 89,468 files, 7 had differences. They all relate to a single weird corner case around giant multiline strings containing multiline string interpolation expressions where the formatting is slightly different. #1706 helps that somewhat -- there were about dozen diffs before -- but doesn't totally eliminate it. I think it's close enough to be tolerable.
The dart_style package defines an opinionated automated formatter for Dart code. It replaces the whitespace in your program with what it deems to be the best formatting for it. It also makes minor changes around non-semantic punctuation like trailing commas and brackets in parameter lists.
The resulting code should follow the Dart style guide and look nice to most human readers, most of the time.
The formatter handles indentation, inline whitespace, and (by far the most difficult) intelligent line wrapping. It has no problems with nested collections, function expressions, long argument lists, or otherwise tricky code.
The formatter turns code like this:
// BEFORE formatting process = await Process.start(path.join(p.pubCacheBinPath,Platform.isWindows ?'${command.first}.bat':command.first,),[...command.sublist(1),'web:0', // Allow for binding to a random available port. ],workingDirectory:workingDir,environment:{'PUB_CACHE':p.pubCachePath,'PATH': path.dirname(Platform.resolvedExecutable)+(Platform.isWindows?';':':')+ Platform.environment['PATH']!,},);
into:
// AFTER formatting process = await Process.start( path.join( p.pubCacheBinPath, Platform.isWindows ? '${command.first}.bat' : command.first, ), [ ...command.sublist(1), 'web:0', // Allow for binding to a random available port. ], workingDirectory: workingDir, environment: { 'PUB_CACHE': p.pubCachePath, 'PATH': path.dirname(Platform.resolvedExecutable) + (Platform.isWindows ? ';' : ':') + Platform.environment['PATH']!, }, );
The formatter will never break your code—you can safely invoke it automatically from build and presubmit scripts.
The formatter is part of the unified dart
developer tool included in the Dart SDK, so most users run it directly from there using dart format
.
IDEs and editors that support Dart usually provide easy ways to run the formatter. For example, in Visual Studio Code, formatting Dart code will use the dart_style formatter by default. Most users have it set to reformat every time they save a file.
Here's a simple example of using the formatter on the command line:
$ dart format my_file.dart
This command formats the my_file.dart
file and writes the result back to the same file.
The dart format
command takes a list of paths, which can point to directories or files. If the path is a directory, it processes every .dart
file in that directory and all of its subdirectories.
By default, dart format
formats each file and writes the result back to the same files. If you pass --output show
, it prints the formatted code to stdout and doesn't modify the files.
If you want to use the formatter in something like a presubmit script or commit hook, you can pass flags to omit writing formatting changes to disk and to update the exit code to indicate success/failure:
$ dart format --output=none --set-exit-if-changed .
The `dart_style package exposes a simple library API for formatting code. Basic usage looks like this:
import 'package:dart_style/dart_style.dart'; main() { final formatter = DartFormatter(); try { print(formatter.format(""" library an_entire_compilation_unit; class SomeClass {} """)); print(formatter.formatStatement("aSingle(statement);")); } on FormatterException catch (ex) { print(ex); } }
Before sending an email, see if you are asking a frequently asked question.
Before filing a bug, or if you want to understand how work on the formatter is managed, see how we track issues.