Skip to content

Commit 96c385a

Browse files
authored
feat: fancy styling and premade progress bars (#4)
1 parent 8f05c89 commit 96c385a

File tree

5 files changed

+157
-17
lines changed

5 files changed

+157
-17
lines changed

gleam.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ version = "1.0.0"
1414

1515
[dependencies]
1616
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
17-
ansi = ">= 0.1.0 and < 1.0.0"
17+
gleam_community_ansi = ">= 1.4.0 and < 2.0.0"
1818

1919
[dev-dependencies]
2020
gleeunit = ">= 1.0.0 and < 2.0.0"

manifest.toml

+5-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
# You typically do not need to edit this file
33

44
packages = [
5-
{ name = "ansi", version = "0.1.0", build_tools = ["rebar3"], requirements = [], otp_app = "ansi", source = "hex", outer_checksum = "4BF92B41E29E0480350A3260DC3F7ED52073C56FE236EFCAB1680A5E4C3923A5" },
5+
{ name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" },
6+
{ name = "gleam_community_colour", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "795964217EBEDB3DA656F5EB8F67D7AD22872EB95182042D3E7AFEF32D3FD2FE" },
7+
{ name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" },
68
{ name = "gleam_stdlib", version = "0.38.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "663CF11861179AF415A625307447775C09404E752FF99A24E2057C835319F1BE" },
79
{ name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" },
10+
{ name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" },
811
]
912

1013
[requirements]
11-
ansi = { version = ">= 0.1.0 and < 1.0.0"}
14+
gleam_community_ansi = { version = ">= 1.4.0 and < 2.0.0"}
1215
gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
1316
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }

src/glitzer.gleam

+5-5
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@ pub fn sleep(ms: Int) -> a
66

77
pub fn main() {
88
let bar =
9-
progress.default_bar()
10-
|> progress.with_fill_finished(progress.char_from_string("-"))
9+
progress.fancy_thick_bar()
10+
|> progress.with_length(10)
1111

1212
do_something(bar, 0)
1313
}
1414

1515
fn do_something(bar, count) {
16-
case count < 100 {
16+
case count < 10 {
1717
True -> {
18-
let bar = case count > 50 {
18+
let bar = case count > 5 {
1919
True -> progress.finish(bar)
2020
False -> progress.tick(bar)
2121
}
2222
progress.print_bar(bar)
23-
sleep(15)
23+
sleep(100)
2424
do_something(bar, count + 1)
2525
}
2626
False -> Nil

src/glitzer/codes.gleam

+2
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ pub const return_home_code = "\u{001b}[H"
99

1010
/// Print this code to return to the start of the line
1111
pub const return_line_start_code = "\r"
12+
13+
pub const hide_cursor_code = "\u{001b}[?25l"

src/glitzer/progress.gleam

+144-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import gleam/option.{type Option}
33
import gleam/string
44
import gleam/string_builder.{type StringBuilder}
55

6+
import gleam_community/ansi
7+
68
import glitzer/codes
79

810
/// A `String` with only one character.
@@ -48,24 +50,113 @@ pub opaque type ProgressStyle {
4850
empty: Char,
4951
fill: Char,
5052
fill_finished: Option(Char),
53+
fill_head: Option(Char),
54+
fill_head_finished: Option(Char),
5155
length: Int,
5256
state: State,
5357
)
5458
}
5559

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.
5763
pub fn default_bar() -> ProgressStyle {
5864
ProgressStyle(
5965
left: "[",
6066
right: "]",
6167
empty: Char(" "),
6268
fill: Char("#"),
6369
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,
64153
length: 100,
65154
state: State(progress: 0, finished: False),
66155
)
67156
}
68157

158+
// ENDSECTION: progress bar definitions
159+
69160
/// Create a new (completely empty) progress bar.
70161
///
71162
/// <details>
@@ -89,6 +180,8 @@ pub fn new_bar() -> ProgressStyle {
89180
empty: Char(" "),
90181
fill: Char(" "),
91182
fill_finished: option.None,
183+
fill_head: option.None,
184+
fill_head_finished: option.None,
92185
length: 0,
93186
state: State(progress: 0, finished: False),
94187
)
@@ -130,6 +223,11 @@ pub fn with_fill_finished(
130223
ProgressStyle(..bar, fill_finished: option.Some(char))
131224
}
132225

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+
133231
/// Add length to a progress bar.
134232
pub fn with_length(bar bar: ProgressStyle, length len: Int) -> ProgressStyle {
135233
ProgressStyle(..bar, length: len)
@@ -188,7 +286,8 @@ pub fn print_bar(bar bar: ProgressStyle) {
188286
|> string_builder.to_string
189287

190288
io.print_error(
191-
codes.clear_line_code
289+
codes.hide_cursor_code
290+
<> codes.clear_line_code
192291
<> codes.return_line_start_code
193292
<> bar.left
194293
<> fill
@@ -204,15 +303,12 @@ fn build_progress_fill(
204303
) -> StringBuilder {
205304
let fill = case left_nonempty > 0 {
206305
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)
214309
}
215310
}
311+
// fill all thats left with empty characters
216312
False -> string_builder.append(fill, bar.empty.char)
217313
}
218314

@@ -221,3 +317,42 @@ fn build_progress_fill(
221317
False -> fill
222318
}
223319
}
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

Comments
 (0)