@@ -3,6 +3,8 @@ import gleam/option.{type Option}
3
3
import gleam/string
4
4
import gleam/string_builder . { type StringBuilder }
5
5
6
+ import gleam_community/ansi
7
+
6
8
import glitzer/codes
7
9
8
10
/// A `String` with only one character.
@@ -48,24 +50,113 @@ pub opaque type ProgressStyle {
48
50
empty : Char ,
49
51
fill : Char ,
50
52
fill_finished : Option ( Char ) ,
53
+ fill_head : Option ( Char ) ,
54
+ fill_head_finished : Option ( Char ) ,
51
55
length : Int ,
52
56
state : State ,
53
57
)
54
58
}
55
59
56
- /// Create and return a default style for a progress bar.
60
+ // SECTION: progress bar definitions
61
+
62
+ /// Create and return a default progress bar.
57
63
pub fn default_bar ( ) -> ProgressStyle {
58
64
ProgressStyle (
59
65
left : "[" ,
60
66
right : "]" ,
61
67
empty : Char ( " " ) ,
62
68
fill : Char ( "#" ) ,
63
69
fill_finished : option . None ,
70
+ fill_head_finished : option . None ,
71
+ fill_head : option . None ,
72
+ length : 100 ,
73
+ state : State ( progress : 0 , finished : False ) ,
74
+ )
75
+ }
76
+
77
+ /// Create and return a fancy and slim progress bar (inspired by pip).
78
+ pub fn slim_bar ( ) -> ProgressStyle {
79
+ let sym = "\u{2014} "
80
+ ProgressStyle (
81
+ left : "" ,
82
+ right : "" ,
83
+ empty : Char ( " " ) ,
84
+ fill : Char ( sym ) ,
85
+ fill_finished : option . None ,
86
+ fill_head : option . None ,
87
+ fill_head_finished : option . None ,
88
+ length : 100 ,
89
+ state : State ( progress : 0 , finished : False ) ,
90
+ )
91
+ }
92
+
93
+ /// Create and return a fancy and slim progress bar (inspired by pip).
94
+ pub fn fancy_slim_bar ( ) -> ProgressStyle {
95
+ let sym = "\u{2014} "
96
+ ProgressStyle (
97
+ left : "" ,
98
+ right : "" ,
99
+ empty : Char ( ansi . blue ( sym ) ) ,
100
+ fill : Char ( ansi . red ( sym ) ) ,
101
+ fill_finished : option . Some ( Char ( ansi . green ( sym ) ) ) ,
102
+ fill_head : option . None ,
103
+ fill_head_finished : option . None ,
104
+ length : 100 ,
105
+ state : State ( progress : 0 , finished : False ) ,
106
+ )
107
+ }
108
+
109
+ /// Create and return a fancy and slim progress bar with an arrow head.
110
+ pub fn fancy_slim_arrow_bar ( ) -> ProgressStyle {
111
+ let sym = "\u{2014} "
112
+ let sym_head = "\u{2192} "
113
+ ProgressStyle (
114
+ left : "" ,
115
+ right : "" ,
116
+ empty : Char ( ansi . blue ( sym ) ) ,
117
+ fill : Char ( ansi . red ( sym ) ) ,
118
+ fill_finished : option . Some ( Char ( ansi . green ( sym ) ) ) ,
119
+ fill_head : option . Some ( Char ( ansi . red ( sym_head ) ) ) ,
120
+ fill_head_finished : option . Some ( Char ( ansi . green ( sym_head ) ) ) ,
121
+ length : 100 ,
122
+ state : State ( progress : 0 , finished : False ) ,
123
+ )
124
+ }
125
+
126
+ pub fn thick_bar ( ) -> ProgressStyle {
127
+ let sym = "\u{2588} "
128
+ let empty_sym = "\u{2592} "
129
+ ProgressStyle (
130
+ left : "" ,
131
+ right : "" ,
132
+ empty : Char ( empty_sym ) ,
133
+ fill : Char ( sym ) ,
134
+ fill_finished : option . None ,
135
+ fill_head : option . None ,
136
+ fill_head_finished : option . None ,
137
+ length : 100 ,
138
+ state : State ( progress : 0 , finished : False ) ,
139
+ )
140
+ }
141
+
142
+ pub fn fancy_thick_bar ( ) -> ProgressStyle {
143
+ let sym = "\u{2588} "
144
+ let empty_sym = "\u{2592} "
145
+ ProgressStyle (
146
+ left : "" ,
147
+ right : "" ,
148
+ empty : Char ( ansi . blue ( empty_sym ) ) ,
149
+ fill : Char ( ansi . red ( sym ) ) ,
150
+ fill_finished : option . Some ( Char ( ansi . green ( sym ) ) ) ,
151
+ fill_head : option . None ,
152
+ fill_head_finished : option . None ,
64
153
length : 100 ,
65
154
state : State ( progress : 0 , finished : False ) ,
66
155
)
67
156
}
68
157
158
+ // ENDSECTION: progress bar definitions
159
+
69
160
/// Create a new (completely empty) progress bar.
70
161
///
71
162
/// <details>
@@ -89,6 +180,8 @@ pub fn new_bar() -> ProgressStyle {
89
180
empty : Char ( " " ) ,
90
181
fill : Char ( " " ) ,
91
182
fill_finished : option . None ,
183
+ fill_head : option . None ,
184
+ fill_head_finished : option . None ,
92
185
length : 0 ,
93
186
state : State ( progress : 0 , finished : False ) ,
94
187
)
@@ -130,6 +223,11 @@ pub fn with_fill_finished(
130
223
ProgressStyle ( .. bar , fill_finished : option . Some ( char ) )
131
224
}
132
225
226
+ /// Add a head to the progress bar.
227
+ pub fn with_fill_head ( bar bar : ProgressStyle , fill char : Char ) -> ProgressStyle {
228
+ ProgressStyle ( .. bar , fill_head : option . Some ( char ) )
229
+ }
230
+
133
231
/// Add length to a progress bar.
134
232
pub fn with_length ( bar bar : ProgressStyle , length len : Int ) -> ProgressStyle {
135
233
ProgressStyle ( .. bar , length : len )
@@ -188,7 +286,8 @@ pub fn print_bar(bar bar: ProgressStyle) {
188
286
|> string_builder . to_string
189
287
190
288
io . print_error (
191
- codes . clear_line_code
289
+ codes . hide_cursor_code
290
+ <> codes . clear_line_code
192
291
<> codes . return_line_start_code
193
292
<> bar . left
194
293
<> fill
@@ -204,15 +303,12 @@ fn build_progress_fill(
204
303
) -> StringBuilder {
205
304
let fill = case left_nonempty > 0 {
206
305
True -> {
207
- case bar . state . finished {
208
- True ->
209
- string_builder . append (
210
- fill ,
211
- option . unwrap ( bar . fill_finished , bar . fill ) . char ,
212
- )
213
- False -> string_builder . append ( fill , bar . fill . char )
306
+ case left_nonempty == 1 {
307
+ True -> get_finished_head_fill ( fill , bar )
308
+ False -> get_finished_fill ( fill , bar )
214
309
}
215
310
}
311
+ // fill all thats left with empty characters
216
312
False -> string_builder . append ( fill , bar . empty . char )
217
313
}
218
314
@@ -221,3 +317,42 @@ fn build_progress_fill(
221
317
False -> fill
222
318
}
223
319
}
320
+
321
+ fn get_finished_head_fill (
322
+ fill : StringBuilder ,
323
+ bar : ProgressStyle ,
324
+ ) -> StringBuilder {
325
+ case bar . state . finished {
326
+ True ->
327
+ // build the finished style
328
+ string_builder . append (
329
+ fill ,
330
+ option . unwrap (
331
+ // if head_finished exists
332
+ bar . fill_head_finished ,
333
+ option . unwrap (
334
+ // if only a head exist
335
+ bar . fill_head ,
336
+ // otherwise, use fill_finished of fill (if fill_finished doesnt exist)
337
+ option . unwrap ( bar . fill_finished , bar . fill ) ,
338
+ ) ,
339
+ ) . char ,
340
+ )
341
+ // build the unfinished style
342
+ False ->
343
+ string_builder . append ( fill , option . unwrap ( bar . fill_head , bar . fill ) . char )
344
+ }
345
+ }
346
+
347
+ fn get_finished_fill ( fill : StringBuilder , bar : ProgressStyle ) -> StringBuilder {
348
+ case bar . state . finished {
349
+ True ->
350
+ // build the finished style
351
+ string_builder . append (
352
+ fill ,
353
+ option . unwrap ( bar . fill_finished , bar . fill ) . char ,
354
+ )
355
+ // build the unfinished style
356
+ False -> string_builder . append ( fill , bar . fill . char )
357
+ }
358
+ }
0 commit comments