make prompt colors configurable via GOSH_COLORS
- GOSH_COLORS='user=..:root=..:path=..' with ANSI SGR codes, empty value disables the color for that part - only digits and ';' are accepted, invalid entries are ignored - read before every prompt, so 'set GOSH_COLORS=...' takes effect immediately - document in help and README, including example color schemes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7afcecd053
commit
2b8f06ea6d
3 files changed
+155
-20
No files matched your search
+33
-6
@@ -1026,25 +1026,52 @@ func TestGetPrompt_EscapesControlCharacters(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBuildPrompt_Color(t *testing.T) {
|
||||
got := buildPrompt("flow", "host", "~/src", false, true)
|
||||
want := colorUser + "flow@host" + colorReset + ":" + colorPath + "~/src" + colorReset + " $ "
|
||||
got := buildPrompt("flow", "host", "~/src", false, &defaultColors)
|
||||
want := "\x1b[1;32mflow@host\x1b[0m:\x1b[1;34m~/src\x1b[0m $ "
|
||||
if got != want {
|
||||
t.Errorf("buildPrompt() = %q, want %q", got, want)
|
||||
}
|
||||
|
||||
got = buildPrompt("root", "host", "/", true, true)
|
||||
if !strings.HasPrefix(got, colorRoot+"root@host") {
|
||||
got = buildPrompt("root", "host", "/", true, &defaultColors)
|
||||
if !strings.HasPrefix(got, "\x1b[1;31mroot@host") {
|
||||
t.Errorf("expected red user for root, got %q", got)
|
||||
}
|
||||
|
||||
got = buildPrompt("flow", "host", "~/src", false, false)
|
||||
got = buildPrompt("flow", "host", "~/src", false, nil)
|
||||
if got != "flow@host:~/src $ " {
|
||||
t.Errorf("buildPrompt() without color = %q", got)
|
||||
}
|
||||
|
||||
colors := promptColors{user: "38;5;208", path: ""}
|
||||
got = buildPrompt("flow", "host", "~/src", false, &colors)
|
||||
if got != "\x1b[38;5;208mflow@host\x1b[0m:~/src $ " {
|
||||
t.Errorf("buildPrompt() with custom colors = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseColors(t *testing.T) {
|
||||
tests := []struct {
|
||||
spec string
|
||||
want promptColors
|
||||
}{
|
||||
{"", defaultColors},
|
||||
{"user=1;33", promptColors{user: "1;33", root: "1;31", path: "1;34"}},
|
||||
{"user=33:root=41;97:path=38;5;208", promptColors{user: "33", root: "41;97", path: "38;5;208"}},
|
||||
{"path=", promptColors{user: "1;32", root: "1;31", path: ""}},
|
||||
{"path=0m\x1b[2J", defaultColors}, // escape sequences are rejected
|
||||
{"path=red", defaultColors}, // only numeric codes
|
||||
{"foo=1:user", defaultColors}, // unknown key, missing value
|
||||
{"user=35::path=36", promptColors{user: "35", root: "1;31", path: "36"}},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
if got := parseColors(tc.spec); got != tc.want {
|
||||
t.Errorf("parseColors(%q) = %+v, want %+v", tc.spec, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildPrompt_ColorEscapesControlCharacters(t *testing.T) {
|
||||
got := buildPrompt("flow", "host", "evil\x1b[2J\x07", false, true)
|
||||
got := buildPrompt("flow", "host", "evil\x1b[2J\x07", false, &defaultColors)
|
||||
if strings.Contains(got, "\x1b[2J") || strings.Contains(got, "\x07") {
|
||||
t.Errorf("prompt contains control characters from path: %q", got)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user