| Index: bin/format.dart | 
| diff --git a/bin/format.dart b/bin/format.dart | 
| index 7703107779f6056c1c8c1e62d87514984f8eb6e6..1238f3930c70613fe8c66fd57898080cda33adc2 100644 | 
| --- a/bin/format.dart | 
| +++ b/bin/format.dart | 
| @@ -10,6 +10,7 @@ import 'package:dart_style/src/dart_formatter.dart'; | 
| 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 @@ void main(List<String> args) { | 
| 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 @@ void main(List<String> args) { | 
| 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 @@ void main(List<String> args) { | 
| 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 @@ void main(List<String> args) { | 
| 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 @@ void main(List<String> args) { | 
| 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 @@ void formatPaths(FormatterOptions options, List<String> paths) { | 
| } | 
| } | 
|  | 
| +/// 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; | 
|  | 
|  |