Allow command-line API to pass in selection to preserve. Fix #194.
R=pquitslund@google.com
Review URL: https://chromiumcodereview.appspot.com//968053004
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b16a46..796af87 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# 0.1.6
+
+* Allow passing in selection to preserve through command line (#194).
+
# 0.1.5+1, 0.1.5+2, 0.1.5+3
* Fix test files to work in main Dart repo test runner.
diff --git a/bin/format.dart b/bin/format.dart
index 7703107..1238f39 100644
--- a/bin/format.dart
+++ b/bin/format.dart
@@ -10,6 +10,7 @@
import 'package:dart_style/src/formatter_exception.dart';
import 'package:dart_style/src/formatter_options.dart';
import 'package:dart_style/src/io.dart';
+import 'package:dart_style/src/source_code.dart';
void main(List<String> args) {
var parser = new ArgParser(allowTrailingOptions: true);
@@ -19,6 +20,8 @@
parser.addOption("line-length", abbr: "l",
help: "Wrap lines longer than this.",
defaultsTo: "80");
+ parser.addOption("preserve",
+ help: 'Selection to preserve, formatted as "start:length".');
parser.addFlag("dry-run", abbr: "n", negatable: false,
help: "Show which files would be modified but make no changes.");
parser.addFlag("overwrite", abbr: "w", negatable: false,
@@ -35,9 +38,7 @@
try {
argResults = parser.parse(args);
} on FormatException catch (err) {
- printUsage(parser, err.message);
- exitCode = 64;
- return;
+ usageError(parser, err.message);
}
if (argResults["help"]) {
@@ -45,36 +46,45 @@
return;
}
+ // Can only preserve a selection when parsing from stdin.
+ var selection;
+
+ if (argResults["preserve"] != null && argResults.rest.isNotEmpty) {
+ usageError(parser, "Can only use --preserve when reading from stdin.");
+ }
+
+ try {
+ selection = parseSelection(argResults["preserve"]);
+ } on FormatException catch (_) {
+ usageError(parser,
+ '--preserve must be a colon-separated pair of integers, was '
+ '"${argResults['preserve']}".');
+ }
+
if (argResults["dry-run"] && argResults["overwrite"]) {
- printUsage(parser,
+ usageError(parser,
"Cannot use --dry-run and --overwrite at the same time.");
- exitCode = 64;
- return;
}
checkForReporterCollision(String chosen, String other) {
- if (!argResults[other]) return false;
+ if (!argResults[other]) return;
- printUsage(parser,
+ usageError(parser,
"Cannot use --$chosen and --$other at the same time.");
- exitCode = 64;
- return true;
}
var reporter = OutputReporter.print;
if (argResults["dry-run"]) {
- if (checkForReporterCollision("dry-run", "overwrite")) return;
- if (checkForReporterCollision("dry-run", "machine")) return;
+ checkForReporterCollision("dry-run", "overwrite");
+ checkForReporterCollision("dry-run", "machine");
reporter = OutputReporter.dryRun;
} else if (argResults["overwrite"]) {
- if (checkForReporterCollision("overwrite", "machine")) return;
+ checkForReporterCollision("overwrite", "machine");
if (argResults.rest.isEmpty) {
- printUsage(parser,
+ usageError(parser,
"Cannot use --overwrite without providing any paths to format.");
- exitCode = 64;
- return;
}
reporter = OutputReporter.overwrite;
@@ -87,10 +97,8 @@
try {
pageWidth = int.parse(argResults["line-length"]);
} on FormatException catch (_) {
- printUsage(parser, '--line-length must be an integer, was '
+ usageError(parser, '--line-length must be an integer, was '
'"${argResults['line-length']}".');
- exitCode = 64;
- return;
}
var followLinks = argResults["follow-links"];
@@ -99,20 +107,43 @@
pageWidth: pageWidth, followLinks: followLinks);
if (argResults.rest.isEmpty) {
- formatStdin(options);
+ formatStdin(options, selection);
} else {
formatPaths(options, argResults.rest);
}
}
+List<int> parseSelection(String selection) {
+ if (selection == null) return null;
+
+ var coordinates = selection.split(":");
+ if (coordinates.length != 2) {
+ throw new 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) {
+void formatStdin(FormatterOptions options, List<int> selection) {
+ var selectionStart = 0;
+ var selectionLength = 0;
+
+ if (selection != null) {
+ selectionStart = selection[0];
+ selectionLength = selection[1];
+ }
+
var input = new StringBuffer();
stdin.transform(new Utf8Decoder()).listen(input.write, onDone: () {
var formatter = new DartFormatter(pageWidth: options.pageWidth);
try {
- var source = input.toString();
- var output = formatter.format(source, uri: "stdin");
+ var source = new SourceCode(input.toString(),
+ uri: "stdin",
+ selectionStart: selectionStart,
+ selectionLength: selectionLength);
+ var output = formatter.formatSource(source);
options.reporter.showFile(null, "<stdin>", output,
changed: source != output);
return true;
@@ -149,6 +180,12 @@
}
}
+/// Prints [error] and usage help then exits with exit code 64.
+void usageError(ArgParser parser, String error) {
+ printUsage(parser, error);
+ exit(64);
+}
+
void printUsage(ArgParser parser, [String error]) {
var output = stdout;
diff --git a/lib/src/formatter_options.dart b/lib/src/formatter_options.dart
index 396e10a..3869e8a 100644
--- a/lib/src/formatter_options.dart
+++ b/lib/src/formatter_options.dart
@@ -7,6 +7,8 @@
import 'dart:convert';
import 'dart:io';
+import 'source_code.dart';
+
/// Global options that affect how the formatter produces and uses its outputs.
class FormatterOptions {
/// The [OutputReporter] used to show the formatting results.
@@ -52,13 +54,13 @@
///
/// If the contents of the file are the same as the formatted output,
/// [changed] will be false.
- void showFile(File file, String label, String output, {bool changed});
+ void showFile(File file, String label, SourceCode output, {bool changed});
}
/// Prints only the names of files whose contents are different from their
/// formatted version.
class _DryRunReporter extends OutputReporter {
- void showFile(File file, String label, String output, {bool changed}) {
+ void showFile(File file, String label, SourceCode output, {bool changed}) {
// Only show the changed files.
if (changed) print(label);
}
@@ -78,16 +80,16 @@
print("Skipping hidden file $path");
}
- void showFile(File file, String label, String output, {bool changed}) {
+ void showFile(File file, String label, SourceCode output, {bool changed}) {
// Don't add an extra newline.
- stdout.write(output);
+ stdout.write(output.text);
}
}
/// Prints the formatted result and selection info of each file to stdout as a
/// JSON map.
class _PrintJsonReporter extends OutputReporter {
- void showFile(File file, String label, String output, {bool changed}) {
+ void showFile(File file, String label, SourceCode output, {bool changed}) {
// TODO(rnystrom): Put an empty selection in here to remain compatible with
// the old formatter. Since there's no way to pass a selection on the
// command line, this will never be used, which is why it's hard-coded to
@@ -95,17 +97,20 @@
// result here.
print(JSON.encode({
"path": label,
- "source": output,
- "selection": {"offset": -1, "length": -1}
+ "source": output.text,
+ "selection": {
+ "offset": output.selectionStart != null ? output.selectionStart : -1,
+ "length": output.selectionLength != null ? output.selectionLength : -1
+ }
}));
}
}
/// Overwrites each file with its formatted result.
class _OverwriteReporter extends _PrintReporter {
- void showFile(File file, String label, String output, {bool changed}) {
+ void showFile(File file, String label, SourceCode output, {bool changed}) {
if (changed) {
- file.writeAsStringSync(output);
+ file.writeAsStringSync(output.text);
print("Formatted $label");
} else {
print("Unchanged $label");
diff --git a/lib/src/io.dart b/lib/src/io.dart
index a1f5361..e7c8555 100644
--- a/lib/src/io.dart
+++ b/lib/src/io.dart
@@ -11,6 +11,7 @@
import 'dart_formatter.dart';
import 'formatter_options.dart';
import 'formatter_exception.dart';
+import 'source_code.dart';
/// Runs the formatter on every .dart file in [path] (and its subdirectories),
/// and replaces them with their formatted output.
@@ -47,15 +48,15 @@
/// Runs the formatter on [file].
///
/// Returns `true` if successful or `false` if an error occurred.
-bool processFile(FormatterOptions options, File file,
- {String label}) {
+bool processFile(FormatterOptions options, File file, {String label}) {
if (label == null) label = file.path;
var formatter = new DartFormatter(pageWidth: options.pageWidth);
try {
- var source = file.readAsStringSync();
- var output = formatter.format(source, uri: file.path);
- options.reporter.showFile(file, label, output, changed: source != output);
+ var source = new SourceCode(file.readAsStringSync(), uri: file.path);
+ var output = formatter.formatSource(source);
+ options.reporter.showFile(file, label, output,
+ changed: source.text != output.text);
return true;
} on FormatterException catch (err) {
stderr.writeln(err.message());
diff --git a/pubspec.yaml b/pubspec.yaml
index 6611764..a7eefab 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: dart_style
-version: 0.1.5+3
+version: 0.1.6
author: Dart Team <misc@dartlang.org>
description: Opinionated, automatic Dart source code formatter.
homepage: https://github.com/dart-lang/dart_style
diff --git a/test/command_line_test.dart b/test/command_line_test.dart
index b2cf84a..4d1e0d9 100644
--- a/test/command_line_test.dart
+++ b/test/command_line_test.dart
@@ -147,6 +147,41 @@
});
});
+ group("--preserve", () {
+ test("errors if given paths.", () {
+ var process = runFormatter(["--preserve", "path", "another"]);
+ process.shouldExit(64);
+ });
+
+ test("errors on wrong number of components.", () {
+ var process = runFormatter(["--preserve", "1"]);
+ process.shouldExit(64);
+
+ process = runFormatter(["--preserve", "1:2:3"]);
+ process.shouldExit(64);
+ });
+
+ test("errors on non-integer component.", () {
+ var process = runFormatter(["--preserve", "1:2.3"]);
+ process.shouldExit(64);
+ });
+
+ test("updates selection.", () {
+ var process = runFormatter(["--preserve", "6:10", "-m"]);
+ process.writeLine(unformattedSource);
+ process.closeStdin();
+
+ var json = JSON.encode({
+ "path": "<stdin>",
+ "source": formattedSource,
+ "selection": {"offset": 5, "length": 9}
+ });
+
+ process.stdout.expect(json);
+ process.shouldExit();
+ });
+ });
+
group("with no paths", () {
test("errors on --overwrite.", () {
var process = runFormatter(["--overwrite"]);