OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dart_style.src.formatter_options; | 5 library dart_style.src.formatter_options; |
6 | 6 |
7 import 'dart:convert'; | 7 import 'dart:convert'; |
8 import 'dart:io'; | 8 import 'dart:io'; |
9 | 9 |
10 import 'source_code.dart'; | 10 import 'source_code.dart'; |
11 | 11 |
12 /// Global options that affect how the formatter produces and uses its outputs. | 12 /// Global options that affect how the formatter produces and uses its outputs. |
13 class FormatterOptions { | 13 class FormatterOptions { |
14 /// The [OutputReporter] used to show the formatting results. | 14 /// The [OutputReporter] used to show the formatting results. |
15 final OutputReporter reporter; | 15 final OutputReporter reporter; |
16 | 16 |
| 17 /// The number of spaces of indentation to prefix the output with. |
| 18 final int indent; |
| 19 |
17 /// The number of columns that formatted output should be constrained to fit | 20 /// The number of columns that formatted output should be constrained to fit |
18 /// within. | 21 /// within. |
19 final int pageWidth; | 22 final int pageWidth; |
20 | 23 |
21 /// Whether symlinks should be traversed when formatting a directory. | 24 /// Whether symlinks should be traversed when formatting a directory. |
22 final bool followLinks; | 25 final bool followLinks; |
23 | 26 |
24 FormatterOptions(this.reporter, | 27 FormatterOptions(this.reporter, |
25 {this.pageWidth: 80, this.followLinks: false}); | 28 {this.indent: 0, this.pageWidth: 80, this.followLinks: false}); |
26 } | 29 } |
27 | 30 |
28 /// How the formatter reports the results it produces. | 31 /// How the formatter reports the results it produces. |
29 abstract class OutputReporter { | 32 abstract class OutputReporter { |
30 /// Prints only the names of files whose contents are different from their | 33 /// Prints only the names of files whose contents are different from their |
31 /// formatted version. | 34 /// formatted version. |
32 static final dryRun = new _DryRunReporter(); | 35 static final dryRun = new _DryRunReporter(); |
33 | 36 |
34 /// Prints the formatted results of each file to stdout. | 37 /// Prints the formatted results of each file to stdout. |
35 static final print = new _PrintReporter(); | 38 static final print = new _PrintReporter(); |
36 | 39 |
37 /// Prints the formatted result and selection info of each file to stdout as | 40 /// Prints the formatted result and selection info of each file to stdout as |
38 /// a JSON map. | 41 /// a JSON map. |
39 static final printJson = new _PrintJsonReporter(); | 42 static final printJson = new _PrintJsonReporter(); |
40 | 43 |
41 /// Overwrites each file with its formatted result. | 44 /// Overwrites each file with its formatted result. |
42 static final overwrite = new _OverwriteReporter(); | 45 static final overwrite = new _OverwriteReporter(); |
43 | 46 |
44 /// Describe the directory whose contents are about to be processed. | 47 /// Describe the directory whose contents are about to be processed. |
45 void showDirectory(String path) {} | 48 void showDirectory(String path) {} |
46 | 49 |
47 /// Describe the symlink at [path] that wasn't followed. | 50 /// Describe the symlink at [path] that wasn't followed. |
48 void showSkippedLink(String path) {} | 51 void showSkippedLink(String path) {} |
49 | 52 |
50 /// Describe the hidden file at [path] that wasn't processed. | 53 /// Describe the hidden [path] that wasn't processed. |
51 void showHiddenFile(String path) {} | 54 void showHiddenPath(String path) {} |
| 55 |
| 56 /// Called when [file] is about to be formatted. |
| 57 void beforeFile(File file, String label) {} |
52 | 58 |
53 /// Describe the processed file at [path] whose formatted result is [output]. | 59 /// Describe the processed file at [path] whose formatted result is [output]. |
54 /// | 60 /// |
55 /// If the contents of the file are the same as the formatted output, | 61 /// If the contents of the file are the same as the formatted output, |
56 /// [changed] will be false. | 62 /// [changed] will be false. |
57 void showFile(File file, String label, SourceCode output, {bool changed}); | 63 void afterFile(File file, String label, SourceCode output, {bool changed}); |
58 } | 64 } |
59 | 65 |
60 /// Prints only the names of files whose contents are different from their | 66 /// Prints only the names of files whose contents are different from their |
61 /// formatted version. | 67 /// formatted version. |
62 class _DryRunReporter extends OutputReporter { | 68 class _DryRunReporter extends OutputReporter { |
63 void showFile(File file, String label, SourceCode output, {bool changed}) { | 69 void afterFile(File file, String label, SourceCode output, {bool changed}) { |
64 // Only show the changed files. | 70 // Only show the changed files. |
65 if (changed) print(label); | 71 if (changed) print(label); |
66 } | 72 } |
67 } | 73 } |
68 | 74 |
69 /// Prints the formatted results of each file to stdout. | 75 /// Prints the formatted results of each file to stdout. |
70 class _PrintReporter extends OutputReporter { | 76 class _PrintReporter extends OutputReporter { |
71 void showDirectory(String path) { | 77 void showDirectory(String path) { |
72 print("Formatting directory $path:"); | 78 print("Formatting directory $path:"); |
73 } | 79 } |
74 | 80 |
75 void showSkippedLink(String path) { | 81 void showSkippedLink(String path) { |
76 print("Skipping link $path"); | 82 print("Skipping link $path"); |
77 } | 83 } |
78 | 84 |
79 void showHiddenFile(String path) { | 85 void showHiddenPath(String path) { |
80 print("Skipping hidden file $path"); | 86 print("Skipping hidden path $path"); |
81 } | 87 } |
82 | 88 |
83 void showFile(File file, String label, SourceCode output, {bool changed}) { | 89 void afterFile(File file, String label, SourceCode output, {bool changed}) { |
84 // Don't add an extra newline. | 90 // Don't add an extra newline. |
85 stdout.write(output.text); | 91 stdout.write(output.text); |
86 } | 92 } |
87 } | 93 } |
88 | 94 |
89 /// Prints the formatted result and selection info of each file to stdout as a | 95 /// Prints the formatted result and selection info of each file to stdout as a |
90 /// JSON map. | 96 /// JSON map. |
91 class _PrintJsonReporter extends OutputReporter { | 97 class _PrintJsonReporter extends OutputReporter { |
92 void showFile(File file, String label, SourceCode output, {bool changed}) { | 98 void afterFile(File file, String label, SourceCode output, {bool changed}) { |
93 // TODO(rnystrom): Put an empty selection in here to remain compatible with | 99 // TODO(rnystrom): Put an empty selection in here to remain compatible with |
94 // the old formatter. Since there's no way to pass a selection on the | 100 // the old formatter. Since there's no way to pass a selection on the |
95 // command line, this will never be used, which is why it's hard-coded to | 101 // command line, this will never be used, which is why it's hard-coded to |
96 // -1, -1. If we add support for passing in a selection, put the real | 102 // -1, -1. If we add support for passing in a selection, put the real |
97 // result here. | 103 // result here. |
98 print(JSON.encode({ | 104 print(JSON.encode({ |
99 "path": label, | 105 "path": label, |
100 "source": output.text, | 106 "source": output.text, |
101 "selection": { | 107 "selection": { |
102 "offset": output.selectionStart != null ? output.selectionStart : -1, | 108 "offset": output.selectionStart != null ? output.selectionStart : -1, |
103 "length": output.selectionLength != null ? output.selectionLength : -1 | 109 "length": output.selectionLength != null ? output.selectionLength : -1 |
104 } | 110 } |
105 })); | 111 })); |
106 } | 112 } |
107 } | 113 } |
108 | 114 |
109 /// Overwrites each file with its formatted result. | 115 /// Overwrites each file with its formatted result. |
110 class _OverwriteReporter extends _PrintReporter { | 116 class _OverwriteReporter extends _PrintReporter { |
111 void showFile(File file, String label, SourceCode output, {bool changed}) { | 117 void afterFile(File file, String label, SourceCode output, {bool changed}) { |
112 if (changed) { | 118 if (changed) { |
113 file.writeAsStringSync(output.text); | 119 file.writeAsStringSync(output.text); |
114 print("Formatted $label"); | 120 print("Formatted $label"); |
115 } else { | 121 } else { |
116 print("Unchanged $label"); | 122 print("Unchanged $label"); |
117 } | 123 } |
118 } | 124 } |
119 } | 125 } |
| 126 |
| 127 /// A decorating reporter that reports how long it took for format each file. |
| 128 class ProfileReporter implements OutputReporter { |
| 129 final OutputReporter _inner; |
| 130 |
| 131 /// The files that have been started but have not completed yet. |
| 132 /// |
| 133 /// Maps a file label to the time that it started being formatted. |
| 134 final Map<String, DateTime> _ongoing = {}; |
| 135 |
| 136 /// The elapsed time it took to format each completed file. |
| 137 final Map<String, Duration> _elapsed = {}; |
| 138 |
| 139 /// The number of files that completed so fast that they aren't worth |
| 140 /// tracking. |
| 141 int _elided = 0; |
| 142 |
| 143 ProfileReporter(this._inner); |
| 144 |
| 145 /// Show the times for the slowest files to format. |
| 146 void showProfile() { |
| 147 // Everything should be done. |
| 148 assert(_ongoing.isEmpty); |
| 149 |
| 150 var files = _elapsed.keys.toList(); |
| 151 files.sort((a, b) => _elapsed[b].compareTo(_elapsed[a])); |
| 152 |
| 153 for (var file in files) { |
| 154 print("${_elapsed[file]}: $file"); |
| 155 } |
| 156 |
| 157 if (_elided >= 1) { |
| 158 var s = _elided > 1 ? 's' : ''; |
| 159 print("...$_elided more file$s each took less than 10ms."); |
| 160 } |
| 161 } |
| 162 |
| 163 /// Describe the directory whose contents are about to be processed. |
| 164 void showDirectory(String path) { |
| 165 _inner.showDirectory(path); |
| 166 } |
| 167 |
| 168 /// Describe the symlink at [path] that wasn't followed. |
| 169 void showSkippedLink(String path) { |
| 170 _inner.showSkippedLink(path); |
| 171 } |
| 172 |
| 173 /// Describe the hidden [path] that wasn't processed. |
| 174 void showHiddenPath(String path) { |
| 175 _inner.showHiddenPath(path); |
| 176 } |
| 177 |
| 178 /// Called when [file] is about to be formatted. |
| 179 void beforeFile(File file, String label) { |
| 180 _inner.beforeFile(file, label); |
| 181 |
| 182 _ongoing[label] = new DateTime.now(); |
| 183 } |
| 184 |
| 185 /// Describe the processed file at [path] whose formatted result is [output]. |
| 186 /// |
| 187 /// If the contents of the file are the same as the formatted output, |
| 188 /// [changed] will be false. |
| 189 void afterFile(File file, String label, SourceCode output, {bool changed}) { |
| 190 var elapsed = new DateTime.now().difference(_ongoing.remove(label)); |
| 191 if (elapsed.inMilliseconds >= 10) { |
| 192 _elapsed[label] = elapsed; |
| 193 } else { |
| 194 _elided++; |
| 195 } |
| 196 |
| 197 _inner.afterFile(file, label, output, changed: changed); |
| 198 } |
| 199 } |
OLD | NEW |