mirror of
https://github.com/daylinmorgan/hwylterm.git
synced 2025-01-02 15:30:44 -06:00
start work to support truecolor and hexcodes
This commit is contained in:
parent
f2590506da
commit
c1738c9504
6 changed files with 134 additions and 69 deletions
|
@ -4,11 +4,11 @@
|
||||||
use BB style markup to add color to strings using VT100 escape codes
|
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]
|
import ./bbansi/[styles, utils]
|
||||||
export utils
|
export utils
|
||||||
export bbReset
|
# export bbReset
|
||||||
|
|
||||||
type
|
type
|
||||||
BbSpan* = object
|
BbSpan* = object
|
||||||
|
@ -124,6 +124,9 @@ proc `&`*(x: BbString, y: string): BbString =
|
||||||
result.plain &= y
|
result.plain &= y
|
||||||
result.spans[^1].slice[1] = result.plain.len - 1
|
result.spans[^1].slice[1] = result.plain.len - 1
|
||||||
|
|
||||||
|
template bbfmt*(pattern: static string): BbString =
|
||||||
|
bb(fmt(pattern))
|
||||||
|
|
||||||
proc `&`*(x: string, y: BbString): BbString =
|
proc `&`*(x: string, y: BbString): BbString =
|
||||||
result.raw = x & y.raw
|
result.raw = x & y.raw
|
||||||
result.plain = x & y.plain
|
result.plain = x & y.plain
|
||||||
|
|
30
src/hwylterm/bbansi/colors.nim
Normal file
30
src/hwylterm/bbansi/colors.nim
Normal 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
|
|
@ -2,30 +2,44 @@ import std/tables
|
||||||
export tables
|
export tables
|
||||||
|
|
||||||
|
|
||||||
const
|
type
|
||||||
bbReset* = "\e[0m"
|
BbStyleAbbr* = enum
|
||||||
bbStyles* = {
|
B, I, U
|
||||||
"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* = {
|
BbStyle* = enum
|
||||||
"black": "0",
|
Reset,
|
||||||
"red": "1",
|
Bold, Faint, Italic, Underline, Blink,
|
||||||
"green": "2",
|
Reverse = 7, Conceal, Strike
|
||||||
"yellow": "3",
|
|
||||||
"blue": "4",
|
func toStyle*(a: BbStyleAbbr): BbStyle =
|
||||||
"magenta": "5",
|
case a:
|
||||||
"cyan": "6",
|
of B: Bold
|
||||||
"white": "7",
|
of I: Italic
|
||||||
}.toTable
|
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
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import std/[os, strutils, terminal]
|
import std/[
|
||||||
import ./styles
|
enumutils, os, strutils, terminal, sequtils]
|
||||||
|
import ./[styles, colors]
|
||||||
|
|
||||||
type
|
type
|
||||||
BbMode* = enum
|
BbMode* = enum
|
||||||
|
@ -20,6 +21,22 @@ proc checkColorSupport(): BbMode =
|
||||||
|
|
||||||
let bbMode* = checkColorSupport()
|
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 =
|
proc toAnsiCode*(s: string): string =
|
||||||
if bbMode == Off: return
|
if bbMode == Off: return
|
||||||
var
|
var
|
||||||
|
@ -33,12 +50,26 @@ proc toAnsiCode*(s: string): string =
|
||||||
else:
|
else:
|
||||||
styles = s.splitWhitespace()
|
styles = s.splitWhitespace()
|
||||||
for style in styles:
|
for style in styles:
|
||||||
if style in bbStyles:
|
let normalizedStyle = style.normStyle
|
||||||
codes.add bbStyles[style]
|
if normalizedStyle in ["B", "I", "U"]:
|
||||||
elif style in bbColors and bbMode == On:
|
codes.add parseEnum[BbStyleAbbr](normalizedStyle).toCode()
|
||||||
codes.add "3" & bbColors[style]
|
elif normalizedStyle in BbStyleNames:
|
||||||
if bgStyle in bbColors and bbMode == On:
|
codes.add parseEnum[BbStyle](normalizedStyle).toCode()
|
||||||
codes.add "4" & bbColors[bgStyle]
|
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:
|
if codes.len > 0:
|
||||||
result.add "\e["
|
result.add "\e["
|
||||||
|
|
|
@ -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()
|
|
||||||
|
|
||||||
|
|
|
@ -8,16 +8,16 @@ template bbCheck(input: string, output: string): untyped =
|
||||||
suite "basic":
|
suite "basic":
|
||||||
test "simple":
|
test "simple":
|
||||||
bbCheck "[red][/red]", ""
|
bbCheck "[red][/red]", ""
|
||||||
bbCheck "[red]red text", "\e[31mred text\e[0m"
|
bbCheck "[red]red text", "\e[38;5;1mred text\e[0m"
|
||||||
bbCheck "[red]Red Text", "\e[31mRed Text\e[0m"
|
bbCheck "[red]Red Text", "\e[38;5;1mRed Text\e[0m"
|
||||||
bbCheck "[yellow]Yellow Text", "\e[33mYellow Text\e[0m"
|
bbCheck "[yellow]Yellow Text", "\e[38;5;3mYellow Text\e[0m"
|
||||||
bbCheck "[bold red]Bold Red Text", "\e[1;31mBold Red Text\e[0m"
|
bbCheck "[bold red]Bold Red Text", "\e[1;38;5;1mBold Red Text\e[0m"
|
||||||
bbCheck "[red]5[/]", "\e[31m5\e[0m"
|
bbCheck "[red]5[/]", "\e[38;5;1m5\e[0m"
|
||||||
bbCheck "[bold][red]5","\e[1;31m5\e[0m"
|
bbCheck "[bold][red]5","\e[1;38;5;1m5\e[0m"
|
||||||
|
|
||||||
test "closing":
|
test "closing":
|
||||||
bbCheck "[bold]Bold[red] Bold Red[/red] Bold Only",
|
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":
|
test "abbreviated":
|
||||||
bbCheck "[b]Bold[/] Not Bold", "\e[1mBold\e[0m Not Bold"
|
bbCheck "[b]Bold[/] Not Bold", "\e[1mBold\e[0m Not Bold"
|
||||||
|
@ -30,10 +30,10 @@ suite "basic":
|
||||||
bbCheck "[[red] ignored pattern", "[red] ignored pattern"
|
bbCheck "[[red] ignored pattern", "[red] ignored pattern"
|
||||||
|
|
||||||
test "newlines":
|
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":
|
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":
|
test "concat-ops":
|
||||||
check "[red]RED[/]".bb & " plain string" == "[red]RED[/] plain string".bb
|
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 ==
|
check "a plain string" & "[blue] a blue string".bb ==
|
||||||
"a plain string[blue] a blue string".bb
|
"a plain string[blue] a blue string".bb
|
||||||
|
|
||||||
test "case":
|
test "style insensitive":
|
||||||
bbCheck "[red]no case sensitivity[/RED]", "\e[31mno case sensitivity\e[0m"
|
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":
|
test "style full":
|
||||||
check "[red]Red[/red]".bb == bb("Red", "red")
|
check "[red]Red[/red]".bb == bb("Red", "red")
|
||||||
|
@ -53,3 +54,11 @@ suite "basic":
|
||||||
test "escape":
|
test "escape":
|
||||||
check bbEscape("[info] brackets") == "[[info] brackets"
|
check bbEscape("[info] brackets") == "[[info] brackets"
|
||||||
bbCheck 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"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue