Reorganize the old command line code to get ready for the new CLI.
The only functionality change is that "--verbose" is no longer a
supported option. It was only used for help, and this just always shows
all the help now.
The other changes mostly move code out of bin/format.dart so that
relevant pieces can be shared with the new CLI.
diff --git a/bin/format.dart b/bin/format.dart
index fdca0f8..834dddd 100644
--- a/bin/format.dart
+++ b/bin/format.dart
@@ -2,87 +2,18 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'dart:convert';
import 'dart:io';
import 'package:args/args.dart';
-import 'package:dart_style/src/dart_formatter.dart';
-import 'package:dart_style/src/exceptions.dart';
-import 'package:dart_style/src/formatter_options.dart';
+import 'package:dart_style/src/cli/formatter_options.dart';
+import 'package:dart_style/src/cli/options.dart';
import 'package:dart_style/src/io.dart';
-import 'package:dart_style/src/source_code.dart';
import 'package:dart_style/src/style_fix.dart';
-// Note: The following line of code is modified by tool/grind.dart.
-const version = '1.3.3';
-
void main(List<String> args) {
var parser = ArgParser(allowTrailingOptions: true);
- var verbose = args.contains('-v') || args.contains('--verbose');
- var hide = !verbose;
-
- parser.addSeparator('Common options:');
- parser.addFlag('help',
- abbr: 'h',
- negatable: false,
- help:
- 'Shows this usage information. Add --verbose to show hidden options.');
- parser.addFlag('version',
- negatable: false, help: 'Shows version information.');
- parser.addFlag('verbose',
- abbr: 'v', negatable: false, help: 'Verbose output.');
- parser.addOption('line-length',
- abbr: 'l', help: 'Wrap lines longer than this.', defaultsTo: '80');
- parser.addFlag('overwrite',
- abbr: 'w',
- negatable: false,
- help: 'Overwrite input files with formatted output.');
- parser.addFlag('dry-run',
- abbr: 'n',
- negatable: false,
- help: 'Show which files would be modified but make no changes.');
-
- parser.addSeparator('Non-whitespace fixes (off by default):');
- parser.addFlag('fix', negatable: false, help: 'Apply all style fixes.');
-
- for (var fix in StyleFix.all) {
- // TODO(rnystrom): Allow negating this if used in concert with "--fix"?
- parser.addFlag('fix-${fix.name}', negatable: false, help: fix.description);
- }
-
- if (verbose) {
- parser.addSeparator('Other options:');
- }
- parser.addOption('indent',
- abbr: 'i',
- help: 'Spaces of leading indentation.',
- defaultsTo: '0',
- hide: hide);
- parser.addFlag('machine',
- abbr: 'm',
- negatable: false,
- help: 'Produce machine-readable JSON output.',
- hide: hide);
- parser.addFlag('set-exit-if-changed',
- negatable: false,
- help: 'Return exit code 1 if there are any formatting changes.',
- hide: hide);
- parser.addFlag('follow-links',
- negatable: false,
- help: 'Follow links to files and directories.\n'
- 'If unset, links will be ignored.',
- hide: hide);
- parser.addOption('preserve',
- help: 'Selection to preserve, formatted as "start:length".', hide: hide);
- parser.addOption('stdin-name',
- help: 'The path name to show when an error occurs in source read from '
- 'stdin.',
- defaultsTo: '<stdin>',
- hide: hide);
-
- parser.addFlag('profile', negatable: false, hide: true);
- parser.addFlag('transform', abbr: 't', negatable: false, hide: true);
+ defineOptions(parser);
ArgResults argResults;
try {
@@ -97,7 +28,7 @@
}
if (argResults['version']) {
- print(version);
+ print(dartStyleVersion);
return;
}
@@ -108,7 +39,7 @@
}
try {
- selection = parseSelection(argResults['preserve']);
+ selection = parseSelection(argResults, 'preserve');
} on FormatException catch (_) {
usageError(
parser,
@@ -210,79 +141,6 @@
}
}
-List<int> parseSelection(String selection) {
- if (selection == null) return null;
-
- var coordinates = selection.split(':');
- if (coordinates.length != 2) {
- throw FormatException(
- 'Selection should be a colon-separated pair of integers, "123:45".');
- }
-
- return coordinates.map((coord) => coord.trim()).map(int.parse).toList();
-}
-
-/// Reads input from stdin until it's closed, and the formats it.
-void formatStdin(FormatterOptions options, List<int> selection, String name) {
- var selectionStart = 0;
- var selectionLength = 0;
-
- if (selection != null) {
- selectionStart = selection[0];
- selectionLength = selection[1];
- }
-
- var input = StringBuffer();
- stdin.transform(Utf8Decoder()).listen(input.write, onDone: () {
- var formatter = DartFormatter(
- indent: options.indent,
- pageWidth: options.pageWidth,
- fixes: options.fixes);
- try {
- options.reporter.beforeFile(null, name);
- var source = SourceCode(input.toString(),
- uri: name,
- selectionStart: selectionStart,
- selectionLength: selectionLength);
- var output = formatter.formatSource(source);
- options.reporter
- .afterFile(null, name, output, changed: source.text != output.text);
- return;
- } on FormatterException catch (err) {
- stderr.writeln(err.message());
- exitCode = 65; // sysexits.h: EX_DATAERR
- } catch (err, stack) {
- stderr.writeln('''Hit a bug in the formatter when formatting stdin.
-Please report at: github.com/dart-lang/dart_style/issues
-$err
-$stack''');
- exitCode = 70; // sysexits.h: EX_SOFTWARE
- }
- });
-}
-
-/// Formats all of the files and directories given by [paths].
-void formatPaths(FormatterOptions options, List<String> paths) {
- for (var path in paths) {
- var directory = Directory(path);
- if (directory.existsSync()) {
- if (!processDirectory(options, directory)) {
- exitCode = 65;
- }
- continue;
- }
-
- var file = File(path);
- if (file.existsSync()) {
- if (!processFile(options, file)) {
- exitCode = 65;
- }
- } else {
- stderr.writeln('No file or directory found at "$path".');
- }
- }
-}
-
/// Prints [error] and usage help then exits with exit code 64.
void usageError(ArgParser parser, String error) {
printUsage(parser, error);
diff --git a/lib/src/formatter_options.dart b/lib/src/cli/formatter_options.dart
similarity index 97%
rename from lib/src/formatter_options.dart
rename to lib/src/cli/formatter_options.dart
index b4073a6..6250e88 100644
--- a/lib/src/formatter_options.dart
+++ b/lib/src/cli/formatter_options.dart
@@ -7,8 +7,11 @@
import 'dart:convert';
import 'dart:io';
-import 'source_code.dart';
-import 'style_fix.dart';
+import '../source_code.dart';
+import '../style_fix.dart';
+
+// Note: The following line of code is modified by tool/grind.dart.
+const dartStyleVersion = '1.3.3';
/// Global options that affect how the formatter produces and uses its outputs.
class FormatterOptions {
diff --git a/lib/src/cli/options.dart b/lib/src/cli/options.dart
new file mode 100644
index 0000000..455a68b
--- /dev/null
+++ b/lib/src/cli/options.dart
@@ -0,0 +1,89 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+import 'package:args/args.dart';
+
+import '../style_fix.dart';
+
+void defineOptions(ArgParser parser) {
+ parser.addSeparator('Common options:');
+
+ // Command implicitly adds "--help", so we only need to manually add it for
+ // the old CLI.
+ parser.addFlag('help',
+ abbr: 'h', negatable: false, help: 'Shows this usage information.');
+
+ parser.addFlag('overwrite',
+ abbr: 'w',
+ negatable: false,
+ help: 'Overwrite input files with formatted output.');
+ parser.addFlag('dry-run',
+ abbr: 'n',
+ negatable: false,
+ help: 'Show which files would be modified but make no changes.');
+
+ parser.addSeparator('Non-whitespace fixes (off by default):');
+ parser.addFlag('fix', negatable: false, help: 'Apply all style fixes.');
+
+ for (var fix in StyleFix.all) {
+ // TODO(rnystrom): Allow negating this if used in concert with "--fix"?
+ parser.addFlag('fix-${fix.name}', negatable: false, help: fix.description);
+ }
+
+ parser.addSeparator('Other options:');
+
+ parser.addOption('line-length',
+ abbr: 'l', help: 'Wrap lines longer than this.', defaultsTo: '80');
+ parser.addOption('indent',
+ abbr: 'i', help: 'Spaces of leading indentation.', defaultsTo: '0');
+ parser.addFlag('machine',
+ abbr: 'm',
+ negatable: false,
+ help: 'Produce machine-readable JSON output.');
+ parser.addFlag('set-exit-if-changed',
+ negatable: false,
+ help: 'Return exit code 1 if there are any formatting changes.');
+ parser.addFlag('follow-links',
+ negatable: false,
+ help: 'Follow links to files and directories.\n'
+ 'If unset, links will be ignored.');
+ parser.addFlag('version',
+ negatable: false, help: 'Show version information.');
+
+ parser.addSeparator('Options when formatting from stdin:');
+
+ parser.addOption('preserve',
+ help: 'Selection to preserve formatted as "start:length".');
+ parser.addOption('stdin-name',
+ help: 'The path name to show when an error occurs.',
+ defaultsTo: '<stdin>');
+ parser.addFlag('profile', negatable: false, hide: true);
+
+ // Ancient no longer used flag.
+ parser.addFlag('transform', abbr: 't', negatable: false, hide: true);
+}
+
+List<int> parseSelection(ArgResults argResults, String optionName) {
+ var option = argResults[optionName];
+ if (option == null) return null;
+
+ // Can only preserve a selection when parsing from stdin.
+ if (argResults.rest.isNotEmpty) {
+ throw FormatException(
+ 'Can only use --$optionName when reading from stdin.');
+ }
+
+ try {
+ var coordinates = option.split(':');
+ if (coordinates.length != 2) {
+ throw FormatException(
+ 'Selection should be a colon-separated pair of integers, "123:45".');
+ }
+
+ return coordinates.map<int>((coord) => int.parse(coord.trim())).toList();
+ } on FormatException catch (_) {
+ throw FormatException(
+ '--$optionName must be a colon-separated pair of integers, was '
+ '"${argResults[optionName]}".');
+ }
+}
diff --git a/lib/src/io.dart b/lib/src/io.dart
index a69e500..c6a70c4 100644
--- a/lib/src/io.dart
+++ b/lib/src/io.dart
@@ -4,15 +4,77 @@
library dart_style.src.io;
+import 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as p;
+import 'cli/formatter_options.dart';
import 'dart_formatter.dart';
import 'exceptions.dart';
-import 'formatter_options.dart';
import 'source_code.dart';
+/// Reads input from stdin until it's closed, and the formats it.
+void formatStdin(FormatterOptions options, List<int> selection, String name) {
+ var selectionStart = 0;
+ var selectionLength = 0;
+
+ if (selection != null) {
+ selectionStart = selection[0];
+ selectionLength = selection[1];
+ }
+
+ var input = StringBuffer();
+ stdin.transform(Utf8Decoder()).listen(input.write, onDone: () {
+ var formatter = DartFormatter(
+ indent: options.indent,
+ pageWidth: options.pageWidth,
+ fixes: options.fixes);
+ try {
+ options.reporter.beforeFile(null, name);
+ var source = SourceCode(input.toString(),
+ uri: name,
+ selectionStart: selectionStart,
+ selectionLength: selectionLength);
+ var output = formatter.formatSource(source);
+ options.reporter
+ .afterFile(null, name, output, changed: source.text != output.text);
+ return;
+ } on FormatterException catch (err) {
+ stderr.writeln(err.message());
+ exitCode = 65; // sysexits.h: EX_DATAERR
+ } catch (err, stack) {
+ stderr.writeln('''Hit a bug in the formatter when formatting stdin.
+Please report at: github.com/dart-lang/dart_style/issues
+$err
+$stack''');
+ exitCode = 70; // sysexits.h: EX_SOFTWARE
+ }
+ });
+}
+
+/// Formats all of the files and directories given by [paths].
+void formatPaths(FormatterOptions options, List<String> paths) {
+ for (var path in paths) {
+ var directory = Directory(path);
+ if (directory.existsSync()) {
+ if (!processDirectory(options, directory)) {
+ exitCode = 65;
+ }
+ continue;
+ }
+
+ var file = File(path);
+ if (file.existsSync()) {
+ if (!processFile(options, file)) {
+ exitCode = 65;
+ }
+ } else {
+ stderr.writeln('No file or directory found at "$path".');
+ }
+ }
+}
+
/// Runs the formatter on every .dart file in [path] (and its subdirectories),
/// and replaces them with their formatted output.
///
@@ -24,8 +86,11 @@
var success = true;
var shownHiddenPaths = <String>{};
- for (var entry in directory.listSync(
- recursive: true, followLinks: options.followLinks)) {
+ var entries =
+ directory.listSync(recursive: true, followLinks: options.followLinks);
+ entries.sort((a, b) => a.path.compareTo(b.path));
+
+ for (var entry in entries) {
var relative = p.relative(entry.path, from: directory.path);
if (entry is Link) {
diff --git a/test/command_line_test.dart b/test/command_line_test.dart
index ce17327..9bbaf07 100644
--- a/test/command_line_test.dart
+++ b/test/command_line_test.dart
@@ -13,11 +13,26 @@
import 'utils.dart';
void main() {
- test('exits with 0 on success', () async {
- await d.dir('code', [d.file('a.dart', unformattedSource)]).create();
+ test('formats a directory', () async {
+ await d.dir('code', [
+ d.file('a.dart', unformattedSource),
+ d.file('b.dart', formattedSource),
+ d.file('c.dart', unformattedSource)
+ ]).create();
var process = await runFormatterOnDir();
+ await expectLater(
+ process.stdout, emits(startsWith('Formatting directory')));
+
+ // Prints the formatting result.
+ await expectLater(process.stdout, emits(formattedOutput));
+ await expectLater(process.stdout, emits(formattedOutput));
+ await expectLater(process.stdout, emits(formattedOutput));
await process.shouldExit(0);
+
+ // Does not overwrite by default.
+ await d.dir('code', [d.file('a.dart', unformattedSource)]).validate();
+ await d.dir('code', [d.file('c.dart', unformattedSource)]).validate();
});
test('exits with 64 on a command line argument error', () async {
@@ -77,19 +92,6 @@
await expectLater(process.stdout, emits(''));
await expectLater(process.stdout,
emits('Usage: dartfmt [options...] [files or directories...]'));
- await expectLater(process.stdout, neverEmits('Other options:'));
- await process.shouldExit(0);
- });
-
- test('--help --verbose', () async {
- var process = await runFormatter(['--help', '--verbose']);
- await expectLater(
- process.stdout, emits('Idiomatically formats Dart source code.'));
- await expectLater(process.stdout, emits(''));
- await expectLater(process.stdout,
- emits('Usage: dartfmt [options...] [files or directories...]'));
- await expectLater(process.stdout, emitsThrough('Other options:'));
- await expectLater(process.stdout, emits(startsWith('-i, --indent')));
await process.shouldExit(0);
});
@@ -222,7 +224,7 @@
var process = await runFormatter(['--indent', 'notanum']);
await process.shouldExit(64);
- process = await runFormatter(['--preserve', '-4']);
+ process = await runFormatter(['--indent', '-4']);
await process.shouldExit(64);
});
});
@@ -326,7 +328,7 @@
await process.stdin.close();
// No trailing newline at the end.
- expect(await process.stdout.next, formattedSource.trimRight());
+ expect(await process.stdout.next, formattedOutput);
await process.shouldExit(0);
});
diff --git a/test/io_test.dart b/test/io_test.dart
index da5839c..7f4ce8b 100644
--- a/test/io_test.dart
+++ b/test/io_test.dart
@@ -12,7 +12,7 @@
import 'package:test_descriptor/test_descriptor.dart' as d;
import 'package:test/test.dart';
-import 'package:dart_style/src/formatter_options.dart';
+import 'package:dart_style/src/cli/formatter_options.dart';
import 'utils.dart';
diff --git a/test/utils.dart b/test/utils.dart
index 946a965..4f99ccf 100644
--- a/test/utils.dart
+++ b/test/utils.dart
@@ -18,6 +18,10 @@
const unformattedSource = 'void main() => print("hello") ;';
const formattedSource = 'void main() => print("hello");\n';
+/// The same as formatted source but without a trailing newline because
+/// [TestProcess] filters those when it strips command line output into lines.
+const formattedOutput = 'void main() => print("hello");';
+
final _indentPattern = RegExp(r'\(indent (\d+)\)');
final _fixPattern = RegExp(r'\(fix ([a-x-]+)\)');
diff --git a/tool/grind.dart b/tool/grind.dart
index b6b6ee4..fc2f019 100644
--- a/tool/grind.dart
+++ b/tool/grind.dart
@@ -114,11 +114,12 @@
pubspec = pubspec.replaceAll(_versionPattern, 'version: $bumped');
pubspecFile.writeAsStringSync(pubspec);
- // Update the version constant in bin/format.dart.
- var binFormatFile = getFile('bin/format.dart');
- var binFormat = binFormatFile.readAsStringSync().replaceAll(
- RegExp(r'const version = "[^"]+";'), 'const version = "$bumped";');
- binFormatFile.writeAsStringSync(binFormat);
+ // Update the version constant in formatter_options.dart.
+ var versionFile = getFile('lib/src/cli/formatter_options.dart');
+ var versionSource = versionFile.readAsStringSync().replaceAll(
+ RegExp(r'const dartStyleVersion = "[^"]+";'),
+ 'const dartStyleVersion = "$bumped";');
+ versionFile.writeAsStringSync(versionSource);
// Update the version in the CHANGELOG.
var changelogFile = getFile('CHANGELOG.md');