33// BSD-style license that can be found in the LICENSE file.
44
55import 'dart:convert' ;
6- import 'dart:io' show stderr, stdout ;
6+ import 'dart:io' as io ;
77
88import 'package:analyzer/file_system/file_system.dart' ;
9- import 'package:cli_util/cli_logging.dart' show Ansi;
109import 'package:dartdoc/src/dartdoc_options.dart' ;
1110import 'package:dartdoc/src/package_meta.dart' ;
11+ import 'package:dartdoc/src/progress_bar.dart' ;
1212import 'package:logging/logging.dart' ;
1313
1414final _logger = Logger ('dartdoc' );
1515
1616/// A custom [Level] for tracking file writes and verification.
1717///
1818/// Has a value of `501` – one more than [Level.FINE] .
19- const Level progressLevel = Level ('PROGRESS' , 501 );
19+ const Level _progressLevel = Level ('PROGRESS' , 501 );
2020
2121/// A custom [Level] for errant print statements.
2222///
@@ -36,13 +36,37 @@ void logDebug(String message) {
3636}
3737
3838void logProgress (String message) {
39- _logger.log (progressLevel , message);
39+ _logger.log (_progressLevel , message);
4040}
4141
4242void logPrint (String message) {
4343 _logger.log (printLevel, message);
4444}
4545
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+
4670abstract class Jsonable {
4771 /// The `String` to print when in human-readable mode
4872 String get text;
@@ -54,47 +78,61 @@ abstract class Jsonable {
5478 String toString () => text;
5579}
5680
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 );
6787
68- var output = < String , dynamic > { 'level' : record.level.name} ;
88+ final bool _showProgressBar ;
6989
70- if (record.object is Jsonable ) {
71- output['data' ] = record.object;
72- } else {
73- output['message' ] = record.message;
74- }
90+ ProgressBar ? _progressBar;
7591
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}) {
79113 final stopwatch = Stopwatch ()..start ();
80114
81115 // Used to track if we're printing `...` to show progress.
82116 // Allows unified new-line tracking
83117 var writingProgress = false ;
84- var ansi = Ansi (Ansi .terminalSupportsAnsi);
85118 var spinnerIndex = 0 ;
86119 const spinner = ['-' , r'\' , '|' , '/' ];
87120
88121 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 &&
92130 stopwatch.elapsed.inMilliseconds > 125 ) {
93131 if (writingProgress = false ) {
94- stdout.write (' ' );
132+ io. stdout.write (' ' );
95133 }
96134 writingProgress = true ;
97- stdout.write ('${ ansi . backspace } ${spinner [spinnerIndex ]}' );
135+ io. stdout.write ('$_backspace ${spinner [spinnerIndex ]}' );
98136 spinnerIndex = (spinnerIndex + 1 ) % spinner.length;
99137 stopwatch.reset ();
100138 }
@@ -103,26 +141,79 @@ void startLogging(LoggingContext config) {
103141
104142 stopwatch.reset ();
105143 if (writingProgress) {
106- stdout.write ('${ ansi . backspace } ${ ansi . backspace } ' );
144+ io. stdout.write ('$_backspace $ _backspace ' );
107145 }
108146 var message = record.message;
109147 assert (message.isNotEmpty);
110148
111149 if (record.level < Level .WARNING ) {
112- if (! config.quiet ) {
150+ if (! isQuiet ) {
113151 print (message);
114152 }
115153 } else {
116154 if (writingProgress) {
117155 // Some console implementations, like IntelliJ, apparently need
118156 // the backspace to occur for stderr as well.
119- stderr.write ('${ ansi . backspace } ${ ansi . backspace } ' );
157+ io. stderr.write ('$_backspace $ _backspace ' );
120158 }
121- stderr.writeln (message);
159+ io. stderr.writeln (message);
122160 }
123161 writingProgress = false ;
124162 });
125163 }
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+ );
126217}
127218
128219mixin LoggingContext on DartdocOptionContextBase {
@@ -137,22 +228,34 @@ List<DartdocOption<Object>> createLoggingOptions(
137228 PackageMetaProvider packageMetaProvider) {
138229 var resourceProvider = packageMetaProvider.resourceProvider;
139230 return [
140- DartdocOptionArgOnly <bool >('json' , false , resourceProvider,
141- help: 'Prints out progress JSON maps. One entry per line.' ,
142- negatable: true ),
143231 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+ ),
157254 ];
158255}
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