From 45f2521676fe301a1a4763e97b23c1a40bd4ad4c Mon Sep 17 00:00:00 2001 From: miampf Date: Tue, 4 Jun 2024 23:44:35 +0200 Subject: [PATCH 1/3] added a pip inspired progress bar --- gleam.toml | 2 +- manifest.toml | 7 +++++-- src/glitzer.gleam | 4 +--- src/glitzer/codes.gleam | 2 ++ src/glitzer/progress.gleam | 21 +++++++++++++++++++-- 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/gleam.toml b/gleam.toml index 84d0e11..bb8c079 100644 --- a/gleam.toml +++ b/gleam.toml @@ -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" diff --git a/manifest.toml b/manifest.toml index 37f3e29..2c69a61 100644 --- a/manifest.toml +++ b/manifest.toml @@ -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" } diff --git a/src/glitzer.gleam b/src/glitzer.gleam index 7ae3682..bea9e64 100644 --- a/src/glitzer.gleam +++ b/src/glitzer.gleam @@ -5,9 +5,7 @@ import glitzer/progress pub fn sleep(ms: Int) -> a pub fn main() { - let bar = - progress.default_bar() - |> progress.with_fill_finished(progress.char_from_string("-")) + let bar = progress.fancy_slim_bar() do_something(bar, 0) } diff --git a/src/glitzer/codes.gleam b/src/glitzer/codes.gleam index c409ef1..1ebd118 100644 --- a/src/glitzer/codes.gleam +++ b/src/glitzer/codes.gleam @@ -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" diff --git a/src/glitzer/progress.gleam b/src/glitzer/progress.gleam index 56f5eb6..5800a4c 100644 --- a/src/glitzer/progress.gleam +++ b/src/glitzer/progress.gleam @@ -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. @@ -53,7 +55,7 @@ pub opaque type ProgressStyle { ) } -/// Create and return a default style for a progress bar. +/// Create and return a default progress bar. pub fn default_bar() -> ProgressStyle { ProgressStyle( left: "[", @@ -66,6 +68,20 @@ pub fn default_bar() -> ProgressStyle { ) } +/// Create and return a fancy 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))), + length: 100, + state: State(progress: 0, finished: False), + ) +} + /// Create a new (completely empty) progress bar. /// ///
@@ -188,7 +204,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 From d4e75c4abad7c72313c743398a145800095d017a Mon Sep 17 00:00:00 2001 From: miampf Date: Wed, 5 Jun 2024 00:02:41 +0200 Subject: [PATCH 2/3] implemented head and head_finished --- src/glitzer.gleam | 11 ++++--- src/glitzer/progress.gleam | 63 +++++++++++++++++++++++++++++++++----- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/src/glitzer.gleam b/src/glitzer.gleam index bea9e64..822e622 100644 --- a/src/glitzer.gleam +++ b/src/glitzer.gleam @@ -5,20 +5,23 @@ import glitzer/progress pub fn sleep(ms: Int) -> a pub fn main() { - let bar = progress.fancy_slim_bar() + let bar = + progress.fancy_slim_bar() + |> progress.with_fill_head(progress.char_from_string(">")) + |> 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 diff --git a/src/glitzer/progress.gleam b/src/glitzer/progress.gleam index 5800a4c..0a09990 100644 --- a/src/glitzer/progress.gleam +++ b/src/glitzer/progress.gleam @@ -50,6 +50,8 @@ 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, ) @@ -63,6 +65,8 @@ pub fn default_bar() -> ProgressStyle { 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), ) @@ -77,6 +81,8 @@ pub fn fancy_slim_bar() -> ProgressStyle { 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), ) @@ -105,6 +111,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), ) @@ -146,6 +154,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) @@ -221,15 +234,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) } @@ -238,3 +248,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) + } +} From d2723f95ffc9969396a6ce54d468be13e10d1a8b Mon Sep 17 00:00:00 2001 From: miampf Date: Wed, 5 Jun 2024 00:21:26 +0200 Subject: [PATCH 3/3] added some premade progress bar definitions --- src/glitzer.gleam | 3 +- src/glitzer/progress.gleam | 71 +++++++++++++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/glitzer.gleam b/src/glitzer.gleam index 822e622..03d7c6e 100644 --- a/src/glitzer.gleam +++ b/src/glitzer.gleam @@ -6,8 +6,7 @@ pub fn sleep(ms: Int) -> a pub fn main() { let bar = - progress.fancy_slim_bar() - |> progress.with_fill_head(progress.char_from_string(">")) + progress.fancy_thick_bar() |> progress.with_length(10) do_something(bar, 0) diff --git a/src/glitzer/progress.gleam b/src/glitzer/progress.gleam index 0a09990..0716a64 100644 --- a/src/glitzer/progress.gleam +++ b/src/glitzer/progress.gleam @@ -57,6 +57,8 @@ pub opaque type ProgressStyle { ) } +// SECTION: progress bar definitions + /// Create and return a default progress bar. pub fn default_bar() -> ProgressStyle { ProgressStyle( @@ -72,7 +74,23 @@ pub fn default_bar() -> ProgressStyle { ) } -/// Create and return a fancy progress bar (inspired by pip). +/// 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( @@ -88,6 +106,57 @@ pub fn fancy_slim_bar() -> ProgressStyle { ) } +/// 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. /// ///