Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fancy styling and premade progress bars #4

Merged
merged 3 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gleam.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ version = "1.0.0"

[dependencies]
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
ansi = ">= 0.1.0 and < 1.0.0"
gleam_community_ansi = ">= 1.4.0 and < 2.0.0"

[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"
7 changes: 5 additions & 2 deletions manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
# You typically do not need to edit this file

packages = [
{ name = "ansi", version = "0.1.0", build_tools = ["rebar3"], requirements = [], otp_app = "ansi", source = "hex", outer_checksum = "4BF92B41E29E0480350A3260DC3F7ED52073C56FE236EFCAB1680A5E4C3923A5" },
{ 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" },
{ 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" },
{ name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" },
{ name = "gleam_stdlib", version = "0.38.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "663CF11861179AF415A625307447775C09404E752FF99A24E2057C835319F1BE" },
{ name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" },
{ name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" },
]

[requirements]
ansi = { version = ">= 0.1.0 and < 1.0.0"}
gleam_community_ansi = { version = ">= 1.4.0 and < 2.0.0"}
gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
10 changes: 5 additions & 5 deletions src/glitzer.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ pub fn sleep(ms: Int) -> a

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

do_something(bar, 0)
}

fn do_something(bar, count) {
case count < 100 {
case count < 10 {
True -> {
let bar = case count > 50 {
let bar = case count > 5 {
True -> progress.finish(bar)
False -> progress.tick(bar)
}
progress.print_bar(bar)
sleep(15)
sleep(100)
do_something(bar, count + 1)
}
False -> Nil
Expand Down
2 changes: 2 additions & 0 deletions src/glitzer/codes.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ pub const return_home_code = "\u{001b}[H"

/// Print this code to return to the start of the line
pub const return_line_start_code = "\r"

pub const hide_cursor_code = "\u{001b}[?25l"
153 changes: 144 additions & 9 deletions src/glitzer/progress.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import gleam/option.{type Option}
import gleam/string
import gleam/string_builder.{type StringBuilder}

import gleam_community/ansi

import glitzer/codes

/// A `String` with only one character.
Expand Down Expand Up @@ -48,24 +50,113 @@ pub opaque type ProgressStyle {
empty: Char,
fill: Char,
fill_finished: Option(Char),
fill_head: Option(Char),
fill_head_finished: Option(Char),
length: Int,
state: State,
)
}

/// Create and return a default style for a progress bar.
// SECTION: progress bar definitions

/// Create and return a default progress bar.
pub fn default_bar() -> ProgressStyle {
ProgressStyle(
left: "[",
right: "]",
empty: Char(" "),
fill: Char("#"),
fill_finished: option.None,
fill_head_finished: option.None,
fill_head: option.None,
length: 100,
state: State(progress: 0, finished: False),
)
}

/// Create and return a fancy and slim progress bar (inspired by pip).
pub fn slim_bar() -> ProgressStyle {
let sym = "\u{2014}"
ProgressStyle(
left: "",
right: "",
empty: Char(" "),
fill: Char(sym),
fill_finished: option.None,
fill_head: option.None,
fill_head_finished: option.None,
length: 100,
state: State(progress: 0, finished: False),
)
}

/// Create and return a fancy and slim progress bar (inspired by pip).
pub fn fancy_slim_bar() -> ProgressStyle {
let sym = "\u{2014}"
ProgressStyle(
left: "",
right: "",
empty: Char(ansi.blue(sym)),
fill: Char(ansi.red(sym)),
fill_finished: option.Some(Char(ansi.green(sym))),
fill_head: option.None,
fill_head_finished: option.None,
length: 100,
state: State(progress: 0, finished: False),
)
}

/// Create and return a fancy and slim progress bar with an arrow head.
pub fn fancy_slim_arrow_bar() -> ProgressStyle {
let sym = "\u{2014}"
let sym_head = "\u{2192}"
ProgressStyle(
left: "",
right: "",
empty: Char(ansi.blue(sym)),
fill: Char(ansi.red(sym)),
fill_finished: option.Some(Char(ansi.green(sym))),
fill_head: option.Some(Char(ansi.red(sym_head))),
fill_head_finished: option.Some(Char(ansi.green(sym_head))),
length: 100,
state: State(progress: 0, finished: False),
)
}

pub fn thick_bar() -> ProgressStyle {
let sym = "\u{2588}"
let empty_sym = "\u{2592}"
ProgressStyle(
left: "",
right: "",
empty: Char(empty_sym),
fill: Char(sym),
fill_finished: option.None,
fill_head: option.None,
fill_head_finished: option.None,
length: 100,
state: State(progress: 0, finished: False),
)
}

pub fn fancy_thick_bar() -> ProgressStyle {
let sym = "\u{2588}"
let empty_sym = "\u{2592}"
ProgressStyle(
left: "",
right: "",
empty: Char(ansi.blue(empty_sym)),
fill: Char(ansi.red(sym)),
fill_finished: option.Some(Char(ansi.green(sym))),
fill_head: option.None,
fill_head_finished: option.None,
length: 100,
state: State(progress: 0, finished: False),
)
}

// ENDSECTION: progress bar definitions

/// Create a new (completely empty) progress bar.
///
/// <details>
Expand All @@ -89,6 +180,8 @@ pub fn new_bar() -> ProgressStyle {
empty: Char(" "),
fill: Char(" "),
fill_finished: option.None,
fill_head: option.None,
fill_head_finished: option.None,
length: 0,
state: State(progress: 0, finished: False),
)
Expand Down Expand Up @@ -130,6 +223,11 @@ pub fn with_fill_finished(
ProgressStyle(..bar, fill_finished: option.Some(char))
}

/// Add a head to the progress bar.
pub fn with_fill_head(bar bar: ProgressStyle, fill char: Char) -> ProgressStyle {
ProgressStyle(..bar, fill_head: option.Some(char))
}

/// Add length to a progress bar.
pub fn with_length(bar bar: ProgressStyle, length len: Int) -> ProgressStyle {
ProgressStyle(..bar, length: len)
Expand Down Expand Up @@ -188,7 +286,8 @@ pub fn print_bar(bar bar: ProgressStyle) {
|> string_builder.to_string

io.print_error(
codes.clear_line_code
codes.hide_cursor_code
<> codes.clear_line_code
<> codes.return_line_start_code
<> bar.left
<> fill
Expand All @@ -204,15 +303,12 @@ fn build_progress_fill(
) -> StringBuilder {
let fill = case left_nonempty > 0 {
True -> {
case bar.state.finished {
True ->
string_builder.append(
fill,
option.unwrap(bar.fill_finished, bar.fill).char,
)
False -> string_builder.append(fill, bar.fill.char)
case left_nonempty == 1 {
True -> get_finished_head_fill(fill, bar)
False -> get_finished_fill(fill, bar)
}
}
// fill all thats left with empty characters
False -> string_builder.append(fill, bar.empty.char)
}

Expand All @@ -221,3 +317,42 @@ fn build_progress_fill(
False -> fill
}
}

fn get_finished_head_fill(
fill: StringBuilder,
bar: ProgressStyle,
) -> StringBuilder {
case bar.state.finished {
True ->
// build the finished style
string_builder.append(
fill,
option.unwrap(
// if head_finished exists
bar.fill_head_finished,
option.unwrap(
// if only a head exist
bar.fill_head,
// otherwise, use fill_finished of fill (if fill_finished doesnt exist)
option.unwrap(bar.fill_finished, bar.fill),
),
).char,
)
// build the unfinished style
False ->
string_builder.append(fill, option.unwrap(bar.fill_head, bar.fill).char)
}
}

fn get_finished_fill(fill: StringBuilder, bar: ProgressStyle) -> StringBuilder {
case bar.state.finished {
True ->
// build the finished style
string_builder.append(
fill,
option.unwrap(bar.fill_finished, bar.fill).char,
)
// build the unfinished style
False -> string_builder.append(fill, bar.fill.char)
}
}
Loading