start work to support truecolor and hexcodes

This commit is contained in:
Daylin Morgan 2024-09-23 14:23:30 -05:00
parent f2590506da
commit c1738c9504
Signed by: daylin
GPG key ID: 950D13E9719334AD
6 changed files with 134 additions and 69 deletions

View file

@ -4,11 +4,11 @@
use BB style markup to add color to strings using VT100 escape codes
]##
import std/[os, sequtils, strutils]
import std/[os, sequtils, strformat, strutils]
import ./bbansi/[styles, utils]
export utils
export bbReset
# export bbReset
type
BbSpan* = object
@ -124,6 +124,9 @@ proc `&`*(x: BbString, y: string): BbString =
result.plain &= y
result.spans[^1].slice[1] = result.plain.len - 1
template bbfmt*(pattern: static string): BbString =
bb(fmt(pattern))
proc `&`*(x: string, y: BbString): BbString =
result.raw = x & y.raw
result.plain = x & y.plain

View file

@ -0,0 +1,30 @@
import std/[parseutils, strutils]
type
ColorRgb* = object
red, green, blue: int
ColorHex* = object
code: string
ColorXterm* = enum
# 0-7
Black, Red, Green, Yellow, Blue, Magenta, Cyan, White,
# 8-15
BrightBlack, BrightRed, BrightGreen, BrightYellow,
BrightBlue, BrightMagenta, BrightCyan, BrightWhite
func rgb*(r, g, b: int): ColorRgb =
ColorRgb(red: r, green: g, blue: b)
func hexToRgb*(s: string): ColorRgb =
let code = s.replace("#", "")
assert code.len == 6
discard parseHex(code[0..1], result.red)
discard parseHex(code[2..3], result.green)
discard parseHex(code[4..5], result.blue)
func `$`*(c: ColorRgb): string =
result.add $c.red
result.add ";"
result.add $c.green
result.add ";"
result.add $c.blue

View file

@ -2,30 +2,44 @@ import std/tables
export tables
const
bbReset* = "\e[0m"
bbStyles* = {
"reset": "0",
"bold": "1",
"b": "1",
"faint": "2",
"italic": "3",
"i": "3",
"underline": "4",
"u": "4",
"blink": "5",
"reverse": "7",
"conceal": "8",
"strike": "9",
}.toTable
type
BbStyleAbbr* = enum
B, I, U
bbColors* = {
"black": "0",
"red": "1",
"green": "2",
"yellow": "3",
"blue": "4",
"magenta": "5",
"cyan": "6",
"white": "7",
}.toTable
BbStyle* = enum
Reset,
Bold, Faint, Italic, Underline, Blink,
Reverse = 7, Conceal, Strike
func toStyle*(a: BbStyleAbbr): BbStyle =
case a:
of B: Bold
of I: Italic
of U: Underline
const bbReset* = "\e[0m"
# bbStyles* = {
# "reset": "0",
# "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

View file

@ -1,5 +1,6 @@
import std/[os, strutils, terminal]
import ./styles
import std/[
enumutils, os, strutils, terminal, sequtils]
import ./[styles, colors]
type
BbMode* = enum
@ -20,6 +21,22 @@ proc checkColorSupport(): BbMode =
let bbMode* = checkColorSupport()
func normStyle(style: string): string = style.replace("_","").capitalizeAscii()
func toCode(style: BbStyle): string = $ord(style)
func toCode(abbr: BbStyleAbbr): string = abbr.toStyle().toCode()
func toCode(color: ColorXterm): string = "38;5;" & $ord(color)
func toBgCode(color: ColorXterm): string = "48;5;" & $ord(color)
func toCode(c: ColorRgb): string = "38;2;" & $c
func toBgCode(c: ColorRgb): string = "48:2;" & $c
const ColorXTermNames = ColorXterm.items().toSeq().mapIt(($it).toLowerAscii().capitalizeAscii())
const BbStyleNames = BbStyle.items().toSeq().mapIt(($it).toLowerAscii().capitalizeAscii())
func isHex(s: string): bool =
(s.startswith "#") and (s.len == 7)
# TODO: write seperate parseStyle procedure
proc toAnsiCode*(s: string): string =
if bbMode == Off: return
var
@ -33,12 +50,26 @@ proc toAnsiCode*(s: string): string =
else:
styles = s.splitWhitespace()
for style in styles:
if style in bbStyles:
codes.add bbStyles[style]
elif style in bbColors and bbMode == On:
codes.add "3" & bbColors[style]
if bgStyle in bbColors and bbMode == On:
codes.add "4" & bbColors[bgStyle]
let normalizedStyle = style.normStyle
if normalizedStyle in ["B", "I", "U"]:
codes.add parseEnum[BbStyleAbbr](normalizedStyle).toCode()
elif normalizedStyle in BbStyleNames:
codes.add parseEnum[BbStyle](normalizedStyle).toCode()
elif normalizedStyle in ColorXtermNames and bbMode == On:
codes.add parseEnum[ColorXterm](normalizedStyle).toCode()
elif normalizedStyle.isHex():
codes.add normalizedStyle.hexToRgb.toCode()
else:
when defined(debugBB): echo "unknown style: " & normalizedStyle
if bbMode == On and bgStyle != "":
let normalizedBgStyle = bgStyle.normStyle
if normalizedBgStyle in ColorXtermNames:
codes.add parseEnum[ColorXTerm](normalizedBgStyle).toBgCode()
elif normalizedBgStyle.isHex():
codes.add normalizedBgStyle.hexToRgb().toBgCode()
else:
when defined(debugBB): echo "unknown bg style: " & normalizedBgStyle
if codes.len > 0:
result.add "\e["

