Coverage report for lib/src/formatter_options.dart

Line coverage: 15 / 62 (24.2%)

All files > lib/src/formatter_options.dart

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
3
// BSD-style license that can be found in the LICENSE file.
4
5
library dart_style.src.formatter_options;
6
7
import 'dart:convert';
8
import 'dart:io';
9
10
import 'source_code.dart';
11
import 'style_fix.dart';
12
13
/// Global options that affect how the formatter produces and uses its outputs.
14
class FormatterOptions {
15
  /// The [OutputReporter] used to show the formatting results.
16
  final OutputReporter reporter;
17
18
  /// The number of spaces of indentation to prefix the output with.
19
  final int indent;
20
21
  /// The number of columns that formatted output should be constrained to fit
22
  /// within.
23
  final int pageWidth;
24
25
  /// Whether symlinks should be traversed when formatting a directory.
26
  final bool followLinks;
27
28
  /// The style fixes to apply while formatting.
29
  final Iterable<StyleFix> fixes;
30
311
  FormatterOptions(this.reporter,
32
      {this.indent: 0,
33
      this.pageWidth: 80,
34
      this.followLinks: false,
35
      this.fixes});
36
}
37
38
/// How the formatter reports the results it produces.
39
abstract class OutputReporter {
40
  /// Prints only the names of files whose contents are different from their
41
  /// formatted version.
42
  static final OutputReporter dryRun = new _DryRunReporter();
43
44
  /// Prints the formatted results of each file to stdout.
45
  static final OutputReporter print = new _PrintReporter();
46
47
  /// Prints the formatted result and selection info of each file to stdout as
48
  /// a JSON map.
49
  static final OutputReporter printJson = new _PrintJsonReporter();
50
51
  /// Overwrites each file with its formatted result.
52
  static final OutputReporter overwrite = new _OverwriteReporter();
53
54
  /// Describe the directory whose contents are about to be processed.
550
  void showDirectory(String path) {}
56
57
  /// Describe the symlink at [path] that wasn't followed.
580
  void showSkippedLink(String path) {}
59
60
  /// Describe the hidden [path] that wasn't processed.
610
  void showHiddenPath(String path) {}
62
63
  /// Called when [file] is about to be formatted.
641
  void beforeFile(File file, String label) {}
65
66
  /// Describe the processed file at [path] whose formatted result is [output].
67
  ///
68
  /// If the contents of the file are the same as the formatted output,
69
  /// [changed] will be false.
70
  void afterFile(File file, String label, SourceCode output, {bool changed});
71
}
72
73
/// Prints only the names of files whose contents are different from their
74
/// formatted version.
75
class _DryRunReporter extends OutputReporter {
760
  void afterFile(File file, String label, SourceCode output, {bool changed}) {
77
    // Only show the changed files.
780
    if (changed) print(label);
79
  }
80
}
81
82
/// Prints the formatted results of each file to stdout.
83
class _PrintReporter extends OutputReporter {
841
  void showDirectory(String path) {
852
    print("Formatting directory $path:");
86
  }
87
881
  void showSkippedLink(String path) {
892
    print("Skipping link $path");
90
  }
91
921
  void showHiddenPath(String path) {
932
    print("Skipping hidden path $path");
94
  }
95
960
  void afterFile(File file, String label, SourceCode output, {bool changed}) {
97
    // Don't add an extra newline.
980
    stdout.write(output.text);
99
  }
100
}
101
102
/// Prints the formatted result and selection info of each file to stdout as a
103
/// JSON map.
104
class _PrintJsonReporter extends OutputReporter {
1050
  void afterFile(File file, String label, SourceCode output, {bool changed}) {
106
    // TODO(rnystrom): Put an empty selection in here to remain compatible with
107
    // the old formatter. Since there's no way to pass a selection on the
108
    // command line, this will never be used, which is why it's hard-coded to
109
    // -1, -1. If we add support for passing in a selection, put the real
110
    // result here.
1110
    print(jsonEncode({
112
      "path": label,
1130
      "source": output.text,
1140
      "selection": {
1150
        "offset": output.selectionStart != null ? output.selectionStart : -1,
1160
        "length": output.selectionLength != null ? output.selectionLength : -1
117
      }
118
    }));
119
  }
120
}
121
122
/// Overwrites each file with its formatted result.
123
class _OverwriteReporter extends _PrintReporter {
1241
  void afterFile(File file, String label, SourceCode output, {bool changed}) {
125
    if (changed) {
126
      try {
1272
        file.writeAsStringSync(output.text);
1282
        print("Formatted $label");
1291
      } on FileSystemException catch (err) {
1302
        stderr.writeln("Could not overwrite $label: "
1314
            "${err.osError.message} (error code ${err.osError.errorCode})");
132
      }
133
    } else {
1342
      print("Unchanged $label");
135
    }
136
  }
137
}
138
139
/// Base clase for a reporter that decorates an inner reporter.
140
abstract class _ReporterDecorator implements OutputReporter {
141
  final OutputReporter _inner;
142
1430
  _ReporterDecorator(this._inner);
144
1450
  void showDirectory(String path) {
1460
    _inner.showDirectory(path);
147
  }
148
1490
  void showSkippedLink(String path) {
1500
    _inner.showSkippedLink(path);
151
  }
152
1530
  void showHiddenPath(String path) {
1540
    _inner.showHiddenPath(path);
155
  }
156
1570
  void beforeFile(File file, String label) {
1580
    _inner.beforeFile(file, label);
159
  }
160
1610
  void afterFile(File file, String label, SourceCode output, {bool changed}) {
1620
    _inner.afterFile(file, label, output, changed: changed);
163
  }
164
}
165
166
/// A decorating reporter that reports how long it took for format each file.
167
class ProfileReporter extends _ReporterDecorator {
168
  /// The files that have been started but have not completed yet.
169
  ///
170
  /// Maps a file label to the time that it started being formatted.
171
  final Map<String, DateTime> _ongoing = {};
172
173
  /// The elapsed time it took to format each completed file.
174
  final Map<String, Duration> _elapsed = {};
175
176
  /// The number of files that completed so fast that they aren't worth
177
  /// tracking.
178
  int _elided = 0;
179
1800
  ProfileReporter(OutputReporter inner) : super(inner);
181
182
  /// Show the times for the slowest files to format.
1830
  void showProfile() {
184
    // Everything should be done.
1850
    assert(_ongoing.isEmpty);
186
1870
    var files = _elapsed.keys.toList();
1880
    files.sort((a, b) => _elapsed[b].compareTo(_elapsed[a]));
189
1900
    for (var file in files) {
1910
      print("${_elapsed[file]}: $file");
192
    }
193
1940
    if (_elided >= 1) {
1950
      var s = _elided > 1 ? 's' : '';
1960
      print("...$_elided more file$s each took less than 10ms.");
197
    }
198
  }
199
200
  /// Called when [file] is about to be formatted.
2010
  void beforeFile(File file, String label) {
2020
    super.beforeFile(file, label);
2030
    _ongoing[label] = new DateTime.now();
204
  }
205
206
  /// Describe the processed file at [path] whose formatted result is [output].
207
  ///
208
  /// If the contents of the file are the same as the formatted output,
209
  /// [changed] will be false.
2100
  void afterFile(File file, String label, SourceCode output, {bool changed}) {
2110
    var elapsed = new DateTime.now().difference(_ongoing.remove(label));
2120
    if (elapsed.inMilliseconds >= 10) {
2130
      _elapsed[label] = elapsed;
214
    } else {
2150
      _elided++;
216
    }
217
2180
    super.afterFile(file, label, output, changed: changed);
219
  }
220
}
221
222
/// A decorating reporter that sets the exit code to 1 if any changes are made.
223
class SetExitReporter extends _ReporterDecorator {
2240
  SetExitReporter(OutputReporter inner) : super(inner);
225
226
  /// Describe the processed file at [path] whose formatted result is [output].
227
  ///
228
  /// If the contents of the file are the same as the formatted output,
229
  /// [changed] will be false.
2300
  void afterFile(File file, String label, SourceCode output, {bool changed}) {
2310
    if (changed) exitCode = 1;
232
2330
    super.afterFile(file, label, output, changed: changed);
234
  }
235
}