From f743d4b2dc93e41d0e8b5bb0a3e5f26ffc2534e1 Mon Sep 17 00:00:00 2001 From: Daylin Morgan Date: Tue, 17 Sep 2024 18:54:25 -0500 Subject: [PATCH] add back all the spinners --- src/hwylterm/bbansi.nim | 23 ++-- src/hwylterm/spin.nim | 39 +++--- src/hwylterm/spin/spinners.nim | 211 +++++++++++++++++++++++++++++++++ tests/tbbansi.nim | 4 + 4 files changed, 247 insertions(+), 30 deletions(-) create mode 100644 src/hwylterm/spin/spinners.nim diff --git a/src/hwylterm/bbansi.nim b/src/hwylterm/bbansi.nim index 62efd4b..183c06c 100644 --- a/src/hwylterm/bbansi.nim +++ b/src/hwylterm/bbansi.nim @@ -8,17 +8,16 @@ import std/[os, sequtils, strutils, terminal] import bbansi/[styles, utils] -# TODO: -# - improve terminal/output detection (is output a tty?) -# - add compiletimeSwitch to disable color? -# - add env variable to force color proc checkColorSupport(): bool = - if os.getEnv("NO_COLOR") != "": - return true when defined(bbansiNoColor): return true - if not isatty(stdout): - return true + else: + if os.getEnv("HWYLTERM_FORCE_COLOR") != "": + return false + if os.getEnv("NO_COLOR") != "": + return true + if not isatty(stdout): + return true let noColor = checkColorSupport() @@ -64,7 +63,8 @@ template closeStyle(bbs: var BbString, pattern: string) = let style = pattern[1 ..^ 1].strip() if style in bbs.spans[^1].styles: bbs.endSpan - let newStyle = bbs.spans[^1].styles.filterIt(it != style) # use sets instead + if bbs.spans.len == 0: return + let newStyle = bbs.spans[^1].styles.filterIt(it != style) # use sets instead? bbs.newSpan newStyle template closeFinalSpan(bbs: var BbString) = @@ -90,6 +90,7 @@ proc bb*(s: string): BbString = inc i result.raw = s + if not s.startswith('[') or s.startswith("[["): result.spans.add BbSpan() @@ -169,10 +170,14 @@ proc `&`*(x: BbString, y: BbString): Bbstring = # there is probably a more efficient way to do this bb(x.raw & y.raw) +proc bbEscape*(s: string): string {.inline.} = + s.replace("[", "[[") + proc bbEcho*(args: varargs[string, `$`]) {.sideEffect.} = for x in args: stdout.write(x.bb) stdout.write('\n') + stdout.flushFile when isMainModule: diff --git a/src/hwylterm/spin.nim b/src/hwylterm/spin.nim index 5708837..39f1dac 100644 --- a/src/hwylterm/spin.nim +++ b/src/hwylterm/spin.nim @@ -1,24 +1,5 @@ import std/[os, locks, sequtils, terminal] -import "."/bbansi - -type - SpinnerKind* = enum - Dots - - Spinner* = object - interval*: int - frames*: seq[string] - -proc makeSpinner*(interval: int, frames: seq[string]): Spinner = - Spinner(interval: interval, frames: frames) - -const Spinners*: array[SpinnerKind, Spinner] = [ - # Dots - Spinner( - interval: 80, - frames: @["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"], - ) -] +import "."/[bbansi, spin/spinners] type Spinny = ref object @@ -48,7 +29,7 @@ proc newSpinny*(text: string, s: Spinner): Spinny = Spinny( text: text, running: true, - frames: mapIt(s.frames, $bb(it, style)), + frames: mapIt(s.frames, $bb(bbEscape(it), style)), customSymbol: false, interval: s.interval, style: "bold blue", @@ -136,3 +117,19 @@ template withSpinner*(msg: string = "", body: untyped): untyped = template withSpinner*(body: untyped): untyped = withSpinner("", body) + +template with*(kind: SpinnerKind, msg: string, body: untyped): untyped = + var spinner {.inject.} = newSpinny(msg, kind) + if isatty(stdout): # don't spin if it's not a tty + start spinner + + body + + if isatty(stdout): + stop spinner + +when isMainModule: + for kind, _ in Spinners: + with(kind, $kind): + sleep 1 * 1000 + diff --git a/src/hwylterm/spin/spinners.nim b/src/hwylterm/spin/spinners.nim new file mode 100644 index 0000000..7d176b6 --- /dev/null +++ b/src/hwylterm/spin/spinners.nim @@ -0,0 +1,211 @@ +type + SpinnerKind* = enum + Dots, Dots2, Dots3, Dots4, Dots5, Dots6, Dots7, Dots8, Dots9, + Dots10, Dots11, Dots12, Line, Line2, Pipe, SimpleDots, + SimpleDotsScrolling, Star, Star2, Flip, Hamburger, GrowVertical, + GrowHorizontal, Balloon, Balloon2, Noise, Bounce, BoxBounce, BoxBounce2, + Triangle, Arc, Circle, SquareCorners, CircleQuarters, CircleHalves, Squish, + Toggle, Toggle2, Toggle3, Toggle4, Toggle5, Toggle6, Toggle7, Toggle8, + Toggle9, Toggle10, Toggle11, Toggle12, Toggle13, Arrow, Arrow2, Arrow3, + BouncingBar, BouncingBall, Smiley, Monkey, Hearts, + Clock, Earth, Moon, Runner, Pong, Shark, Dqpb + + Spinner* = object + interval*: int = 80 + frames*: seq[string] + +proc makeSpinner*(interval: int, frames: seq[string]): Spinner = + Spinner(interval: interval, frames: frames) + +const Spinners*: array[SpinnerKind, Spinner] = [ + # Dots + Spinner(frames: @[ "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]), + # Dots2 + Spinner(frames: @[ "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"]), + # Dots3 + Spinner(frames: @[ "⠋", "⠙", "⠚", "⠞", "⠖", "⠦", "⠴", "⠲", "⠳", "⠓"]), + # Dots4 + Spinner(frames: @[ "⠄", "⠆", "⠇", "⠋", "⠙", "⠸", "⠰", "⠠", "⠰", "⠸", "⠙", "⠋", "⠇", "⠆"]), + # Dots5 + Spinner(frames: @[ "⠋", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋"]), + # Dots6 + Spinner(frames: @[ "⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠴", "⠲", "⠒", "⠂", "⠂", "⠒", "⠚", "⠙", "⠉", "⠁"]), + # Dots7 + Spinner(frames: @[ "⠈", "⠉", "⠋", "⠓", "⠒", "⠐", "⠐", "⠒", "⠖", "⠦", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈"]), + # Dots8 + Spinner(frames: @[ "⠁", "⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈", "⠈", ]), + # Dots9 + Spinner(frames: @[ "⢹", "⢺", "⢼", "⣸", "⣇", "⡧", "⡗", "⡏", ]), + # Dots10 + Spinner(frames: @[ "⢄", "⢂", "⢁", "⡁", "⡈", "⡐", "⡠", ]), + # Dots11 + Spinner(interval: 100, frames: @[ "⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈", ]), + # Dots12 + Spinner(frames: @[ "⢀⠀", "⡀⠀", "⠄⠀", "⢂⠀", "⡂⠀", "⠅⠀", "⢃⠀", "⡃⠀", "⠍⠀", "⢋⠀", "⡋⠀", "⠍⠁", "⢋⠁", "⡋⠁", "⠍⠉", "⠋⠉", "⠋⠉", "⠉⠙", "⠉⠙", "⠉⠩", "⠈⢙", "⠈⡙", "⢈⠩", "⡀⢙", "⠄⡙", "⢂⠩", "⡂⢘", "⠅⡘", "⢃⠨", "⡃⢐", "⠍⡐", "⢋⠠", "⡋⢀", "⠍⡁", "⢋⠁", "⡋⠁", "⠍⠉", "⠋⠉", "⠋⠉", "⠉⠙", "⠉⠙", "⠉⠩", "⠈⢙", "⠈⡙", "⠈⠩", "⠀⢙", "⠀⡙", "⠀⠩", "⠀⢘", "⠀⡘", "⠀⠨", "⠀⢐", "⠀⡐", "⠀⠠", "⠀⢀", "⠀⡀", ]), + # Line + Spinner(interval: 130, frames: @[ "-", "\\", "|", "/", ]), + # Line2 + Spinner(interval: 100, frames: @[ "⠂", "-", "–", "—", "–", "-", ]), + # Pipe + Spinner(interval: 100, frames: @[ "┤", "┘", "┴", "└", "├", "┌", "┬", "┐", ]), + # SimpleDots + Spinner(interval: 400, frames: @[ ". ", "..", "...", " ", ]), + # SimpleDotsScrolling + Spinner(interval: 200, frames: @[ ". ", "..", "...", " ..", " .", " ", ]), + # Star + Spinner(interval: 70, frames: @[ "✶", "✸", "✹", "✺", "✹", "✷", ]), + # Star2 + Spinner(frames: @[ "+", "x", "*", ]), + # Flip + Spinner(interval: 70, frames: @[ "_", "_", "_", "-", "`", "`", "'", "´", "-", "_", "_", "_", ]), + # Hamburger + Spinner(interval: 100, frames: @[ "☱", "☲", "☴", ]), + # GrowVertical + Spinner(interval: 120, frames: @[ "▁", "▃", "▄", "▅", "▆", "▇", "▆", "▅", "▄", "▃", ]), + # GrowHorizontal + Spinner(interval: 120, frames: @[ "▏", "▎", "▍", "▌", "▋", "▊", "▉", "▊", "▋", "▌", "▍", "▎", ]), + # Balloon + Spinner(interval: 140, frames: @[ " ", ".", "o", "O", "@", "*", " "]), + # Balloon2 + Spinner(interval: 120, frames: @[ ".", "o", "O", "°", "O", "o", "."]), + # Noise + Spinner(interval: 100, frames: @[ "▓", "▒", "░", ]), + # Bounce + Spinner(interval: 120, frames: @[ "⠁", "⠂", "⠄", "⠂", ]), + # BoxBounce + Spinner(interval: 120, frames: @[ "▖", "▘", "▝", "▗", ]), + # BoxBounce2 + Spinner(interval: 100, frames: @[ "▌", "▀", "▐", "▄", ]), + # Triangle + Spinner(interval: 50, frames: @[ "◢", "◣", "◤", "◥", ]), + # Arc + Spinner(interval: 100, frames: @[ "◜", "◠", "◝", "◞", "◡", "◟", ]), + # Circle + Spinner(interval: 120, frames: @[ "◡", "⊙", "◠", ]), + # SquareCorners + Spinner(interval: 180, frames: @[ "◰", "◳", "◲", "◱", ]), + # CircleQuarters + Spinner(interval: 120, frames: @[ "◴", "◷", "◶", "◵", ]), + # CircleHalves + Spinner(interval: 50, frames: @[ "◐", "◓", "◑", "◒", ]), + # Squish + Spinner(interval: 100, frames: @[ "╫", "╪", ]), + # Toggle + Spinner(interval: 250, frames: @[ "⊶", "⊷", ]), + # Toggle2 + Spinner(frames: @[ "▫", "▪", ]), + # Toggle3 + Spinner(interval: 120, frames: @[ "□", "■", ]), + # Toggle4 + Spinner(interval: 100, frames: @[ "■", "□", "▪", "▫", ]), + # Toggle5 + Spinner(interval: 100, frames: @[ "▮", "▯", ]), + # Toggle6 + Spinner(interval: 300, frames: @[ "ဝ", "၀", ]), + # Toggle7 + Spinner(frames: @[ "⦾", "⦿", ]), + # Toggle8 + Spinner(interval: 100, frames: @[ "◍", "◌", ]), + # Toggle9 + Spinner(interval: 100, frames: @[ "◉", "◎", ]), + # Toggle10 + Spinner(interval: 100, frames: @[ "㊂", "㊀", "㊁", ]), + # Toggle11 + Spinner(interval: 50, frames: @[ "⧇", "⧆", ]), + # Toggle12 + Spinner(interval: 120, frames: @[ "☗", "☖", ]), + # Toggle13 + Spinner(frames: @[ "=", "*", "-", ]), + # Arrow + Spinner(interval: 100, frames: @[ "←", "↖", "↑", "↗", "→", "↘", "↓", "↙", ]), + # Arrow2 + Spinner(frames: @[ "⬆", "↗", "➡", "↘", "⬇", "↙", "⬅", "↖", ]), + # Arrow3 + Spinner(interval: 120, frames: @[ "▹▹▹▹▹", "▸▹▹▹▹", "▹▸▹▹▹", "▹▹▸▹▹", "▹▹▹▸▹", "▹▹▹▹▸", ]), + # BouncingBar + Spinner(frames: @[ "[ ]", "[ =]", "[ ==]", "[ ===]", "[====]", "[=== ]", "[== ]", "[= ]" ]), + # BouncingBall + Spinner(frames: @[ "( ● )", "( ● )", "( ● )", "( ● )", "( ●)", "( ● )", "( ● )", "( ● )", "( ● )", "(● )", ]), + # Smiley + Spinner(interval: 200, frames: @[ "😄", "😝", ]), + # Monkey + Spinner(interval: 300, frames: @[ "🙈", "🙈", "🙉", "🙊", ]), + # Hearts + Spinner(interval: 100, frames: @[ "💛", "💙", "💜", "💚", "❤", ]), + # Clock + Spinner(interval: 100, frames: @[ "🕐", "🕑", "🕒", "🕓", "🕔", "🕕", "🕖", "🕗", "🕘", "🕙", "🕚", ]), + # Earth + Spinner(interval: 180, frames: @[ "🌍", "🌎", "🌏", ]), + # Moon + Spinner(frames: @[ "🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘", ]), + # Runner + Spinner(interval: 140, frames: @["🚶","🏃"]), + # Pong + Spinner( + frames: @[ + "▐⠂ ▌", + "▐⠈ ▌", + "▐ ⠂ ▌", + "▐ ⠠ ▌", + "▐ ⡀ ▌", + "▐ ⠠ ▌", + "▐ ⠂ ▌", + "▐ ⠈ ▌", + "▐ ⠂ ▌", + "▐ ⠠ ▌", + "▐ ⡀ ▌", + "▐ ⠠ ▌", + "▐ ⠂ ▌", + "▐ ⠈ ▌", + "▐ ⠂▌", + "▐ ⠠▌", + "▐ ⡀▌", + "▐ ⠠ ▌", + "▐ ⠂ ▌", + "▐ ⠈ ▌", + "▐ ⠂ ▌", + "▐ ⠠ ▌", + "▐ ⡀ ▌", + "▐ ⠠ ▌", + "▐ ⠂ ▌", + "▐ ⠈ ▌", + "▐ ⠂ ▌", + "▐ ⠠ ▌", + "▐ ⡀ ▌", + "▐⠠ ▌", + ] + ), + # Shark + Spinner( + interval: 120, frames: @[ + "▐|\\____________▌", + "▐_|\\___________▌", + "▐__|\\__________▌", + "▐___|\\_________▌", + "▐____|\\________▌", + "▐_____|\\_______▌", + "▐______|\\______▌", + "▐_______|\\_____▌", + "▐________|\\____▌", + "▐_________|\\___▌", + "▐__________|\\__▌", + "▐___________|\\_▌", + "▐____________|\\▌", + "▐____________/|▌", + "▐___________/|_▌", + "▐__________/|__▌", + "▐_________/|___▌", + "▐________/|____▌", + "▐_______/|_____▌", + "▐______/|______▌", + "▐_____/|_______▌", + "▐____/|________▌", + "▐___/|_________▌", + "▐__/|__________▌", + "▐_/|___________▌", + "▐/|____________▌", + ] + ), + # Dqpb + Spinner(interval: 100, frames: @["d","q","p","b"]), +] diff --git a/tests/tbbansi.nim b/tests/tbbansi.nim index ff99c24..aeefaba 100644 --- a/tests/tbbansi.nim +++ b/tests/tbbansi.nim @@ -7,6 +7,7 @@ template bbCheck(input: string, output: string): untyped = suite "basic": test "simple": + bbCheck "[red][/red]", "" bbCheck "[red]red text", "\e[31mred text\e[0m" bbCheck "[red]Red Text", "\e[31mRed Text\e[0m" bbCheck "[yellow]Yellow Text", "\e[33mYellow Text\e[0m" @@ -49,3 +50,6 @@ suite "basic": check "[red]Red[/red]".bb == bb("Red", "red") check "[b][yellow]not yellow[/][/b]".bb == bb("[yellow]not yellow[/]", "b") + test "escape": + check bbEscape("[info] brackets") == "[[info] brackets" + bbCheck bbEscape("[info] brackets"), "[info] brackets"