/
progress.dart
132 lines (110 loc) · 3.15 KB
/
progress.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
part of 'mason_logger.dart';
/// {@template progress_options}
/// An object containing configuration for a [Progress] instance.
/// {@endtemplate}
class ProgressOptions {
/// {@macro progress_options}
const ProgressOptions({this.animation = const ProgressAnimation()});
/// The progress animation configuration.
final ProgressAnimation animation;
}
/// {@template progress_animation}
/// An object which contains configuration for the animation
/// of a [Progress] instance.
/// {@endtemplate}
class ProgressAnimation {
/// {@macro progress_animation}
const ProgressAnimation({this.frames = _defaultFrames});
static const _defaultFrames = [
'⠋',
'⠙',
'⠹',
'⠸',
'⠼',
'⠴',
'⠦',
'⠧',
'⠇',
'⠏'
];
/// The list of animation frames.
final List<String> frames;
}
/// {@template progress}
/// A class that can be used to display progress information to the user.
/// {@endtemplate}
class Progress {
/// {@macro progress}
Progress._(
this._message,
this._stdout,
this._level, {
ProgressOptions options = const ProgressOptions(),
}) : _stopwatch = Stopwatch(),
_options = options {
_stopwatch
..reset()
..start();
_timer = Timer.periodic(const Duration(milliseconds: 80), _onTick);
}
final ProgressOptions _options;
final io.Stdout _stdout;
final Level _level;
final Stopwatch _stopwatch;
late final Timer _timer;
String _message;
int _index = 0;
/// End the progress and mark it as completed.
void complete([String? update]) {
_stopwatch.stop();
_write(
'''$_clearLn${lightGreen.wrap('✓')} ${update ?? _message} $_time\n''',
);
_timer.cancel();
}
/// End the progress and mark it as failed.
void fail([String? update]) {
_timer.cancel();
_write('$_clearLn${red.wrap('✗')} ${update ?? _message} $_time\n');
_stopwatch.stop();
}
/// Update the progress message.
void update(String update) {
_write(_clearLn);
_message = update;
_onTick(_timer);
}
/// Cancel the progress and remove the written line.
void cancel() {
_timer.cancel();
_write(_clearLn);
_stopwatch.stop();
}
void _onTick(Timer _) {
_index++;
final frames = _options.animation.frames;
final char = frames.isEmpty ? '' : frames[_index % frames.length];
final prefix = char.isEmpty
? _clearMessageLength
: '${lightGreen.wrap('$_clearMessageLength$char')} ';
_write('$prefix$_message... $_time');
}
void _write(Object? object) {
if (_level.index > Level.info.index) return;
_stdout.write(object);
}
String get _clearMessageLength {
final length = _message.length + 4 + _time.length;
return '\b${'\b' * length}';
}
String get _clearLn => '$_clearMessageLength\u001b[2K';
String get _time {
final elapsedTime = _stopwatch.elapsed.inMilliseconds;
final displayInMilliseconds = elapsedTime < 100;
final time = displayInMilliseconds ? elapsedTime : elapsedTime / 1000;
final formattedTime = displayInMilliseconds
? '${time.toString()}ms'
: '${time.toStringAsFixed(1)}s';
return '${darkGray.wrap('($formattedTime)')}';
}
}