diff --git a/CHANGELOG.md b/CHANGELOG.md index bbdd1347..20d8f0da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ ## 2.3.3-dev -* Remove support for `inline class` since that syntax has changed. * Fix regression in splitting type annotations with library prefixes (#1249). +* Remove support for `inline class` since that syntax has changed. +* Add `--enable-experiment` command-line option to enable language experiments. + The library API also supports this with `DartFormatter.experimentFlags`. ## 2.3.2 diff --git a/lib/src/cli/format_command.dart b/lib/src/cli/format_command.dart index a05e240b..36092c66 100644 --- a/lib/src/cli/format_command.dart +++ b/lib/src/cli/format_command.dart @@ -122,6 +122,8 @@ class FormatCommand extends Command { var followLinks = argResults['follow-links']; var setExitIfChanged = argResults['set-exit-if-changed'] as bool; + var experimentFlags = argResults['enable-experiment'] as List; + // If stdin isn't connected to a pipe, then the user is not passing // anything to stdin, so let them know they made a mistake. if (argResults.rest.isEmpty && stdin.hasTerminal) { @@ -145,7 +147,8 @@ class FormatCommand extends Command { show: show, output: output, summary: summary, - setExitIfChanged: setExitIfChanged); + setExitIfChanged: setExitIfChanged, + experimentFlags: experimentFlags); if (argResults.rest.isEmpty) { await formatStdin(options, selection, stdinName); diff --git a/lib/src/cli/formatter_options.dart b/lib/src/cli/formatter_options.dart index b197cc92..d35e11d0 100644 --- a/lib/src/cli/formatter_options.dart +++ b/lib/src/cli/formatter_options.dart @@ -39,6 +39,11 @@ class FormatterOptions { /// Sets the exit code to 1 if any changes are made. final bool setExitIfChanged; + /// Flags to enable experimental language features. + /// + /// See dart.dev/go/experiments for details. + final List experimentFlags; + FormatterOptions( {this.indent = 0, this.pageWidth = 80, @@ -47,8 +52,10 @@ class FormatterOptions { this.show = Show.changed, this.output = Output.write, this.summary = Summary.none, - this.setExitIfChanged = false}) - : fixes = [...?fixes]; + this.setExitIfChanged = false, + List? experimentFlags}) + : fixes = [...?fixes], + experimentFlags = [...?experimentFlags]; /// Called when [file] is about to be formatted. /// diff --git a/lib/src/cli/options.dart b/lib/src/cli/options.dart index 3f4f48f5..b59ea9e2 100644 --- a/lib/src/cli/options.dart +++ b/lib/src/cli/options.dart @@ -101,6 +101,7 @@ void defineOptions(ArgParser parser, help: 'Produce machine-readable JSON output.', hide: !verbose); } + parser.addFlag('follow-links', negatable: false, help: 'Follow links to files and directories.\n' @@ -108,6 +109,10 @@ void defineOptions(ArgParser parser, hide: !verbose); parser.addFlag('version', negatable: false, help: 'Show dart_style version.', hide: !verbose); + parser.addMultiOption('enable-experiment', + help: 'Enable one or more experimental features.\n' + 'See dart.dev/go/experiments.', + hide: !verbose); if (verbose) parser.addSeparator('Options when formatting from stdin:'); diff --git a/lib/src/dart_formatter.dart b/lib/src/dart_formatter.dart index 474529b2..19c951b2 100644 --- a/lib/src/dart_formatter.dart +++ b/lib/src/dart_formatter.dart @@ -38,6 +38,11 @@ class DartFormatter { final Set fixes; + /// Flags to enable experimental language features. + /// + /// See dart.dev/go/experiments for details. + final List experimentFlags; + /// Creates a new formatter for Dart code. /// /// If [lineEnding] is given, that will be used for any newlines in the @@ -49,10 +54,15 @@ class DartFormatter { /// /// While formatting, also applies any of the given [fixes]. DartFormatter( - {this.lineEnding, int? pageWidth, int? indent, Iterable? fixes}) + {this.lineEnding, + int? pageWidth, + int? indent, + Iterable? fixes, + List? experimentFlags}) : pageWidth = pageWidth ?? 80, indent = indent ?? 0, - fixes = {...?fixes}; + fixes = {...?fixes}, + experimentFlags = [...?experimentFlags]; /// Formats the given [source] string containing an entire Dart compilation /// unit. @@ -200,19 +210,9 @@ class DartFormatter { // happens to parse without error, then we use that result instead. ParseStringResult _parse(String source, String? uri, {required bool patterns}) { - // Enable all features that are enabled by default in the current analyzer - // version. + var version = patterns ? Version(3, 0, 0) : Version(2, 19, 0); var featureSet = FeatureSet.fromEnableFlags2( - sdkLanguageVersion: Version(2, 19, 0), - flags: [ - 'inline-class', - 'class-modifiers', - if (patterns) 'patterns', - 'records', - 'sealed-class', - 'unnamed-libraries', - ], - ); + sdkLanguageVersion: version, flags: experimentFlags); return parseString( content: source, diff --git a/lib/src/io.dart b/lib/src/io.dart index 1c6d5e9b..f9ef254e 100644 --- a/lib/src/io.dart +++ b/lib/src/io.dart @@ -29,7 +29,8 @@ Future formatStdin( var formatter = DartFormatter( indent: options.indent, pageWidth: options.pageWidth, - fixes: options.fixes); + fixes: options.fixes, + experimentFlags: options.experimentFlags); try { options.beforeFile(null, name); var source = SourceCode(input.toString(), @@ -138,7 +139,8 @@ bool processFile(FormatterOptions options, File file, {String? displayPath}) { var formatter = DartFormatter( indent: options.indent, pageWidth: options.pageWidth, - fixes: options.fixes); + fixes: options.fixes, + experimentFlags: options.experimentFlags); try { var source = SourceCode(file.readAsStringSync(), uri: file.path); options.beforeFile(file, displayPath); diff --git a/pubspec.yaml b/pubspec.yaml index e114401f..e281a08b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,7 +9,7 @@ environment: sdk: "^3.0.0" dependencies: - analyzer: '>=5.12.0 <7.0.0' + analyzer: '^6.2.0' args: ">=1.0.0 <3.0.0" path: ^1.0.0 pub_semver: ">=1.4.4 <3.0.0" @@ -22,7 +22,7 @@ dev_dependencies: # and publishing to npm hasn't been used in a while. # node_preamble: ^1.0.0 lints: ^2.0.0 - test: ^1.16.8 + test: ^1.24.6 test_descriptor: ^2.0.0 test_process: ^2.0.0 yaml: ">=2.0.0 <4.0.0" diff --git a/test/command_test.dart b/test/command_test.dart index 5e7ca614..5b841f9a 100644 --- a/test/command_test.dart +++ b/test/command_test.dart @@ -454,6 +454,31 @@ void main() { }); }); + group('--enable-experiment', () { + test('passes experiment flags to parser', () async { + var process = + await runCommand(['--enable-experiment=test-experiment,variance']); + process.stdin.writeln('class Writer {}'); + await process.stdin.close(); + + // The formatter doesn't actually support formatting variance annotations, + // but we want to test that the experiment flags are passed all the way + // to the parser, so just test that it parses the variance annotation + // without errors and then fails to format. + expect(await process.stderr.next, + 'Hit a bug in the formatter when formatting stdin.'); + expect(await process.stderr.next, + 'Please report at: github.com/dart-lang/dart_style/issues'); + expect(await process.stderr.next, + 'The formatter produced unexpected output. Input was:'); + expect(await process.stderr.next, 'class Writer {}'); + expect(await process.stderr.next, ''); + expect(await process.stderr.next, 'Which formatted to:'); + expect(await process.stderr.next, 'class Writer {}'); + await process.shouldExit(70); + }); + }); + group('with no paths', () { test('errors on --output=write', () async { var process = await runCommand(['--output=write']);