3
3
// BSD-style license that can be found in the LICENSE file.
4
4
5
5
import 'dart:convert' ;
6
- import 'dart:io' show stderr, stdout ;
6
+ import 'dart:io' as io ;
7
7
8
8
import 'package:analyzer/file_system/file_system.dart' ;
9
- import 'package:cli_util/cli_logging.dart' show Ansi;
10
9
import 'package:dartdoc/src/dartdoc_options.dart' ;
11
10
import 'package:dartdoc/src/package_meta.dart' ;
11
+ import 'package:dartdoc/src/progress_bar.dart' ;
12
12
import 'package:logging/logging.dart' ;
13
13
14
14
final _logger = Logger ('dartdoc' );
15
15
16
16
/// A custom [Level] for tracking file writes and verification.
17
17
///
18
18
/// Has a value of `501` – one more than [Level.FINE] .
19
- const Level progressLevel = Level ('PROGRESS' , 501 );
19
+ const Level _progressLevel = Level ('PROGRESS' , 501 );
20
20
21
21
/// A custom [Level] for errant print statements.
22
22
///
@@ -36,13 +36,37 @@ void logDebug(String message) {
36
36
}
37
37
38
38
void logProgress (String message) {
39
- _logger.log (progressLevel , message);
39
+ _logger.log (_progressLevel , message);
40
40
}
41
41
42
42
void logPrint (String message) {
43
43
_logger.log (printLevel, message);
44
44
}
45
45
46
+ /// Creates a new deterministic progress bar, and displays it (with zero
47
+ /// progress).
48
+ void progressBarStart (int totalTickCount) {
49
+ _DartdocLogger .instance.progressBarStart (totalTickCount);
50
+ }
51
+
52
+ /// Increments the progress of the current progress bar.
53
+ void progressBarTick () {
54
+ _DartdocLogger .instance.progressBarTick ();
55
+ }
56
+
57
+ /// Updates the total length of the current progress bar.
58
+ void progressBarUpdateTickCount (int totalTickCount) {
59
+ _DartdocLogger .instance.progressBarUpdateTickCount (totalTickCount);
60
+ }
61
+
62
+ /// Completes the current progress bar.
63
+ ///
64
+ /// It is important to call this after progress is complete, in case rounding
65
+ /// errors leave the displayed progress bar at something less than 100%.
66
+ void progressBarComplete () {
67
+ _DartdocLogger .instance.progressBarComplete ();
68
+ }
69
+
46
70
abstract class Jsonable {
47
71
/// The `String` to print when in human-readable mode
48
72
String get text;
@@ -54,47 +78,61 @@ abstract class Jsonable {
54
78
String toString () => text;
55
79
}
56
80
57
- void startLogging (LoggingContext config) {
58
- // By default, get all log output at `progressLevel` or greater.
59
- // This allows us to capture progress events and print `...`.
60
- // Change this to `Level.FINE` for debug logging.
61
- Logger .root.level = progressLevel;
62
- if (config.json) {
63
- Logger .root.onRecord.listen ((record) {
64
- if (record.level == progressLevel) {
65
- return ;
66
- }
81
+ class _DartdocLogger {
82
+ /// By default, we use a quiet logger.
83
+ ///
84
+ /// This field can be re-set, with [startLogging] .
85
+ static _DartdocLogger instance =
86
+ _DartdocLogger ._(isJson: false , isQuiet: true , showProgress: false );
67
87
68
- var output = < String , dynamic > { 'level' : record.level.name} ;
88
+ final bool _showProgressBar ;
69
89
70
- if (record.object is Jsonable ) {
71
- output['data' ] = record.object;
72
- } else {
73
- output['message' ] = record.message;
74
- }
90
+ ProgressBar ? _progressBar;
75
91
76
- print (json.encode (output));
77
- });
78
- } else {
92
+ _DartdocLogger ._({
93
+ required bool isJson,
94
+ required bool isQuiet,
95
+ required bool showProgress,
96
+ }) : _showProgressBar = showProgress && ! isJson && ! isQuiet {
97
+ // By default, get all log output at `progressLevel` or greater.
98
+ // This allows us to capture progress events and print `...`.
99
+ // Change this to `Level.FINE` for debug logging.
100
+ Logger .root.level = _progressLevel;
101
+ if (isJson) {
102
+ Logger .root.onRecord.listen (_onJsonRecord);
103
+ return ;
104
+ }
105
+
106
+ _initialize (isQuiet: isQuiet, showProgress: showProgress);
107
+ }
108
+
109
+ /// Initializes this as a non-JSON logger.
110
+ ///
111
+ /// This method mostly sets up callback behavior for each logged message.
112
+ void _initialize ({required bool isQuiet, required bool showProgress}) {
79
113
final stopwatch = Stopwatch ()..start ();
80
114
81
115
// Used to track if we're printing `...` to show progress.
82
116
// Allows unified new-line tracking
83
117
var writingProgress = false ;
84
- var ansi = Ansi (Ansi .terminalSupportsAnsi);
85
118
var spinnerIndex = 0 ;
86
119
const spinner = ['-' , r'\' , '|' , '/' ];
87
120
88
121
Logger .root.onRecord.listen ((record) {
89
- if (record.level == progressLevel) {
90
- if (! config.quiet &&
91
- config.showProgress &&
122
+ if (record.level == progressBarUpdate) {
123
+ io.stdout.write (record.message);
124
+ return ;
125
+ }
126
+
127
+ if (record.level == _progressLevel) {
128
+ if (! isQuiet &&
129
+ showProgress &&
92
130
stopwatch.elapsed.inMilliseconds > 125 ) {
93
131
if (writingProgress = false ) {
94
- stdout.write (' ' );
132
+ io. stdout.write (' ' );
95
133
}
96
134
writingProgress = true ;
97
- stdout.write ('${ ansi . backspace } ${spinner [spinnerIndex ]}' );
135
+ io. stdout.write ('$_backspace ${spinner [spinnerIndex ]}' );
98
136
spinnerIndex = (spinnerIndex + 1 ) % spinner.length;
99
137
stopwatch.reset ();
100
138
}
@@ -103,26 +141,79 @@ void startLogging(LoggingContext config) {
103
141
104
142
stopwatch.reset ();
105
143
if (writingProgress) {
106
- stdout.write ('${ ansi . backspace } ${ ansi . backspace } ' );
144
+ io. stdout.write ('$_backspace $ _backspace ' );
107
145
}
108
146
var message = record.message;
109
147
assert (message.isNotEmpty);
110
148
111
149
if (record.level < Level .WARNING ) {
112
- if (! config.quiet ) {
150
+ if (! isQuiet ) {
113
151
print (message);
114
152
}
115
153
} else {
116
154
if (writingProgress) {
117
155
// Some console implementations, like IntelliJ, apparently need
118
156
// the backspace to occur for stderr as well.
119
- stderr.write ('${ ansi . backspace } ${ ansi . backspace } ' );
157
+ io. stderr.write ('$_backspace $ _backspace ' );
120
158
}
121
- stderr.writeln (message);
159
+ io. stderr.writeln (message);
122
160
}
123
161
writingProgress = false ;
124
162
});
125
163
}
164
+
165
+ void progressBarStart (int totalTickCount) {
166
+ if (! _showProgressBar) {
167
+ return ;
168
+ }
169
+ _progressBar = ProgressBar (_logger, totalTickCount);
170
+ }
171
+
172
+ void progressBarTick () {
173
+ if (! _showProgressBar) {
174
+ return ;
175
+ }
176
+ _progressBar? .tick ();
177
+ }
178
+
179
+ void progressBarUpdateTickCount (int totalTickCount) {
180
+ if (! _showProgressBar) {
181
+ return ;
182
+ }
183
+ _progressBar? .totalTickCount = totalTickCount;
184
+ }
185
+
186
+ void progressBarComplete () {
187
+ if (! _showProgressBar) {
188
+ return ;
189
+ }
190
+ _progressBar? .complete ();
191
+ _progressBar = null ;
192
+ }
193
+
194
+ void _onJsonRecord (LogRecord record) {
195
+ if (record.level == _progressLevel) {
196
+ return ;
197
+ }
198
+
199
+ var output = < String , dynamic > {'level' : record.level.name};
200
+
201
+ if (record.object is Jsonable ) {
202
+ output['data' ] = record.object;
203
+ } else {
204
+ output['message' ] = record.message;
205
+ }
206
+
207
+ print (json.encode (output));
208
+ }
209
+ }
210
+
211
+ void startLogging (LoggingContext config) {
212
+ _DartdocLogger .instance = _DartdocLogger ._(
213
+ isJson: config.json,
214
+ isQuiet: config.quiet,
215
+ showProgress: config.showProgress,
216
+ );
126
217
}
127
218
128
219
mixin LoggingContext on DartdocOptionContextBase {
@@ -137,22 +228,34 @@ List<DartdocOption<Object>> createLoggingOptions(
137
228
PackageMetaProvider packageMetaProvider) {
138
229
var resourceProvider = packageMetaProvider.resourceProvider;
139
230
return [
140
- DartdocOptionArgOnly <bool >('json' , false , resourceProvider,
141
- help: 'Prints out progress JSON maps. One entry per line.' ,
142
- negatable: true ),
143
231
DartdocOptionArgOnly <bool >(
144
- 'showProgress' , Ansi .terminalSupportsAnsi, resourceProvider,
145
- help: 'Display progress indications to console stdout.' ,
146
- negatable: true ),
147
- DartdocOptionArgSynth <bool >('quiet' ,
148
- (DartdocSyntheticOption <Object > option, Folder dir) {
149
- if (option.parent['generateDocs' ].valueAt (dir) == false ) {
150
- return true ;
151
- }
152
- return false ;
153
- }, resourceProvider,
154
- abbr: 'q' ,
155
- negatable: true ,
156
- help: 'Only show warnings and errors; silence all other output.' ),
232
+ 'json' ,
233
+ false ,
234
+ resourceProvider,
235
+ help: 'Prints out progress JSON maps. One entry per line.' ,
236
+ negatable: true ,
237
+ ),
238
+ DartdocOptionArgOnly <bool >(
239
+ 'showProgress' ,
240
+ _terminalSupportsAnsi,
241
+ resourceProvider,
242
+ help: 'Display progress indications to console stdout.' ,
243
+ negatable: true ,
244
+ ),
245
+ DartdocOptionArgSynth <bool >(
246
+ 'quiet' ,
247
+ (DartdocSyntheticOption <Object > option, Folder dir) =>
248
+ option.parent['generateDocs' ].valueAt (dir) == false ,
249
+ resourceProvider,
250
+ abbr: 'q' ,
251
+ negatable: true ,
252
+ help: 'Only show warnings and errors; silence all other output.' ,
253
+ ),
157
254
];
158
255
}
256
+
257
+ const String _backspace = '\b ' ;
258
+
259
+ bool get _terminalSupportsAnsi =>
260
+ io.stdout.supportsAnsiEscapes &&
261
+ io.stdioType (io.stdout) == io.StdioType .terminal;
0 commit comments