@@ -25,6 +25,22 @@ import 'string_compare.dart' as string_compare;
25
25
26
26
/// Dart source code formatter.
27
27
class DartFormatter {
28
+ /// The latest Dart language version that can be parsed and formatted by this
29
+ /// version of the formatter.
30
+ static final latestLanguageVersion = Version (3 , 3 , 0 );
31
+
32
+ /// The highest Dart language version without support for patterns.
33
+ static final _lastNonPatternsVersion = Version (2 , 19 , 0 );
34
+
35
+ /// The Dart language version that formatted code should be parsed as.
36
+ ///
37
+ /// Note that a `// @dart=` comment inside the code overrides this.
38
+ final Version languageVersion;
39
+
40
+ /// Whether the user passed in a non-`null` language version.
41
+ // TODO(rnystrom): Remove this when the language version is required.
42
+ final bool _omittedLanguageVersion;
43
+
28
44
/// The string that newlines should use.
29
45
///
30
46
/// If not explicitly provided, this is inferred from the source text. If the
@@ -45,7 +61,15 @@ class DartFormatter {
45
61
/// See dart.dev/go/experiments for details.
46
62
final List <String > experimentFlags;
47
63
48
- /// Creates a new formatter for Dart code.
64
+ /// Creates a new formatter for Dart code at [languageVersion] .
65
+ ///
66
+ /// If [languageVersion] is omitted, then it defaults to
67
+ /// [latestLanguageVersion] . In a future major release of dart_style, the
68
+ /// language version will affect the applied formatting style. At that point,
69
+ /// this parameter will become required so that the applied style doesn't
70
+ /// change unexpectedly. It is optional now so that users can migrate to
71
+ /// versions of dart_style that accept this parameter and be ready for the
72
+ /// major version when it's released.
49
73
///
50
74
/// If [lineEnding] is given, that will be used for any newlines in the
51
75
/// output. Otherwise, the line separator will be inferred from the line
@@ -56,12 +80,15 @@ class DartFormatter {
56
80
///
57
81
/// While formatting, also applies any of the given [fixes] .
58
82
DartFormatter (
59
- {this .lineEnding,
83
+ {Version ? languageVersion,
84
+ this .lineEnding,
60
85
int ? pageWidth,
61
86
int ? indent,
62
87
Iterable <StyleFix >? fixes,
63
88
List <String >? experimentFlags})
64
- : pageWidth = pageWidth ?? 80 ,
89
+ : languageVersion = languageVersion ?? latestLanguageVersion,
90
+ _omittedLanguageVersion = languageVersion == null ,
91
+ pageWidth = pageWidth ?? 80 ,
65
92
indent = indent ?? 0 ,
66
93
fixes = {...? fixes},
67
94
experimentFlags = [...? experimentFlags];
@@ -72,18 +99,15 @@ class DartFormatter {
72
99
/// If [uri] is given, it is a [String] or [Uri] used to identify the file
73
100
/// being formatted in error messages.
74
101
String format (String source, {Object ? uri}) {
75
- if (uri == null ) {
76
- // Do nothing.
77
- } else if (uri is Uri ) {
78
- uri = uri.toString ();
79
- } else if (uri is String ) {
80
- // Do nothing.
81
- } else {
82
- throw ArgumentError ('uri must be `null`, a Uri, or a String.' );
83
- }
102
+ var uriString = switch (uri) {
103
+ null => null ,
104
+ Uri () => uri.toString (),
105
+ String () => uri,
106
+ _ => throw ArgumentError ('uri must be `null`, a Uri, or a String.' ),
107
+ };
84
108
85
109
return formatSource (
86
- SourceCode (source, uri: uri as String ? , isCompilationUnit: true ))
110
+ SourceCode (source, uri: uriString , isCompilationUnit: true ))
87
111
.text;
88
112
}
89
113
@@ -118,13 +142,26 @@ class DartFormatter {
118
142
}
119
143
120
144
// Parse it.
121
- var parseResult = _parse (text, source.uri, patterns: true );
122
-
123
- // If we couldn't parse it with patterns enabled, it may be because of
124
- // one of the breaking syntax changes to switch cases. Try parsing it
125
- // again without patterns.
126
- if (parseResult.errors.isNotEmpty) {
127
- var withoutPatternsResult = _parse (text, source.uri, patterns: false );
145
+ var parseResult = _parse (text, source.uri, languageVersion);
146
+
147
+ // If we couldn't parse it, and the language version supports patterns, it
148
+ // may be because of the breaking syntax changes to switch cases. Try
149
+ // parsing it again without pattern support.
150
+ // TODO(rnystrom): This is a pretty big hack. Before Dart 3.0, every
151
+ // language version was a strict syntactic superset of all previous
152
+ // versions. When patterns were added, a small number of switch cases
153
+ // became syntax errors.
154
+ //
155
+ // For most of its history, the formatter simply parsed every file at the
156
+ // latest language version without having to detect each file's actual
157
+ // version. We are moving towards requiring the language version when
158
+ // formatting, but for now, try to degrade gracefully if the user omits the
159
+ // version.
160
+ //
161
+ // Remove this when the languageVersion constructor parameter is required.
162
+ if (_omittedLanguageVersion && parseResult.errors.isNotEmpty) {
163
+ var withoutPatternsResult =
164
+ _parse (text, source.uri, _lastNonPatternsVersion);
128
165
129
166
// If we succeeded this time, use this parse instead.
130
167
if (withoutPatternsResult.errors.isEmpty) {
@@ -197,31 +234,8 @@ class DartFormatter {
197
234
return output;
198
235
}
199
236
200
- /// Parse [source] from [uri] .
201
- ///
202
- /// If [patterns] is `true` , the parse at the latest language version
203
- /// which supports patterns and treats switch cases as patterns. If `false` ,
204
- /// then parses using an older language version where switch cases are
205
- /// constant expressions.
206
- ///
207
- // TODO(rnystrom): This is a pretty big hack. Up until now, every language
208
- // version was a strict syntactic superset of all previous versions. That let
209
- // the formatter parse every file at the latest language version without
210
- // having to detect each file's actual version, which requires digging around
211
- // in the file system for package configs and looking for "@dart" comments in
212
- // files. It also means the library API that parses arbitrary strings doesn't
213
- // have to worry about what version the code should be interpreted as.
214
- //
215
- // But with patterns, a small number of switch cases are no longer
216
- // syntactically valid. Breakage from this is very rare. Instead of adding
217
- // the machinery to detect language versions (which is likely to be slow and
218
- // brittle), we just try parsing everything with patterns enabled. When a
219
- // parse error occurs, we try parsing it again with pattern disabled. If that
220
- // happens to parse without error, then we use that result instead.
221
- ParseStringResult _parse (String source, String ? uri,
222
- {required bool patterns}) {
223
- var version = patterns ? Version (3 , 3 , 0 ) : Version (2 , 19 , 0 );
224
-
237
+ /// Parse [source] from [uri] at language [version] .
238
+ ParseStringResult _parse (String source, String ? uri, Version version) {
225
239
// Don't pass the formatter's own experiment flag to the parser.
226
240
var experiments = experimentFlags.toList ();
227
241
experiments.remove (tallStyleExperimentFlag);
0 commit comments