From 80060b6447dd1fb9b46c4ff87cdea8f640d2ecb1 Mon Sep 17 00:00:00 2001 From: miampf Date: Tue, 4 Jun 2024 20:46:04 +0200 Subject: [PATCH 1/5] move for module --- manifest.toml | 11 +++++++++++ src/glitzer.gleam | 5 ----- src/glitzer/progress.gleam | 0 3 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 manifest.toml delete mode 100644 src/glitzer.gleam create mode 100644 src/glitzer/progress.gleam diff --git a/manifest.toml b/manifest.toml new file mode 100644 index 0000000..2a7bc47 --- /dev/null +++ b/manifest.toml @@ -0,0 +1,11 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { 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" }, +] + +[requirements] +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 deleted file mode 100644 index 2e8e537..0000000 --- a/src/glitzer.gleam +++ /dev/null @@ -1,5 +0,0 @@ -import gleam/io - -pub fn main() { - io.println("Hello from glitzer!") -} diff --git a/src/glitzer/progress.gleam b/src/glitzer/progress.gleam new file mode 100644 index 0000000..e69de29 From 81c2df99a6e2f77e3f099018b0070b32a8cbfb64 Mon Sep 17 00:00:00 2001 From: miampf Date: Tue, 4 Jun 2024 22:37:16 +0200 Subject: [PATCH 2/5] first simple progress bar implementation --- gleam.toml | 1 + manifest.toml | 2 + src/glitzer.gleam | 19 ++++ src/glitzer/codes.gleam | 11 +++ src/glitzer/progress.gleam | 175 +++++++++++++++++++++++++++++++++++++ src/glitzer_ffi.erl | 7 ++ src/glitzer_ffi.mjs | 3 + 7 files changed, 218 insertions(+) create mode 100644 src/glitzer.gleam create mode 100644 src/glitzer/codes.gleam create mode 100644 src/glitzer_ffi.erl create mode 100644 src/glitzer_ffi.mjs diff --git a/gleam.toml b/gleam.toml index 13aa279..84d0e11 100644 --- a/gleam.toml +++ b/gleam.toml @@ -14,6 +14,7 @@ version = "1.0.0" [dependencies] gleam_stdlib = ">= 0.34.0 and < 2.0.0" +ansi = ">= 0.1.0 and < 1.0.0" [dev-dependencies] gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml index 2a7bc47..37f3e29 100644 --- a/manifest.toml +++ b/manifest.toml @@ -2,10 +2,12 @@ # 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_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" }, ] [requirements] +ansi = { version = ">= 0.1.0 and < 1.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 new file mode 100644 index 0000000..463cca2 --- /dev/null +++ b/src/glitzer.gleam @@ -0,0 +1,19 @@ +import gleam/iterator + +import glitzer/progress + +@external(erlang, "glitzer_ffi", "sleep") +@external(javascript, "./glitzer_ffi.mjs", "sleep") +pub fn sleep(ms: Int) -> a + +pub fn main() { + let bar = progress.default_bar() + + iterator.range(0, 100) + |> iterator.map(fn(_) { + let bar = progress.tick(bar) + progress.print_bar(bar) + sleep(1000) + }) + |> iterator.run +} diff --git a/src/glitzer/codes.gleam b/src/glitzer/codes.gleam new file mode 100644 index 0000000..c409ef1 --- /dev/null +++ b/src/glitzer/codes.gleam @@ -0,0 +1,11 @@ +/// Print this code to clear the whole screen. +pub const clear_screen_code = "\u{001b}[2J" + +/// Print this code to clear the current line. +pub const clear_line_code = "\u{001b}[2K" + +/// Print this code to return the cursor to the "home" position. +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" diff --git a/src/glitzer/progress.gleam b/src/glitzer/progress.gleam index e69de29..eafa337 100644 --- a/src/glitzer/progress.gleam +++ b/src/glitzer/progress.gleam @@ -0,0 +1,175 @@ +import gleam/io +import gleam/string +import gleam/string_builder.{type StringBuilder} + +import glitzer/codes + +/// A `String` with only one character. +pub opaque type Char { + Char(char: String) +} + +/// Create a `Char` from a `String`. Returns a `Char` with the given string if +/// the strings length is equal to one and a `Char` of "#" otherwise. +/// +///
+/// Example: +/// +/// ```gleam +/// import glitzer/progress +/// +/// fn example() { +/// let char = progress.char_from_string("A") +/// } +/// ``` +pub fn char_from_string(from in: String) -> Char { + let len = string.length(in) + + case len { + 1 -> Char(in) + _ -> Char("#") + } +} + +pub fn string_from_char(from in: Char) -> String { + in.char +} + +pub opaque type State { + State(progress: Int) +} + +/// The style of a progress bar. +pub opaque type ProgressStyle { + ProgressStyle( + left: String, + right: String, + empty: Char, + fill: Char, + length: Int, + state: State, + ) +} + +/// Create and return a default style for a progress bar. +pub fn default_bar() -> ProgressStyle { + ProgressStyle( + left: "[", + right: "]", + empty: Char(" "), + fill: Char("#"), + length: 100, + state: State(progress: 0), + ) +} + +/// Create a new (completely empty) progress bar. +/// +///
+/// Example: +/// +/// ```gleam +/// import glitzer/progress +/// +/// fn example() { +/// let bar = +/// progress.new_bar() +/// |> progress.with_length(50) +/// |> progress.with_left_text("Progress: ") +/// |> progress.with_fill(progress.char_from_string("+")) +/// } +/// ``` +pub fn new_bar() -> ProgressStyle { + ProgressStyle( + left: "", + right: "", + empty: Char(" "), + fill: Char(" "), + length: 0, + state: State(progress: 0), + ) +} + +/// Add left text to a progress bar. +pub fn with_left_text( + bar bar: ProgressStyle, + left text: String, +) -> ProgressStyle { + ProgressStyle(..bar, left: text) +} + +/// Add right text to a progress bar. +pub fn with_right_text( + bar bar: ProgressStyle, + right text: String, +) -> ProgressStyle { + ProgressStyle(..bar, right: text) +} + +/// Add a character to a progress bar that is used to represent the +/// "background" of the bar. +pub fn with_empty(bar bar: ProgressStyle, empty char: Char) -> ProgressStyle { + ProgressStyle(..bar, empty: char) +} + +/// Add a character to a progress bar that is used to fill the bar on each +/// tick. +pub fn with_fill(bar bar: ProgressStyle, fill char: Char) -> ProgressStyle { + ProgressStyle(..bar, fill: char) +} + +/// Add length to a progress bar. +pub fn with_length(bar bar: ProgressStyle, length len: Int) -> ProgressStyle { + ProgressStyle(..bar, length: len) +} + +/// Increase the progress of the bar by one. +pub fn tick(bar bar: ProgressStyle) -> ProgressStyle { + ProgressStyle(..bar, state: State(progress: bar.state.progress + 1)) +} + +/// Print the progress bar to stderr. +pub fn print_bar(bar bar: ProgressStyle) { + let fill = + build_progress_fill( + string_builder.new(), + bar, + bar.length, + bar.state.progress, + 0, + ) + |> string_builder.to_string + + io.print_error( + codes.clear_line_code + <> codes.return_line_start_code + <> bar.left + <> fill + <> bar.right, + ) +} + +fn build_progress_fill( + to_fill: StringBuilder, + bar: ProgressStyle, + max: Int, + left_to_fill: Int, + count: Int, +) -> StringBuilder { + let fill = case left_to_fill > 0 { + True -> bar.fill.char + False -> bar.empty.char + } + + case count >= max { + False -> + build_progress_fill( + string_builder.append(to_fill, fill), + bar, + max, + left_to_fill - 1, + count + 1, + ) + True -> to_fill + } +} diff --git a/src/glitzer_ffi.erl b/src/glitzer_ffi.erl new file mode 100644 index 0000000..ff49e5c --- /dev/null +++ b/src/glitzer_ffi.erl @@ -0,0 +1,7 @@ +-module(glitzer_ffi). + +% Public API +-export([sleep/1]). + +sleep(Int) -> + timer:sleep(Int). diff --git a/src/glitzer_ffi.mjs b/src/glitzer_ffi.mjs new file mode 100644 index 0000000..69595b9 --- /dev/null +++ b/src/glitzer_ffi.mjs @@ -0,0 +1,3 @@ +export function sleep(ms = 0) { + return new Promise(resolve => setTimeout(resolve, ms)); +} From 543ea437f3fdd253f3102b2e2e4332f1da3072e3 Mon Sep 17 00:00:00 2001 From: miampf Date: Tue, 4 Jun 2024 22:53:14 +0200 Subject: [PATCH 3/5] made function a bit better --- src/glitzer.gleam | 22 +++++++++++++--------- src/glitzer/progress.gleam | 32 +++++++++----------------------- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/src/glitzer.gleam b/src/glitzer.gleam index 463cca2..559ddfe 100644 --- a/src/glitzer.gleam +++ b/src/glitzer.gleam @@ -1,5 +1,3 @@ -import gleam/iterator - import glitzer/progress @external(erlang, "glitzer_ffi", "sleep") @@ -9,11 +7,17 @@ pub fn sleep(ms: Int) -> a pub fn main() { let bar = progress.default_bar() - iterator.range(0, 100) - |> iterator.map(fn(_) { - let bar = progress.tick(bar) - progress.print_bar(bar) - sleep(1000) - }) - |> iterator.run + do_something(bar, 0) +} + +fn do_something(bar, count) { + case count < 100 { + True -> { + let bar = progress.tick(bar) + progress.print_bar(bar) + sleep(1000) + do_something(bar, count + 1) + } + False -> Nil + } } diff --git a/src/glitzer/progress.gleam b/src/glitzer/progress.gleam index eafa337..0106693 100644 --- a/src/glitzer/progress.gleam +++ b/src/glitzer/progress.gleam @@ -131,13 +131,7 @@ pub fn tick(bar bar: ProgressStyle) -> ProgressStyle { /// Print the progress bar to stderr. pub fn print_bar(bar bar: ProgressStyle) { let fill = - build_progress_fill( - string_builder.new(), - bar, - bar.length, - bar.state.progress, - 0, - ) + build_progress_fill(string_builder.new(), bar, bar.state.progress, 0) |> string_builder.to_string io.print_error( @@ -150,26 +144,18 @@ pub fn print_bar(bar bar: ProgressStyle) { } fn build_progress_fill( - to_fill: StringBuilder, + fill: StringBuilder, bar: ProgressStyle, - max: Int, - left_to_fill: Int, + left_nonempty: Int, count: Int, ) -> StringBuilder { - let fill = case left_to_fill > 0 { - True -> bar.fill.char - False -> bar.empty.char + let fill = case left_nonempty > 0 { + True -> string_builder.append(fill, bar.fill.char) + False -> string_builder.append(fill, bar.empty.char) } - case count >= max { - False -> - build_progress_fill( - string_builder.append(to_fill, fill), - bar, - max, - left_to_fill - 1, - count + 1, - ) - True -> to_fill + case bar.length > count { + True -> build_progress_fill(fill, bar, left_nonempty - 1, count + 1) + False -> fill } } From 0e62c65b07415d06c6a2511bf4e3bd0542ec4fd9 Mon Sep 17 00:00:00 2001 From: miampf Date: Tue, 4 Jun 2024 22:57:55 +0200 Subject: [PATCH 4/5] test `finish` in main --- src/glitzer.gleam | 7 +++++-- src/glitzer/progress.gleam | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/glitzer.gleam b/src/glitzer.gleam index 559ddfe..292fd37 100644 --- a/src/glitzer.gleam +++ b/src/glitzer.gleam @@ -13,9 +13,12 @@ pub fn main() { fn do_something(bar, count) { case count < 100 { True -> { - let bar = progress.tick(bar) + let bar = case count > 50 { + True -> progress.finish(bar) + False -> progress.tick(bar) + } progress.print_bar(bar) - sleep(1000) + sleep(15) do_something(bar, count + 1) } False -> Nil diff --git a/src/glitzer/progress.gleam b/src/glitzer/progress.gleam index 0106693..ec91e18 100644 --- a/src/glitzer/progress.gleam +++ b/src/glitzer/progress.gleam @@ -128,6 +128,11 @@ pub fn tick(bar bar: ProgressStyle) -> ProgressStyle { ProgressStyle(..bar, state: State(progress: bar.state.progress + 1)) } +/// Completely fill the progress bar. +pub fn finish(bar bar: ProgressStyle) -> ProgressStyle { + ProgressStyle(..bar, state: State(progress: bar.length + 1)) +} + /// Print the progress bar to stderr. pub fn print_bar(bar bar: ProgressStyle) { let fill = From f7fef15b194559106333f2cf3d1f497e0b45c98b Mon Sep 17 00:00:00 2001 From: miampf Date: Tue, 4 Jun 2024 23:04:21 +0200 Subject: [PATCH 5/5] added example comment --- src/glitzer/progress.gleam | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/glitzer/progress.gleam b/src/glitzer/progress.gleam index ec91e18..be3e081 100644 --- a/src/glitzer/progress.gleam +++ b/src/glitzer/progress.gleam @@ -134,6 +134,31 @@ pub fn finish(bar bar: ProgressStyle) -> ProgressStyle { } /// Print the progress bar to stderr. +/// +///
+/// Example: +/// +/// ```gleam +/// import glitzer/progress +/// +/// fn example() { +/// let bar = progress.default_bar() +/// +/// run_example(bar, 0) +/// } +/// +/// fn run_example(bar, count) { +/// case count < 100 { +/// True -> { +/// let bar = progress.tick(bar) +/// // do some awesome stuff :3 +/// progress.print_bar(bar) +/// run_example(bar, count + 1) +/// } +/// False -> Nil +/// } +/// } +/// ``` pub fn print_bar(bar bar: ProgressStyle) { let fill = build_progress_fill(string_builder.new(), bar, bar.state.progress, 0)