Add a command-line option to set the exit code on a formatting change.
Fix #365.
R=kevmoo@google.com
Review URL: https://codereview.chromium.org//2333373003 .
diff --git a/CHANGELOG.md b/CHANGELOG.md
index da338c4..0ce8399 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
* Handle metadata annotations before parameters with trailing commas (#520).
* Always split enum declarations if they end in a trailing comma (#529).
+* Add `--set-exit-if-changed` to set the exit code on a change (#365).
# 0.2.9
diff --git a/bin/format.dart b/bin/format.dart
index 72182bf..a8a00e2 100644
--- a/bin/format.dart
+++ b/bin/format.dart
@@ -33,6 +33,9 @@
abbr: "n",
negatable: false,
help: "Show which files would be modified but make no changes.");
+ parser.addFlag("set-exit-if-changed",
+ negatable: false,
+ help: "Return exit code 1 if there are any formatting changes.");
parser.addFlag("overwrite",
abbr: "w",
negatable: false,
@@ -119,6 +122,10 @@
reporter = new ProfileReporter(reporter);
}
+ if (argResults["set-exit-if-changed"]) {
+ reporter = new SetExitReporter(reporter);
+ }
+
var pageWidth;
try {
pageWidth = int.parse(argResults["line-length"]);
diff --git a/lib/src/debug.dart b/lib/src/debug.dart
index 55734cf..b3ca65f 100644
--- a/lib/src/debug.dart
+++ b/lib/src/debug.dart
@@ -86,10 +86,10 @@
var rules =
chunks.map((chunk) => chunk.rule).where((rule) => rule != null).toSet();
- var rows = [];
+ var rows = <List<String>>[];
addChunk(List<Chunk> chunks, String prefix, int index) {
- var row = [];
+ var row = <String>[];
row.add("$prefix$index:");
var chunk = chunks[index];
diff --git a/lib/src/formatter_options.dart b/lib/src/formatter_options.dart
index 6195d18..9899771 100644
--- a/lib/src/formatter_options.dart
+++ b/lib/src/formatter_options.dart
@@ -129,10 +129,35 @@
}
}
-/// A decorating reporter that reports how long it took for format each file.
-class ProfileReporter implements OutputReporter {
+/// Base clase for a reporter that decorates an inner reporter.
+abstract class _ReporterDecorator implements OutputReporter {
final OutputReporter _inner;
+ _ReporterDecorator(this._inner);
+
+ void showDirectory(String path) {
+ _inner.showDirectory(path);
+ }
+
+ void showSkippedLink(String path) {
+ _inner.showSkippedLink(path);
+ }
+
+ void showHiddenPath(String path) {
+ _inner.showHiddenPath(path);
+ }
+
+ void beforeFile(File file, String label) {
+ _inner.beforeFile(file, label);
+ }
+
+ void afterFile(File file, String label, SourceCode output, {bool changed}) {
+ _inner.afterFile(file, label, output, changed: changed);
+ }
+}
+
+/// A decorating reporter that reports how long it took for format each file.
+class ProfileReporter extends _ReporterDecorator {
/// The files that have been started but have not completed yet.
///
/// Maps a file label to the time that it started being formatted.
@@ -145,7 +170,7 @@
/// tracking.
int _elided = 0;
- ProfileReporter(this._inner);
+ ProfileReporter(OutputReporter inner) : super(inner);
/// Show the times for the slowest files to format.
void showProfile() {
@@ -165,25 +190,9 @@
}
}
- /// Describe the directory whose contents are about to be processed.
- void showDirectory(String path) {
- _inner.showDirectory(path);
- }
-
- /// Describe the symlink at [path] that wasn't followed.
- void showSkippedLink(String path) {
- _inner.showSkippedLink(path);
- }
-
- /// Describe the hidden [path] that wasn't processed.
- void showHiddenPath(String path) {
- _inner.showHiddenPath(path);
- }
-
/// Called when [file] is about to be formatted.
void beforeFile(File file, String label) {
- _inner.beforeFile(file, label);
-
+ super.beforeFile(file, label);
_ongoing[label] = new DateTime.now();
}
@@ -199,6 +208,21 @@
_elided++;
}
- _inner.afterFile(file, label, output, changed: changed);
+ super.afterFile(file, label, output, changed: changed);
+ }
+}
+
+/// A decorating reporter that sets the exit code to 1 if any changes are made.
+class SetExitReporter extends _ReporterDecorator {
+ SetExitReporter(OutputReporter inner) : super(inner);
+
+ /// Describe the processed file at [path] whose formatted result is [output].
+ ///
+ /// If the contents of the file are the same as the formatted output,
+ /// [changed] will be false.
+ void afterFile(File file, String label, SourceCode output, {bool changed}) {
+ if (changed) exitCode = 1;
+
+ super.afterFile(file, label, output, changed: changed);
}
}
diff --git a/test/command_line_test.dart b/test/command_line_test.dart
index 425143d..e105859 100644
--- a/test/command_line_test.dart
+++ b/test/command_line_test.dart
@@ -210,6 +210,29 @@
});
});
+ group("--set-exit-if-changed", () {
+ test("gives exit code 0 if there are no changes", () {
+ d.dir("code", [d.file("a.dart", formattedSource)]).create();
+
+ var process = runFormatterOnDir(["--set-exit-if-changed"]);
+ process.shouldExit(0);
+ });
+
+ test("gives exit code 1 if there are changes", () {
+ d.dir("code", [d.file("a.dart", unformattedSource)]).create();
+
+ var process = runFormatterOnDir(["--set-exit-if-changed"]);
+ process.shouldExit(1);
+ });
+
+ test("gives exit code 1 if there are changes even in dry run", () {
+ d.dir("code", [d.file("a.dart", unformattedSource)]).create();
+
+ var process = runFormatterOnDir(["--set-exit-if-changed", "--dry-run"]);
+ process.shouldExit(1);
+ });
+ });
+
group("with no paths", () {
test("errors on --overwrite", () {
var process = runFormatter(["--overwrite"]);