Index: lib/src/log.dart |
diff --git a/lib/src/log.dart b/lib/src/log.dart |
index 0b14ab30eb0b2050557c973e18d2ddb75903856c..50db65948fc0bcc86f88df754d7e86c26487cc8c 100644 |
--- a/lib/src/log.dart |
+++ b/lib/src/log.dart |
@@ -48,6 +48,8 @@ Transcript<Entry> _transcript; |
/// This will also be in [_progresses]. |
Progress _animatedProgress; |
+_Collapser _collapser; |
+ |
final _cyan = getSpecial('\u001b[36m'); |
final _green = getSpecial('\u001b[32m'); |
final _magenta = getSpecial('\u001b[35m'); |
@@ -209,6 +211,9 @@ void fine(message) => write(Level.FINE, message); |
/// Logs [message] at [level]. |
void write(Level level, message) { |
+ // Don't allow interleaving collapsible messages with other kinds. |
+ if (_collapser != null) _collapser.end(); |
+ |
message = message.toString(); |
var lines = splitLines(message); |
@@ -409,6 +414,31 @@ void unmuteProgress() { |
_numMutes--; |
} |
+/// Logs a collapsible [message]. |
+/// |
+/// If a number of collapsible messages are printed in short succession, they |
+/// are collapsed to just showing [template] with "##" replaced with the number |
+/// of collapsed messages. Avoids spamming the output with not-very-interesting |
+/// output. |
+void collapsible(String message, String template) { |
+ // Only collapse messages when the output is not verbose. |
+ if (verbosity._loggers[Level.MESSAGE] != _logToStdout) { |
+ write(Level.MESSAGE, message); |
+ return; |
+ } |
+ |
+ // If this is a different set of collapsed messages, end the previous ones. |
+ if (_collapser != null && _collapser._template != template) { |
+ _collapser.end(); |
+ } |
+ |
+ if (_collapser != null) { |
+ _collapser.increment(); |
+ } else { |
+ _collapser = new _Collapser(message, template); |
+ } |
+} |
+ |
/// Wraps [text] in the ANSI escape codes to make it bold when on a platform |
/// that supports that. |
/// |
@@ -545,3 +575,57 @@ class _JsonLogger { |
print(JSON.encode(message)); |
} |
} |
+ |
+/// Collapses a series of collapsible messages into a single line of output if |
+/// they happen within a short window of time. |
+class _Collapser { |
+ /// The window of time where a series of calls to [collapsible] will be |
+ /// collapsed to a single message. |
+ static final _window = new Duration(milliseconds: 100); |
+ |
+ /// The Timer used to coalesce a number of collapsible messages. |
+ /// |
+ /// This is `null` if no collapsible messages are waiting to be displayed. |
+ Timer _timer; |
+ |
+ /// The first collapsible message waiting to be displayed. |
+ String _firstMessage; |
+ |
+ /// The template used to display the number of collapsed messages when more |
+ /// than one collapsible message is logged within the window of time. |
+ /// |
+ /// Inside the template, "##" will be replaced with the number of collapsed |
+ /// messages. |
+ String _template; |
+ |
+ /// The number of collapsible messages that are waiting to be logged. |
+ int _count = 1; |
+ |
+ _Collapser(this._firstMessage, this._template) { |
+ _initTimer(); |
+ } |
+ |
+ void increment() { |
+ // Reset the timer. |
+ _timer.cancel(); |
+ _initTimer(); |
+ |
+ _count++; |
+ } |
+ |
+ void end() { |
+ // Clear this first so we don't stack overflow when we call message() below. |
+ _collapser = null; |
+ |
+ _timer.cancel(); |
+ if (_count == 1) { |
+ message(_firstMessage); |
+ } else { |
+ message(_template.replaceAll("##", _count.toString())); |
nweiz
2015/08/05 22:57:14
I'd still really like to have this dynamically upd
Bob Nystrom
2015/08/05 23:49:23
Leaving as-is per our discussion.
|
+ } |
+ } |
+ |
+ void _initTimer() { |
+ _timer = new Timer(_window, end); |
+ } |
+} |