OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 import 'dart:convert'; |
| 6 import 'dart:io'; |
| 7 |
| 8 import 'package:args/args.dart'; |
| 9 import 'package:dart_style/src/dart_formatter.dart'; |
| 10 import 'package:dart_style/src/formatter_exception.dart'; |
| 11 import 'package:dart_style/src/formatter_options.dart'; |
| 12 import 'package:dart_style/src/io.dart'; |
| 13 import 'package:dart_style/src/source_code.dart'; |
| 14 |
| 15 void main(List<String> args) { |
| 16 var parser = new ArgParser(allowTrailingOptions: true); |
| 17 |
| 18 parser.addFlag("help", |
| 19 abbr: "h", negatable: false, help: "Shows usage information."); |
| 20 parser.addOption("line-length", |
| 21 abbr: "l", help: "Wrap lines longer than this.", defaultsTo: "80"); |
| 22 parser.addOption("preserve", |
| 23 help: 'Selection to preserve, formatted as "start:length".'); |
| 24 parser.addFlag("dry-run", |
| 25 abbr: "n", |
| 26 negatable: false, |
| 27 help: "Show which files would be modified but make no changes."); |
| 28 parser.addFlag("overwrite", |
| 29 abbr: "w", |
| 30 negatable: false, |
| 31 help: "Overwrite input files with formatted output."); |
| 32 parser.addFlag("machine", |
| 33 abbr: "m", |
| 34 negatable: false, |
| 35 help: "Produce machine-readable JSON output."); |
| 36 parser.addFlag("follow-links", |
| 37 negatable: false, |
| 38 help: "Follow links to files and directories.\n" |
| 39 "If unset, links will be ignored."); |
| 40 parser.addFlag("transform", |
| 41 abbr: "t", |
| 42 negatable: false, |
| 43 help: "Unused flag for compability with the old formatter."); |
| 44 |
| 45 var argResults; |
| 46 try { |
| 47 argResults = parser.parse(args); |
| 48 } on FormatException catch (err) { |
| 49 usageError(parser, err.message); |
| 50 } |
| 51 |
| 52 if (argResults["help"]) { |
| 53 printUsage(parser); |
| 54 return; |
| 55 } |
| 56 |
| 57 // Can only preserve a selection when parsing from stdin. |
| 58 var selection; |
| 59 |
| 60 if (argResults["preserve"] != null && argResults.rest.isNotEmpty) { |
| 61 usageError(parser, "Can only use --preserve when reading from stdin."); |
| 62 } |
| 63 |
| 64 try { |
| 65 selection = parseSelection(argResults["preserve"]); |
| 66 } on FormatException catch (_) { |
| 67 usageError( |
| 68 parser, |
| 69 '--preserve must be a colon-separated pair of integers, was ' |
| 70 '"${argResults['preserve']}".'); |
| 71 } |
| 72 |
| 73 if (argResults["dry-run"] && argResults["overwrite"]) { |
| 74 usageError( |
| 75 parser, "Cannot use --dry-run and --overwrite at the same time."); |
| 76 } |
| 77 |
| 78 checkForReporterCollision(String chosen, String other) { |
| 79 if (!argResults[other]) return; |
| 80 |
| 81 usageError(parser, "Cannot use --$chosen and --$other at the same time."); |
| 82 } |
| 83 |
| 84 var reporter = OutputReporter.print; |
| 85 if (argResults["dry-run"]) { |
| 86 checkForReporterCollision("dry-run", "overwrite"); |
| 87 checkForReporterCollision("dry-run", "machine"); |
| 88 |
| 89 reporter = OutputReporter.dryRun; |
| 90 } else if (argResults["overwrite"]) { |
| 91 checkForReporterCollision("overwrite", "machine"); |
| 92 |
| 93 if (argResults.rest.isEmpty) { |
| 94 usageError(parser, |
| 95 "Cannot use --overwrite without providing any paths to format."); |
| 96 } |
| 97 |
| 98 reporter = OutputReporter.overwrite; |
| 99 } else if (argResults["machine"]) { |
| 100 reporter = OutputReporter.printJson; |
| 101 } |
| 102 |
| 103 var pageWidth; |
| 104 |
| 105 try { |
| 106 pageWidth = int.parse(argResults["line-length"]); |
| 107 } on FormatException catch (_) { |
| 108 usageError( |
| 109 parser, |
| 110 '--line-length must be an integer, was ' |
| 111 '"${argResults['line-length']}".'); |
| 112 } |
| 113 |
| 114 var followLinks = argResults["follow-links"]; |
| 115 |
| 116 var options = new FormatterOptions(reporter, |
| 117 pageWidth: pageWidth, followLinks: followLinks); |
| 118 |
| 119 if (argResults.rest.isEmpty) { |
| 120 formatStdin(options, selection); |
| 121 } else { |
| 122 formatPaths(options, argResults.rest); |
| 123 } |
| 124 } |
| 125 |
| 126 List<int> parseSelection(String selection) { |
| 127 if (selection == null) return null; |
| 128 |
| 129 var coordinates = selection.split(":"); |
| 130 if (coordinates.length != 2) { |
| 131 throw new FormatException( |
| 132 'Selection should be a colon-separated pair of integers, "123:45".'); |
| 133 } |
| 134 |
| 135 return coordinates.map((coord) => coord.trim()).map(int.parse).toList(); |
| 136 } |
| 137 |
| 138 /// Reads input from stdin until it's closed, and the formats it. |
| 139 void formatStdin(FormatterOptions options, List<int> selection) { |
| 140 var selectionStart = 0; |
| 141 var selectionLength = 0; |
| 142 |
| 143 if (selection != null) { |
| 144 selectionStart = selection[0]; |
| 145 selectionLength = selection[1]; |
| 146 } |
| 147 |
| 148 var input = new StringBuffer(); |
| 149 stdin.transform(new Utf8Decoder()).listen(input.write, onDone: () { |
| 150 var formatter = new DartFormatter(pageWidth: options.pageWidth); |
| 151 try { |
| 152 var source = new SourceCode(input.toString(), |
| 153 uri: "stdin", |
| 154 selectionStart: selectionStart, |
| 155 selectionLength: selectionLength); |
| 156 var output = formatter.formatSource(source); |
| 157 options.reporter |
| 158 .showFile(null, "<stdin>", output, changed: source != output); |
| 159 return true; |
| 160 } on FormatterException catch (err) { |
| 161 stderr.writeln(err.message()); |
| 162 exitCode = 65; // sysexits.h: EX_DATAERR |
| 163 } catch (err, stack) { |
| 164 stderr.writeln('''Hit a bug in the formatter when formatting stdin. |
| 165 Please report at: github.com/dart-lang/dart_style/issues |
| 166 $err |
| 167 $stack'''); |
| 168 exitCode = 70; // sysexits.h: EX_SOFTWARE |
| 169 } |
| 170 }); |
| 171 } |
| 172 |
| 173 /// Formats all of the files and directories given by [paths]. |
| 174 void formatPaths(FormatterOptions options, List<String> paths) { |
| 175 for (var path in paths) { |
| 176 var directory = new Directory(path); |
| 177 if (directory.existsSync()) { |
| 178 if (!processDirectory(options, directory)) { |
| 179 exitCode = 65; |
| 180 } |
| 181 continue; |
| 182 } |
| 183 |
| 184 var file = new File(path); |
| 185 if (file.existsSync()) { |
| 186 if (!processFile(options, file)) { |
| 187 exitCode = 65; |
| 188 } |
| 189 } else { |
| 190 stderr.writeln('No file or directory found at "$path".'); |
| 191 } |
| 192 } |
| 193 } |
| 194 |
| 195 /// Prints [error] and usage help then exits with exit code 64. |
| 196 void usageError(ArgParser parser, String error) { |
| 197 printUsage(parser, error); |
| 198 exit(64); |
| 199 } |
| 200 |
| 201 void printUsage(ArgParser parser, [String error]) { |
| 202 var output = stdout; |
| 203 |
| 204 var message = "Reformats whitespace in Dart source files."; |
| 205 if (error != null) { |
| 206 message = error; |
| 207 output = stdout; |
| 208 } |
| 209 |
| 210 output.write("""$message |
| 211 |
| 212 Usage: dartfmt [-n|-w] [files or directories...] |
| 213 |
| 214 ${parser.usage} |
| 215 """); |
| 216 } |
OLD | NEW |