refactor: Removed hardcoded colors.

This commit is contained in:
2026-06-29 15:07:50 -04:00
parent c3e667e7bc
commit 17d7fb5eb6
10 changed files with 134 additions and 161 deletions
+1 -1
View File
@@ -24,7 +24,7 @@
12. Rewrite `write_command_help` to use text/tables 12. Rewrite `write_command_help` to use text/tables
13. Instead of using a writer to strip colors, just don't print the colors. 13. Consider getting rid of color global.
14. Add a text filter to the multi_select. 14. Add a text filter to the multi_select.
+38 -49
View File
@@ -106,7 +106,7 @@ parse_args :: proc(args: []string, out: io.Stream, err: io.Stream) -> (cmd: Comm
cmd.flags.color = terminal.is_terminal(os.stdout) ? .Always : .Never cmd.flags.color = terminal.is_terminal(os.stdout) ? .Always : .Never
} }
if cmd.flags.color == .Never { if cmd.flags.color == .Never {
cmd.out = make_ansi_strip_writer(cmd.out) disable_color = true
} }
if cmd.flags.config_file == "" { if cmd.flags.config_file == "" {
@@ -141,36 +141,23 @@ write_command_help :: proc(name: string, w: io.Writer) -> bool {
fmt.wprintf( fmt.wprintf(
w, w,
"%s\n\n\n" + "%s\n\n\n%s\n\n %s [flags]\n\n",
COLOR_HEADINGS +
"Usage:" +
ANSI_RESET +
"\n\n " +
COLOR_FLAGS +
"%s" +
ANSI_RESET +
" [flags]\n\n",
info.short, info.short,
info.usage, colorize(.Heading, "Usage:"),
colorize(.Flag, info.usage),
flush = false, flush = false,
) )
if len(info.aliases) > 0 { if len(info.aliases) > 0 {
fmt.wprintf( fmt.wprintf(
w, w,
"\n" + "\n%s\n\n %s",
COLOR_HEADINGS + colorize(.Heading, "Aliases:"),
"Aliases:" + colorize(.Command, info.name),
ANSI_RESET +
"\n\n " +
COLOR_COMMANDS +
"%s" +
ANSI_RESET,
info.name,
flush = false, flush = false,
) )
for a in info.aliases { for a in info.aliases {
fmt.wprintf(w, ", " + COLOR_COMMANDS + "%s" + ANSI_RESET, a, flush = false) fmt.wprintf(w, ", %s", colorize(.Command, a), flush = false)
} }
fmt.wprintf(w, "\n", flush = false) fmt.wprintf(w, "\n", flush = false)
} }
@@ -181,21 +168,14 @@ write_command_help :: proc(name: string, w: io.Writer) -> bool {
fmt.wprintf( fmt.wprintf(
w, w,
"\n" + "\n%s\n\n %s" +
COLOR_HEADINGS + ` help for %s
"Flags:" + %s <path> config file (default "~/.envr/config.json")
ANSI_RESET +
"\n\n " +
COLOR_FLAGS +
"-h, --help" +
ANSI_RESET +
" help for %s\n " +
COLOR_FLAGS +
"-c, --config-file" +
ANSI_RESET +
` <path> config file (default "~/.envr/config.json")
`, `,
colorize(.Heading, "Flags:"),
colorize(.Flag, "-h, --help"),
info.name, info.name,
colorize(.Flag, "-c, --config-file"),
flush = false, flush = false,
) )
return true return true
@@ -250,15 +230,13 @@ at before, restore your backup with:
> envr restore ~/<path to repository>/.env > envr restore ~/<path to repository>/.env
%sUsage:%s %s
%senvr%s [command] %s [command]
`, `,
COLOR_HEADINGS, colorize(.Heading, "Usage:"),
ANSI_RESET, colorize(.Flag, "envr"),
COLOR_FLAGS,
ANSI_RESET,
flush = false, flush = false,
) )
@@ -274,7 +252,7 @@ at before, restore your backup with:
for a in c.aliases { for a in c.aliases {
name = strings.join([]string{name, a}, ", ", tbl.format_allocator) name = strings.join([]string{name, a}, ", ", tbl.format_allocator)
} }
table.row(&tbl, table.format(&tbl, "%s%s%s", COLOR_COMMANDS, name, ANSI_RESET), c.short) table.row(&tbl, colorize(.Command, name, tbl.format_allocator), c.short)
} }
write_borderless_table(w, &tbl) write_borderless_table(w, &tbl)
@@ -282,29 +260,40 @@ at before, restore your backup with:
table.caption(&tbl, "Flags:") table.caption(&tbl, "Flags:")
table.row(&tbl, COLOR_FLAGS + "-h, --help" + ANSI_RESET, `show this documentation`) table.row(&tbl, colorize(.Flag, "-h, --help", tbl.format_allocator), `show this documentation`)
table.row( table.row(
&tbl, &tbl,
COLOR_FLAGS + "-c, --config-file" + ANSI_RESET + " <path>", table.format(
&tbl,
"%s <path>",
colorize(.Flag, "-c, --config-file", tbl.format_allocator),
),
`config file (default "~/.envr/config.json")`, `config file (default "~/.envr/config.json")`,
) )
table.row( table.row(
&tbl, &tbl,
COLOR_FLAGS + "-o, --output" + ANSI_RESET + " 'table'|'json'", table.format(
`the format of output data. (default 'table')`, &tbl,
"%s 'table'|'json'",
colorize(.Flag, "-o, --output", tbl.format_allocator),
),
`The format of output data. (default 'table')`,
) )
table.row( table.row(
&tbl, &tbl,
COLOR_FLAGS + "--color" + ANSI_RESET + " 'auto'|'always'|'never'", table.format(
&tbl,
"%s 'auto'|'always'|'never'",
colorize(.Flag, "--color", tbl.format_allocator),
),
`Whether or not to colorize output. (default 'auto')`, `Whether or not to colorize output. (default 'auto')`,
) )
write_borderless_table(w, &tbl) write_borderless_table(w, &tbl)
fmt.wprintf( fmt.wprintf(
w, w,
`Use "%senvr%s [command] --help" for more information about a command.`, `Use "%s [command] --help" for more information about a command.`,
COLOR_FLAGS, colorize(.Flag, "envr", tbl.format_allocator),
ANSI_RESET,
flush = false, flush = false,
) )
} }
+1 -5
View File
@@ -38,11 +38,7 @@ Generate one with: ssh-keygen -t ed25519`,
selected, result := multi_select("Select SSH private keys:", keys[:]) selected, result := multi_select("Select SSH private keys:", keys[:])
defer delete(selected) defer delete(selected)
if result == .Cancel { if result == .Cancel {
fmt.wprintln( fmt.wprintln(cmd.out, colorize(.Message, "Cancelled."), flush = false)
cmd.out,
ansi.CSI + ansi.FAINT + ansi.SGR + "Cancelled." + ANSI_RESET,
flush = false,
)
return return
} }
+2 -2
View File
@@ -32,8 +32,8 @@ cmd_list :: proc(cmd: ^Command) {
table.aligned_header_of_values( table.aligned_header_of_values(
&t, &t,
.Center, .Center,
COLOR_TABLE_HEADING + "Directory" + ANSI_RESET, colorize(.Table_Heading, "Directory"),
COLOR_TABLE_HEADING + "Path" + ANSI_RESET, colorize(.Table_Heading, "Path"),
) )
for row in rows { for row in rows {
+7 -21
View File
@@ -4,7 +4,6 @@ import "core:encoding/json"
import "core:fmt" import "core:fmt"
import "core:os" import "core:os"
import "core:terminal" import "core:terminal"
import "core:terminal/ansi"
cmd_scan :: proc(cmd: ^Command) { cmd_scan :: proc(cmd: ^Command) {
db, db_ok := db_open(cmd.flags.config_file) db, db_ok := db_open(cmd.flags.config_file)
@@ -72,11 +71,7 @@ cmd_scan :: proc(cmd: ^Command) {
selected, result := multi_select("Select .env files to backup:", files[:]) selected, result := multi_select("Select .env files to backup:", files[:])
defer delete(selected) defer delete(selected)
if result == .Cancel { if result == .Cancel {
fmt.wprintln( fmt.wprintln(cmd.out, colorize(.Message, "Cancelled."), flush = false)
cmd.out,
ansi.CSI + ansi.FAINT + ansi.SGR + "Cancelled." + ANSI_RESET,
flush = false,
)
return return
} }
@@ -100,25 +95,16 @@ cmd_scan :: proc(cmd: ^Command) {
} }
if added_count > 0 { if added_count > 0 {
fmt.wprintf( fmt.wprintln(
cmd.out, cmd.out,
ansi.CSI + colorize(
ansi.BOLD + .Sucess,
";" + fmt.tprintf("Successfully added %d file(s) to backup.", added_count),
ansi.FG_GREEN + ),
ansi.SGR +
"Successfully added %d file(s) to backup." +
ANSI_RESET +
"\n",
added_count,
flush = false, flush = false,
) )
} else { } else {
fmt.wprintln( fmt.wprintln(cmd.out, colorize(.Message, "No files were added."), flush = false)
cmd.out,
ansi.CSI + ansi.FAINT + ansi.SGR + "No files were added." + ANSI_RESET,
flush = false,
)
} }
} }
+2 -2
View File
@@ -54,8 +54,8 @@ cmd_sync :: proc(cmd: ^Command) {
table.aligned_header_of_values( table.aligned_header_of_values(
&t, &t,
.Center, .Center,
COLOR_TABLE_HEADING + "File" + ANSI_RESET, colorize(.Table_Heading, "File"),
COLOR_TABLE_HEADING + "Status" + ANSI_RESET, colorize(.Table_Heading, "Status"),
) )
for res in results { for res in results {
+49 -69
View File
@@ -1,86 +1,66 @@
package main package main
import "core:io" import "base:runtime"
import "core:strings"
import "core:terminal/ansi" import "core:terminal/ansi"
COLOR_HEADINGS :: Color_Code :: enum {
ansi.CSI + ansi.FG_BRIGHT_GREEN + ";" + ansi.BOLD + ";" + ansi.UNDERLINE + ansi.SGR Heading,
Command,
// Example
Flag,
Table_Heading,
Message,
Sucess,
Caret,
Option_Label,
}
COLOR_COMMANDS :: ansi.CSI + ansi.FG_BRIGHT_CYAN + ";" + ansi.BOLD + ansi.SGR // COLOR_EXAMPLE :: ansi.CSI + ansi.ITALIC + ansi.SGR
COLOR_EXAMPLE :: ansi.CSI + ansi.ITALIC + ansi.SGR
COLOR_FLAGS :: ansi.CSI + ansi.BOLD + ";" + ansi.FG_BRIGHT_WHITE + ansi.SGR
COLOR_TABLE_HEADING :: ansi.CSI + ansi.FG_BRIGHT_GREEN + ansi.SGR
@(private = "file")
ANSI_RESET :: ansi.CSI + ansi.RESET + ansi.SGR ANSI_RESET :: ansi.CSI + ansi.RESET + ansi.SGR
ANSI_Strip_State :: enum { Normal, GotESC, InCSI } disable_color := false
ANSI_Strip_Data :: struct { colorize :: proc(
inner: io.Writer, color: Color_Code,
state: ANSI_Strip_State, text: string,
} allocator := context.temp_allocator,
disable := disable_color,
ansi_strip_proc :: proc( ) -> (
stream_data: rawptr, string,
mode: io.Stream_Mode, runtime.Allocator_Error,
p: []byte, ) #optional_allocator_error {
offset: i64, if disable {
whence: io.Seek_From, return text, nil
) -> (n: i64, err: io.Error) {
data := cast(^ANSI_Strip_Data) stream_data
#partial switch mode {
case .Write:
start := 0
for i in 0..<len(p) {
b := p[i]
switch data.state {
case .Normal:
if b == 0x1b {
if i > start {
io.write(data.inner, p[start:i])
}
data.state = .GotESC
}
case .GotESC:
if b == '[' {
data.state = .InCSI
} else { } else {
start = i return strings.concatenate(
data.state = .Normal {ansi.CSI, color_code(color), ansi.SGR, text, ANSI_RESET},
} allocator,
)
case .InCSI:
if b >= 0x40 && b <= 0x7E {
start = i + 1
data.state = .Normal
}
} }
} }
if data.state == .Normal && len(p) > start { @(private = "file")
io.write(data.inner, p[start:]) color_code :: proc(code: Color_Code) -> string {
} switch code {
case .Heading:
n = i64(len(p)) return ansi.BOLD + ";" + ansi.UNDERLINE + ";" + ansi.FG_BRIGHT_GREEN
return case .Command:
return ansi.BOLD + ";" + ansi.FG_BRIGHT_CYAN
case .Flush: case .Flag:
return 0, io.flush(data.inner) return ansi.BOLD + ";" + ansi.FG_BRIGHT_WHITE
case .Close: case .Table_Heading:
return 0, io.close(data.inner) return ansi.FG_BRIGHT_GREEN
case .Message:
return ansi.FAINT
case .Sucess, .Caret:
return ansi.BOLD + ";" + ansi.FG_GREEN
case .Option_Label:
return ansi.BOLD + ";" + ansi.FG_CYAN
case: case:
return data.inner.procedure(data.inner.data, mode, p, offset, whence) panic("Unknown case")
} }
} }
make_ansi_strip_writer :: proc(inner: io.Writer) -> io.Writer {
data := new(ANSI_Strip_Data, context.temp_allocator)
data.inner = inner
return io.Writer{procedure = ansi_strip_proc, data = rawptr(data)}
}
+19 -5
View File
@@ -66,11 +66,17 @@ multi_select :: proc(
case .Space: case .Space:
selected[cursor] = !selected[cursor] selected[cursor] = !selected[cursor]
case .Enter: case .Enter:
fmt.printf(ansi.CSI + "%d" + ansi.CUU + ansi.CSI + ansi.ED + ansi.CSI + ansi.DECTCEM_SHOW, visible + 1) fmt.printf(
ansi.CSI + "%d" + ansi.CUU + ansi.CSI + ansi.ED + ansi.CSI + ansi.DECTCEM_SHOW,
visible + 1,
)
result = .Confirm result = .Confirm
return return
case .Escape: case .Escape:
fmt.printf(ansi.CSI + "%d" + ansi.CUU + ansi.CSI + ansi.ED + ansi.CSI + ansi.DECTCEM_SHOW, visible + 1) fmt.printf(
ansi.CSI + "%d" + ansi.CUU + ansi.CSI + ansi.ED + ansi.CSI + ansi.DECTCEM_SHOW,
visible + 1,
)
result = .Cancel result = .Cancel
return return
case .Unknown: case .Unknown:
@@ -89,7 +95,10 @@ render_options :: proc(
cursor: int, cursor: int,
scroll_offset: int, scroll_offset: int,
) -> int { ) -> int {
fmt.printf(ansi.CSI + ansi.BOLD + ";" + ansi.FG_CYAN + ansi.SGR + "%s" + ANSI_RESET + " (↑/↓ move, space select, enter confirm)\r\n", prompt) fmt.printf(
"%s (↑/↓ move, space select, enter confirm)\r\n",
colorize(.Option_Label, prompt),
)
end := scroll_offset + MAX_VISIBLE end := scroll_offset + MAX_VISIBLE
if end > len(options) { if end > len(options) {
@@ -102,9 +111,14 @@ render_options :: proc(
checkbox = "x" checkbox = "x"
} }
if i == cursor { if i == cursor {
fmt.printf(ansi.CSI + ansi.BOLD + ";" + ansi.FG_GREEN + ansi.SGR + "> " + ANSI_RESET + "[" + ansi.CSI + ansi.FG_GREEN + ansi.SGR + "%s" + ANSI_RESET + "] %s\r\n", checkbox, options[i]) fmt.printf(
"%s [%s] %s\r\n",
colorize(.Caret, ">"),
colorize(.Sucess, checkbox),
options[i],
)
} else { } else {
fmt.printf(" [" + ansi.CSI + ansi.FAINT + ansi.SGR + "%s" + ANSI_RESET + "] %s\r\n", checkbox, options[i]) fmt.printf(" [%s] %s\r\n", colorize(.Sucess, checkbox), options[i])
} }
} }
+1 -1
View File
@@ -42,7 +42,7 @@ write_borderless_table :: proc(w: io.Writer, t: ^table.Table) {
if t.caption != "" { if t.caption != "" {
table.write_text_align( table.write_text_align(
w, w,
fmt.tprintf("%s%s%s", COLOR_HEADINGS, t.caption, ANSI_RESET), colorize(.Heading, t.caption),
.Left, .Left,
0, //t.lpad, 0, //t.lpad,
0, //t.rpad, 0, //t.rpad,
+10 -2
View File
@@ -2,6 +2,7 @@
package main package main
import "core:strings"
import "core:testing" import "core:testing"
@(test) @(test)
@@ -16,7 +17,7 @@ test_ansi_aware_width_empty :: proc(t: ^testing.T) {
@(test) @(test)
test_ansi_aware_width_with_color_codes :: proc(t: ^testing.T) { test_ansi_aware_width_with_color_codes :: proc(t: ^testing.T) {
colored := COLOR_TABLE_HEADING + "Directory" + ANSI_RESET colored := colorize(.Heading, "Directory", disable = false)
testing.expect_value(t, ansi_aware_width(colored), 9) testing.expect_value(t, ansi_aware_width(colored), 9)
} }
@@ -28,6 +29,13 @@ test_ansi_aware_width_multibyte :: proc(t: ^testing.T) {
@(test) @(test)
test_ansi_aware_width_multiple_escape_sequences :: proc(t: ^testing.T) { test_ansi_aware_width_multiple_escape_sequences :: proc(t: ^testing.T) {
colored := COLOR_TABLE_HEADING + "a" + ANSI_RESET + "b" + COLOR_TABLE_HEADING + "c" + ANSI_RESET colored := strings.concatenate(
{
colorize(.Heading, "a", disable = false),
colorize(.Heading, "b", disable = false),
colorize(.Heading, "c", disable = false),
},
)
testing.expect_value(t, ansi_aware_width(colored), 3) testing.expect_value(t, ansi_aware_width(colored), 3)
} }