View file

@ -1,22 +0,0 @@
import std/[os, terminal]
type
BbMode* = enum
On, NoColor, Off
proc checkColorSupport(): BbMode =
when defined(bbansiOff):
return Off
when defined(bbansiNoColor):
return NoColor
else:
if os.getEnv("HWYLTERM_FORCE_COLOR") != "":
return On
if os.getEnv("NO_COLOR") != "":
return NoColor
if not isatty(stdout):
return Off
let bbMode* = checkColorSupport()

View file

@ -8,16 +8,16 @@ 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"
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"
bbCheck "[red]red text", "\e[38;5;1mred text\e[0m"
bbCheck "[red]Red Text", "\e[38;5;1mRed Text\e[0m"
bbCheck "[yellow]Yellow Text", "\e[38;5;3mYellow Text\e[0m"
bbCheck "[bold red]Bold Red Text", "\e[1;38;5;1mBold Red Text\e[0m"
bbCheck "[red]5[/]", "\e[38;5;1m5\e[0m"
bbCheck "[bold][red]5","\e[1;38;5;1m5\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"
"\e[1mBold\e[0m\e[1;38;5;1m Bold Red\e[0m\e[1m Bold Only\e[0m"
test "abbreviated":
bbCheck "[b]Bold[/] Not Bold", "\e[1mBold\e[0m Not Bold"
@ -30,10 +30,10 @@ suite "basic":
bbCheck "[[red] ignored pattern", "[red] ignored pattern"
test "newlines":
bbCheck "[red]Red Text[/]\nNext Line", "\e[31mRed Text\e[0m\nNext Line"
bbCheck "[red]Red Text[/]\nNext Line", "\e[38;5;1mRed Text\e[0m\nNext Line"
test "on color":
bbCheck "[red on yellow]Red on Yellow", "\e[31;43mRed on Yellow\e[0m"
bbCheck "[red on yellow]Red on Yellow", "\e[38;5;1;48;5;3mRed on Yellow\e[0m"
test "concat-ops":
check "[red]RED[/]".bb & " plain string" == "[red]RED[/] plain string".bb
@ -43,8 +43,9 @@ suite "basic":
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 insensitive":
bbCheck "[red]no case sensitivity[/RED]", "\e[38;5;1mno case sensitivity\e[0m"
bbCheck "[bright_red]should be BrightRed[/]", "\e[38;5;9mshould be BrightRed\e[0m"
test "style full":
check "[red]Red[/red]".bb == bb("Red", "red")
@ -53,3 +54,11 @@ suite "basic":
test "escape":
check bbEscape("[info] brackets") == "[[info] brackets"
bbCheck bbEscape("[info] brackets"), "[info] brackets"
test "fmt":
let x = 5
check $bbfmt"[red]{x}" == "\e[38;5;1m5\e[0m"
test "hex":
bbCheck "[#FF0000]red", "\e[38;2;255;0;0mred\e[0m"