mirror of
https://github.com/daylinmorgan/hwylterm.git
synced 2024-12-21 18:50:44 -06:00
start hwyl with bbansi and spinny
This commit is contained in:
parent
ec00bafad6
commit
a9ec2e738e
12 changed files with 572 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
tests/*
|
||||||
|
!tests/*.nim
|
||||||
|
!tests/*.nims
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# hwylterm
|
||||||
|
|
||||||
|
Adding some fun to the terminal!
|
2
config.nims
Normal file
2
config.nims
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
task test, "run tests":
|
||||||
|
selfExec "r tests/tbbansi.nim"
|
12
hwylterm.nimble
Normal file
12
hwylterm.nimble
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Daylin Morgan"
|
||||||
|
description = "bringing some fun (hwyl) to the terminal"
|
||||||
|
license = "MIT"
|
||||||
|
srcDir = "src"
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 2.0.8"
|
2
src/hwylterm.nim
Normal file
2
src/hwylterm.nim
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
import hwylterm/[spin, bbansi]
|
||||||
|
export spin, bbansi
|
276
src/hwylterm/bbansi.nim
Normal file
276
src/hwylterm/bbansi.nim
Normal file
|
@ -0,0 +1,276 @@
|
||||||
|
##[
|
||||||
|
## bbansi
|
||||||
|
|
||||||
|
use BB style markup to add color to strings using VT100 escape codes
|
||||||
|
]##
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
let noColor = checkColorSupport()
|
||||||
|
|
||||||
|
type
|
||||||
|
BbSpan* = object
|
||||||
|
styles*: seq[string]
|
||||||
|
slice*: array[2, int]
|
||||||
|
|
||||||
|
BbString* = object
|
||||||
|
raw*: string
|
||||||
|
plain*: string
|
||||||
|
spans*: seq[BbSpan]
|
||||||
|
|
||||||
|
proc len(span: BbSpan): int =
|
||||||
|
span.slice[1] - span.slice[0]
|
||||||
|
|
||||||
|
template endSpan(bbs: var BbString) =
|
||||||
|
if bbs.spans.len == 0:
|
||||||
|
return
|
||||||
|
if bbs.plain.len >= 1:
|
||||||
|
bbs.spans[^1].slice[1] = bbs.plain.len - 1
|
||||||
|
if bbs.spans[^1].len == 0 and bbs.plain.len == 0:
|
||||||
|
bbs.spans.delete(bbs.spans.len - 1)
|
||||||
|
|
||||||
|
proc newSpan(bbs: var BbString, styles: seq[string] = @[]) =
|
||||||
|
bbs.spans.add BbSpan(styles: styles, slice: [bbs.plain.len, 0])
|
||||||
|
|
||||||
|
template resetSpan(bbs: var BbString) =
|
||||||
|
bbs.endSpan
|
||||||
|
bbs.newSpan
|
||||||
|
|
||||||
|
template closeLastStyle(bbs: var BbString) =
|
||||||
|
bbs.endSpan
|
||||||
|
let newStyle = bbs.spans[^1].styles[0 ..^ 2] # drop the latest style
|
||||||
|
bbs.newSpan newStyle
|
||||||
|
|
||||||
|
template addToSpan(bbs: var BbString, pattern: string) =
|
||||||
|
let currStyl = bbs.spans[^1].styles
|
||||||
|
bbs.endSpan
|
||||||
|
bbs.newSpan currStyl & @[pattern]
|
||||||
|
|
||||||
|
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
|
||||||
|
bbs.newSpan newStyle
|
||||||
|
|
||||||
|
template closeFinalSpan(bbs: var BbString) =
|
||||||
|
if bbs.spans.len >= 1 and bbs.spans[^1].slice[1] == 0:
|
||||||
|
bbs.endSpan
|
||||||
|
|
||||||
|
proc bb*(s: string): BbString =
|
||||||
|
## convert bbcode markup to ansi escape codes
|
||||||
|
var
|
||||||
|
pattern: string
|
||||||
|
i = 0
|
||||||
|
|
||||||
|
template next() =
|
||||||
|
result.plain.add s[i]
|
||||||
|
inc i
|
||||||
|
|
||||||
|
template incPattern() =
|
||||||
|
pattern.add s[i]
|
||||||
|
inc i
|
||||||
|
|
||||||
|
template resetPattern() =
|
||||||
|
pattern = ""
|
||||||
|
inc i
|
||||||
|
|
||||||
|
result.raw = s
|
||||||
|
if not s.startswith('[') or s.startswith("[["):
|
||||||
|
result.spans.add BbSpan()
|
||||||
|
|
||||||
|
while i < s.len:
|
||||||
|
case s[i]
|
||||||
|
of '\\':
|
||||||
|
if i < s.len and s[i + 1] == '[':
|
||||||
|
inc i
|
||||||
|
next
|
||||||
|
of '[':
|
||||||
|
if i < s.len and s[i + 1] == '[':
|
||||||
|
inc i
|
||||||
|
next
|
||||||
|
continue
|
||||||
|
inc i
|
||||||
|
while i < s.len and s[i] != ']':
|
||||||
|
incPattern
|
||||||
|
pattern = pattern.strip()
|
||||||
|
if result.spans.len > 0:
|
||||||
|
if pattern == "/":
|
||||||
|
result.closeLastStyle
|
||||||
|
elif pattern == "reset":
|
||||||
|
result.resetSpan
|
||||||
|
elif pattern.startswith('/'):
|
||||||
|
result.closeStyle pattern
|
||||||
|
else:
|
||||||
|
result.addToSpan pattern
|
||||||
|
else:
|
||||||
|
result.newSpan @[pattern]
|
||||||
|
resetPattern
|
||||||
|
else:
|
||||||
|
next
|
||||||
|
|
||||||
|
result.closeFinalSpan
|
||||||
|
|
||||||
|
proc bb*(s: string, style: string): BbString =
|
||||||
|
bb("[" & style & "]" & s & "[/" & style & "]")
|
||||||
|
|
||||||
|
proc `&`*(x: BbString, y: string): BbString =
|
||||||
|
result = x
|
||||||
|
result.raw &= y
|
||||||
|
result.plain &= y
|
||||||
|
result.spans[^1].slice[1] = result.plain.len - 1
|
||||||
|
|
||||||
|
proc `&`*(x: string, y: BbString): BbString =
|
||||||
|
result.raw = x & y.raw
|
||||||
|
result.plain = x & y.plain
|
||||||
|
result.spans.add BbSpan(styles: @[], slice: [0, x.len - 1])
|
||||||
|
for span in y.spans:
|
||||||
|
let
|
||||||
|
length = x.len
|
||||||
|
styles = span.styles
|
||||||
|
slice = span.slice
|
||||||
|
result.spans.add BbSpan(
|
||||||
|
styles: styles, slice: [slice[0] + length, slice[1] + length]
|
||||||
|
)
|
||||||
|
|
||||||
|
func len*(bbs: BbString): int =
|
||||||
|
bbs.plain.len
|
||||||
|
|
||||||
|
proc `$`*(bbs: BbString): string =
|
||||||
|
if noColor:
|
||||||
|
return bbs.plain
|
||||||
|
|
||||||
|
for span in bbs.spans:
|
||||||
|
var codes = ""
|
||||||
|
if span.styles.len > 0:
|
||||||
|
codes = span.styles.join(" ").toAnsiCode
|
||||||
|
|
||||||
|
result.add codes
|
||||||
|
result.add bbs.plain[span.slice[0] .. span.slice[1]]
|
||||||
|
|
||||||
|
if codes != "":
|
||||||
|
result.add bbReset
|
||||||
|
|
||||||
|
proc `&`*(x: BbString, y: BbString): Bbstring =
|
||||||
|
# there is probably a more efficient way to do this
|
||||||
|
bb(x.raw & y.raw)
|
||||||
|
|
||||||
|
proc bbEcho*(args: varargs[string, `$`]) {.sideEffect.} =
|
||||||
|
for x in args:
|
||||||
|
stdout.write(x.bb)
|
||||||
|
stdout.write('\n')
|
||||||
|
stdout.flushFile
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
import std/[parseopt, strformat, sugar]
|
||||||
|
const version = staticExec "git describe --tags --always --dirty=-dev"
|
||||||
|
const longOptPad = 8
|
||||||
|
proc writeHelp() =
|
||||||
|
let help =
|
||||||
|
fmt"""
|
||||||
|
[bold]bbansi[/] \[[green]args...[/]] [[[faint]-h|-v[/]]
|
||||||
|
|
||||||
|
[italic]usage[/]:
|
||||||
|
bbansi "[[yellow] yellow text!"
|
||||||
|
|-> [yellow] yellow text![/]
|
||||||
|
bbansi "[[bold red] bold red text[[/] plain text..."
|
||||||
|
|-> [bold red] bold red text[/] plain text...
|
||||||
|
bbansi "[[red]some red[[/red] but all italic" --style:italic
|
||||||
|
|-> [italic][red]some red[/red] but all italic[/italic]
|
||||||
|
|
||||||
|
flags:
|
||||||
|
""".bb &
|
||||||
|
$(
|
||||||
|
bb(
|
||||||
|
collect(
|
||||||
|
for (s, l, d) in [
|
||||||
|
("h", "help", "show this help"),
|
||||||
|
("v", "version", "show version"),
|
||||||
|
("s", "style", "set style for string"),
|
||||||
|
]:
|
||||||
|
fmt"[yellow]-{s}[/] [green]--{l.alignLeft(longOptPad)}[/] {d}"
|
||||||
|
)
|
||||||
|
.join("\n ")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
echo help
|
||||||
|
quit(QuitSuccess)
|
||||||
|
|
||||||
|
proc testCard() =
|
||||||
|
for style in [
|
||||||
|
"bold", "faint", "italic", "underline", "blink", "reverse", "conceal", "strike"
|
||||||
|
]:
|
||||||
|
echo style, " -> ", fmt"[{style}]****".bb
|
||||||
|
const colors =
|
||||||
|
["black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"]
|
||||||
|
for color in colors:
|
||||||
|
echo color, " -> ", fmt"[{color}]****".bb
|
||||||
|
for color in colors:
|
||||||
|
echo "on ", color, " -> ", fmt"[on {color}]****".bb
|
||||||
|
|
||||||
|
proc debug(bbs: BbString): string =
|
||||||
|
echo "bbString("
|
||||||
|
echo " raw: ", bbs.raw
|
||||||
|
echo " plain: ", bbs.plain
|
||||||
|
echo " spans: ", bbs.spans
|
||||||
|
echo " escaped: ", escape($bbs)
|
||||||
|
echo ")"
|
||||||
|
|
||||||
|
proc writeVersion() =
|
||||||
|
echo fmt"[yellow]bbansi version[/][red] ->[/] [bold]{version}[/]".bb
|
||||||
|
quit(QuitSuccess)
|
||||||
|
|
||||||
|
var
|
||||||
|
strArgs: seq[string]
|
||||||
|
style: string
|
||||||
|
showDebug: bool
|
||||||
|
var p = initOptParser()
|
||||||
|
for kind, key, val in p.getopt():
|
||||||
|
case kind
|
||||||
|
of cmdEnd:
|
||||||
|
break
|
||||||
|
of cmdShortOption, cmdLongOption:
|
||||||
|
case key
|
||||||
|
of "help", "h":
|
||||||
|
writeHelp()
|
||||||
|
of "version", "v":
|
||||||
|
writeVersion()
|
||||||
|
of "testCard":
|
||||||
|
testCard()
|
||||||
|
quit(QuitSuccess)
|
||||||
|
of "style", "s":
|
||||||
|
if val == "":
|
||||||
|
echo "[red]ERROR[/]: expected value for -s/--style".bb
|
||||||
|
quit(QuitFailure)
|
||||||
|
style = val
|
||||||
|
of "debug":
|
||||||
|
showDebug = true
|
||||||
|
else:
|
||||||
|
echo bb"[yellow]warning[/]: unexpected option/value -> ", key, ", ", val
|
||||||
|
of cmdArgument:
|
||||||
|
strArgs.add key
|
||||||
|
if strArgs.len == 0:
|
||||||
|
writeHelp()
|
||||||
|
for arg in strArgs:
|
||||||
|
let styled =
|
||||||
|
if style != "":
|
||||||
|
arg.bb(style)
|
||||||
|
else:
|
||||||
|
arg.bb
|
||||||
|
echo styled
|
||||||
|
if showDebug:
|
||||||
|
echo debug(styled)
|
29
src/hwylterm/bbansi/styles.nim
Normal file
29
src/hwylterm/bbansi/styles.nim
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import std/tables
|
||||||
|
export tables
|
||||||
|
|
||||||
|
const
|
||||||
|
bbReset* = "\e[0m"
|
||||||
|
bbStyles* = {
|
||||||
|
"bold": "1",
|
||||||
|
"b": "1",
|
||||||
|
"faint": "2",
|
||||||
|
"italic": "3",
|
||||||
|
"i": "3",
|
||||||
|
"underline": "4",
|
||||||
|
"u": "4",
|
||||||
|
"blink": "5",
|
||||||
|
"reverse": "7",
|
||||||
|
"conceal": "8",
|
||||||
|
"strike": "9",
|
||||||
|
}.toTable
|
||||||
|
|
||||||
|
bbColors* = {
|
||||||
|
"black": "0",
|
||||||
|
"red": "1",
|
||||||
|
"green": "2",
|
||||||
|
"yellow": "3",
|
||||||
|
"blue": "4",
|
||||||
|
"magenta": "5",
|
||||||
|
"cyan": "6",
|
||||||
|
"white": "7",
|
||||||
|
}.toTable
|
27
src/hwylterm/bbansi/utils.nim
Normal file
27
src/hwylterm/bbansi/utils.nim
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import std/[strutils]
|
||||||
|
|
||||||
|
import styles
|
||||||
|
|
||||||
|
proc toAnsiCode*(s: string): string =
|
||||||
|
var
|
||||||
|
codes: seq[string]
|
||||||
|
styles: seq[string]
|
||||||
|
bgStyle: string
|
||||||
|
if " on " in s or s.startswith("on"):
|
||||||
|
let fgBgSplit = s.rsplit("on", maxsplit = 1)
|
||||||
|
styles = fgBgSplit[0].toLowerAscii().splitWhitespace()
|
||||||
|
bgStyle = fgBgSplit[1].strip().toLowerAscii()
|
||||||
|
else:
|
||||||
|
styles = s.splitWhitespace()
|
||||||
|
for style in styles:
|
||||||
|
if style in bbStyles:
|
||||||
|
codes.add bbStyles[style]
|
||||||
|
elif style in bbColors:
|
||||||
|
codes.add "3" & bbColors[style]
|
||||||
|
if bgStyle in bbColors:
|
||||||
|
codes.add "4" & bbColors[bgStyle]
|
||||||
|
|
||||||
|
if codes.len > 0:
|
||||||
|
result.add "\e["
|
||||||
|
result.add codes.join ";"
|
||||||
|
result.add "m"
|
138
src/hwylterm/spin.nim
Normal file
138
src/hwylterm/spin.nim
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
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: @["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
type
|
||||||
|
Spinny = ref object
|
||||||
|
t: Thread[Spinny]
|
||||||
|
lock: Lock
|
||||||
|
text: string
|
||||||
|
running: bool
|
||||||
|
frames: seq[string]
|
||||||
|
frame: string
|
||||||
|
interval: int
|
||||||
|
customSymbol: bool
|
||||||
|
style: string
|
||||||
|
|
||||||
|
EventKind = enum
|
||||||
|
Stop
|
||||||
|
SymbolChange
|
||||||
|
TextChange
|
||||||
|
|
||||||
|
SpinnyEvent = object
|
||||||
|
kind: EventKind
|
||||||
|
payload: string
|
||||||
|
|
||||||
|
var spinnyChannel: Channel[SpinnyEvent]
|
||||||
|
|
||||||
|
proc newSpinny*(text: string, s: Spinner): Spinny =
|
||||||
|
let style = "bold blue"
|
||||||
|
Spinny(
|
||||||
|
text: text,
|
||||||
|
running: true,
|
||||||
|
frames: mapIt(s.frames, $bb(it, style)),
|
||||||
|
customSymbol: false,
|
||||||
|
interval: s.interval,
|
||||||
|
style: "bold blue",
|
||||||
|
)
|
||||||
|
|
||||||
|
proc newSpinny*(text: string, spinType: SpinnerKind): Spinny =
|
||||||
|
newSpinny(text, Spinners[spinType])
|
||||||
|
|
||||||
|
proc setSymbolColor*(spinny: Spinny, style: string) =
|
||||||
|
spinny.frames = mapIt(spinny.frames, $bb(it, style))
|
||||||
|
|
||||||
|
proc setSymbol*(spinny: Spinny, symbol: string) =
|
||||||
|
spinnyChannel.send(SpinnyEvent(kind: SymbolChange, payload: symbol))
|
||||||
|
|
||||||
|
proc setText*(spinny: Spinny, text: string) =
|
||||||
|
spinnyChannel.send(SpinnyEvent(kind: TextChange, payload: text))
|
||||||
|
|
||||||
|
proc handleEvent(spinny: Spinny, eventData: SpinnyEvent): bool =
|
||||||
|
result = true
|
||||||
|
case eventData.kind
|
||||||
|
of Stop:
|
||||||
|
result = false
|
||||||
|
of SymbolChange:
|
||||||
|
spinny.customSymbol = true
|
||||||
|
spinny.frame = eventData.payload
|
||||||
|
of TextChange:
|
||||||
|
spinny.text = eventData.payload
|
||||||
|
|
||||||
|
proc spinnyLoop(spinny: Spinny) {.thread.} =
|
||||||
|
var frameCounter = 0
|
||||||
|
|
||||||
|
while spinny.running:
|
||||||
|
let data = spinnyChannel.tryRecv()
|
||||||
|
if data.dataAvailable:
|
||||||
|
# If we received a Stop event
|
||||||
|
if not spinny.handleEvent(data.msg):
|
||||||
|
spinnyChannel.close()
|
||||||
|
# This is required so we can reopen the same channel more than once
|
||||||
|
# See https://github.com/nim-lang/Nim/issues/6369
|
||||||
|
spinnyChannel = default(typeof(spinnyChannel))
|
||||||
|
# TODO: Do we need spinny.running at all?
|
||||||
|
spinny.running = false
|
||||||
|
break
|
||||||
|
|
||||||
|
stdout.flushFile()
|
||||||
|
if not spinny.customSymbol:
|
||||||
|
spinny.frame = spinny.frames[frameCounter]
|
||||||
|
|
||||||
|
withLock spinny.lock:
|
||||||
|
eraseLine()
|
||||||
|
stdout.write(spinny.frame & " " & spinny.text)
|
||||||
|
stdout.flushFile()
|
||||||
|
|
||||||
|
sleep spinny.interval
|
||||||
|
|
||||||
|
if frameCounter >= spinny.frames.len - 1:
|
||||||
|
frameCounter = 0
|
||||||
|
else:
|
||||||
|
frameCounter += 1
|
||||||
|
|
||||||
|
proc start*(spinny: Spinny) =
|
||||||
|
initLock spinny.lock
|
||||||
|
spinnyChannel.open()
|
||||||
|
createThread(spinny.t, spinnyLoop, spinny)
|
||||||
|
|
||||||
|
proc stop(spinny: Spinny, kind: EventKind, payload = "") =
|
||||||
|
spinnyChannel.send(SpinnyEvent(kind: kind, payload: payload))
|
||||||
|
spinnyChannel.send(SpinnyEvent(kind: Stop))
|
||||||
|
joinThread spinny.t
|
||||||
|
eraseLine stdout
|
||||||
|
flushFile stdout
|
||||||
|
|
||||||
|
proc stop*(spinny: Spinny) =
|
||||||
|
spinny.stop(Stop)
|
||||||
|
|
||||||
|
template withSpinner*(msg: string = "", body: untyped): untyped =
|
||||||
|
var spinner {.inject.} = newSpinny(msg, Dots)
|
||||||
|
if isatty(stdout): # don't spin if it's not a tty
|
||||||
|
start spinner
|
||||||
|
|
||||||
|
body
|
||||||
|
|
||||||
|
if isatty(stdout):
|
||||||
|
stop spinner
|
||||||
|
|
||||||
|
template withSpinner*(body: untyped): untyped =
|
||||||
|
withSpinner("", body)
|
1
tests/config.nims
Normal file
1
tests/config.nims
Normal file
|
@ -0,0 +1 @@
|
||||||
|
switch("path", "$projectDir/../src")
|
51
tests/tbbansi.nim
Normal file
51
tests/tbbansi.nim
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import std/[strutils,unittest]
|
||||||
|
|
||||||
|
import hwylterm/bbansi
|
||||||
|
|
||||||
|
template bbCheck(input: string, output: string): untyped =
|
||||||
|
check escape($bb(input)) == escape(output)
|
||||||
|
|
||||||
|
suite "basic":
|
||||||
|
test "simple":
|
||||||
|
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"
|
||||||
|
bbCheck "[bold red]Bold Red Text", "\e[1;31mBold Red Text\e[0m"
|
||||||
|
bbCheck "[red]5[/]", "\e[31m5\e[0m"
|
||||||
|
bbCheck "[bold][red]5","\e[1;31m5\e[0m"
|
||||||
|
|
||||||
|
test "closing":
|
||||||
|
bbCheck "[bold]Bold[red] Bold Red[/red] Bold Only",
|
||||||
|
"\e[1mBold\e[0m\e[1;31m Bold Red\e[0m\e[1m Bold Only\e[0m"
|
||||||
|
|
||||||
|
test "abbreviated":
|
||||||
|
bbCheck "[b]Bold[/] Not Bold", "\e[1mBold\e[0m Not Bold"
|
||||||
|
|
||||||
|
test "noop":
|
||||||
|
bbCheck "No Style", "No Style"
|
||||||
|
bbCheck "[unknown]Unknown Style", "Unknown Style"
|
||||||
|
|
||||||
|
test "escaped":
|
||||||
|
bbCheck "[[red] ignored pattern", "[red] ignored pattern"
|
||||||
|
|
||||||
|
test "newlines":
|
||||||
|
bbCheck "[red]Red Text[/]\nNext Line", "\e[31mRed Text\e[0m\nNext Line"
|
||||||
|
|
||||||
|
test "on color":
|
||||||
|
bbCheck "[red on yellow]Red on Yellow", "\e[31;43mRed on Yellow\e[0m"
|
||||||
|
|
||||||
|
test "concat-ops":
|
||||||
|
check "[red]RED[/]".bb & " plain string" == "[red]RED[/] plain string".bb
|
||||||
|
check "[red]RED[/]".bb.len == 3
|
||||||
|
check bb("[blue]Blue[/]") & " " & bb("[red]Red[/]") ==
|
||||||
|
"[blue]Blue[/] [red]Red[/]".bb
|
||||||
|
check "a plain string" & "[blue] a blue string".bb ==
|
||||||
|
"a plain string[blue] a blue string".bb
|
||||||
|
|
||||||
|
test "case":
|
||||||
|
bbCheck "[red]no case sensitivity[/RED]", "\e[31mno case sensitivity\e[0m"
|
||||||
|
|
||||||
|
test "style full":
|
||||||
|
check "[red]Red[/red]".bb == bb("Red", "red")
|
||||||
|
check "[b][yellow]not yellow[/][/b]".bb == bb("[yellow]not yellow[/]", "b")
|
||||||
|
|
28
tests/tcli.nim
Normal file
28
tests/tcli.nim
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import std/[
|
||||||
|
compilesettings,
|
||||||
|
os,
|
||||||
|
osproc,
|
||||||
|
strutils,
|
||||||
|
times,
|
||||||
|
unittest
|
||||||
|
]
|
||||||
|
|
||||||
|
const pathToSrc = querySetting(SingleValueSetting.projectPath)
|
||||||
|
|
||||||
|
proc cliRun(cmd: string): string =
|
||||||
|
let (output, _) = execCmdEx(pathToSrc / "bbansi.out " & cmd)
|
||||||
|
return output.strip()
|
||||||
|
|
||||||
|
suite "cli":
|
||||||
|
setup:
|
||||||
|
let
|
||||||
|
cli = pathToSrc / "bbansi.out"
|
||||||
|
srcDir = pathToSrc / ".." / "src"
|
||||||
|
cmd = "nim c -o:" & cli & " " & (srcDir / "bbansi.nim")
|
||||||
|
if not cli.fileExists or getFileInfo(cli).lastWriteTime < getFileInfo(srcDir).lastWriteTime:
|
||||||
|
echo " -> compiling test binary"
|
||||||
|
require execCmdEx(cmd).exitCode == 0
|
||||||
|
test "simple":
|
||||||
|
check "\e[31mRed\e[0m" == cliRun "[red]Red[/]"
|
||||||
|
check "\e[1;31mRed\e[0m\e[1m Not Red but Bold\e[0m" ==
|
||||||
|
cliRun "'[red]Red[/] Not Red but Bold' " & "--style:bold"
|
Loading…
Reference in a new issue