Repository: JanDeDobbeleer/oh-my-posh
Pull request #3619 (2023-03-23 08:16:04)

feat: enable/disable true color per terminal
feat: enable/disable true color per terminal



Patch:
diff --git a/src/ansi/ansi_writer.go b/src/ansi/ansi_writer.go
index be5dad74..1311011e 100644
--- a/src/ansi/ansi_writer.go
+++ b/src/ansi/ansi_writer.go
@@ -80,6 +80,7 @@ type Writer struct {
 	ParentColors       []*Colors
 	AnsiColors         ColorString
 	Plain              bool
+	TrueColor          bool
 
 	builder strings.Builder
 	length  int
@@ -283,7 +284,7 @@ func (w *Writer) Write(background, foreground, text string) {
 	w.background, w.foreground = w.asAnsiColors(background, foreground)
 	// default to white foreground
 	if w.foreground.IsEmpty() {
-		w.foreground = w.AnsiColors.ToColor("white", false)
+		w.foreground = w.AnsiColors.ToColor("white", false, w.TrueColor)
 	}
 	// validate if we start with a color override
 	match := regex.FindNamedRegexMatch(anchorRegex, text)
@@ -365,7 +366,7 @@ func (w *Writer) writeEscapedAnsiString(text string) {
 }
 
 func (w *Writer) getAnsiFromColorString(colorString string, isBackground bool) Color {
-	return w.AnsiColors.ToColor(colorString, isBackground)
+	return w.AnsiColors.ToColor(colorString, isBackground, w.TrueColor)
 }
 
 func (w *Writer) writeSegmentColors() {
diff --git a/src/ansi/colors.go b/src/ansi/colors.go
index 7dfac222..fa95b45a 100644
--- a/src/ansi/colors.go
+++ b/src/ansi/colors.go
@@ -16,7 +16,7 @@ import (
 // This can include a valid hex color in the format `#FFFFFF`,
 // but also a name of one of the first 16 ANSI colors like `lightBlue`.
 type ColorString interface {
-	ToColor(colorString string, isBackground bool) Color
+	ToColor(colorString string, isBackground bool, trueColor bool) Color
 }
 
 // Color is an ANSI color code ready to be printed to the console.
@@ -98,7 +98,7 @@ const (
 	backgroundIndex = 1
 )
 
-func (d *DefaultColors) ToColor(colorString string, isBackground bool) Color {
+func (d *DefaultColors) ToColor(colorString string, isBackground, trueColor bool) Color {
 	if len(colorString) == 0 {
 		return emptyColor
 	}
@@ -128,7 +128,10 @@ func (d *DefaultColors) ToColor(colorString string, isBackground bool) Color {
 	}
 	style := color.HEX(colorString, isBackground)
 	if !style.IsEmpty() {
-		return Color(style.String())
+		if trueColor {
+			return Color(style.String())
+		}
+		return Color(style.C256().String())
 	}
 	if colorInt, err := strconv.ParseInt(colorString, 10, 8); err == nil {
 		c := color.C256(uint8(colorInt), isBackground)
@@ -161,12 +164,12 @@ type PaletteColors struct {
 	palette    Palette
 }
 
-func (p *PaletteColors) ToColor(colorString string, isBackground bool) Color {
+func (p *PaletteColors) ToColor(colorString string, isBackground, trueColor bool) Color {
 	paletteColor, err := p.palette.ResolveColor(colorString)
 	if err != nil {
 		return emptyColor
 	}
-	ansiColor := p.ansiColors.ToColor(paletteColor, isBackground)
+	ansiColor := p.ansiColors.ToColor(paletteColor, isBackground, trueColor)
 	return ansiColor
 }
 
@@ -183,7 +186,7 @@ type cachedColorKey struct {
 	isBackground bool
 }
 
-func (c *CachedColors) ToColor(colorString string, isBackground bool) Color {
+func (c *CachedColors) ToColor(colorString string, isBackground, trueColor bool) Color {
 	if c.colorCache == nil {
 		c.colorCache = make(map[cachedColorKey]Color)
 	}
@@ -191,7 +194,7 @@ func (c *CachedColors) ToColor(colorString string, isBackground bool) Color {
 	if ansiColor, hit := c.colorCache[key]; hit {
 		return ansiColor
 	}
-	ansiColor := c.ansiColors.ToColor(colorString, isBackground)
+	ansiColor := c.ansiColors.ToColor(colorString, isBackground, trueColor)
 	c.colorCache[key] = ansiColor
 	return ansiColor
 }
diff --git a/src/ansi/colors_unix.go b/src/ansi/colors_unix.go
index f6885d68..bb2be8d5 100644
--- a/src/ansi/colors_unix.go
+++ b/src/ansi/colors_unix.go
@@ -8,12 +8,12 @@ func GetAccentColor(_ platform.Environment) (*RGB, error) {
 	return nil, &platform.NotImplemented{}
 }
 
-func (d *DefaultColors) SetAccentColor(_ platform.Environment, defaultColor string) {
+func (d *DefaultColors) SetAccentColor(env platform.Environment, defaultColor string) {
 	if len(defaultColor) == 0 {
 		return
 	}
 	d.accent = &Colors{
-		Foreground: string(d.ToColor(defaultColor, false)),
-		Background: string(d.ToColor(defaultColor, true)),
+		Foreground: string(d.ToColor(defaultColor, false, env.Flags().TrueColor)),
+		Background: string(d.ToColor(defaultColor, true, env.Flags().TrueColor)),
 	}
 }
diff --git a/src/ansi/colors_windows.go b/src/ansi/colors_windows.go
index 79f61da3..e77f29d9 100644
--- a/src/ansi/colors_windows.go
+++ b/src/ansi/colors_windows.go
@@ -28,8 +28,8 @@ func (d *DefaultColors) SetAccentColor(env platform.Environment, defaultColor st
 	rgb, err := GetAccentColor(env)
 	if err != nil {
 		d.accent = &Colors{
-			Foreground: string(d.ToColor(defaultColor, false)),
-			Background: string(d.ToColor(defaultColor, true)),
+			Foreground: string(d.ToColor(defaultColor, false, env.Flags().TrueColor)),
+			Background: string(d.ToColor(defaultColor, true, env.Flags().TrueColor)),
 		}
 		return
 	}
diff --git a/src/cli/config_export_image.go b/src/cli/config_export_image.go
index 74d69c1d..5d495df1 100644
--- a/src/cli/config_export_image.go
+++ b/src/cli/config_export_image.go
@@ -66,6 +66,7 @@ Exports the config to an image file using customized output options.`,
 		writer := &ansi.Writer{
 			TerminalBackground: shell.ConsoleBackgroundColor(env, cfg.TerminalBackground),
 			AnsiColors:         writerColors,
+			TrueColor:          env.CmdFlags.TrueColor,
 		}
 		writer.Init(shell.GENERIC)
 		eng := &engine.Engine{
diff --git a/src/cli/debug.go b/src/cli/debug.go
index bc0f752d..babceb0a 100644
--- a/src/cli/debug.go
+++ b/src/cli/debug.go
@@ -38,6 +38,7 @@ var debugCmd = &cobra.Command{
 			TerminalBackground: shell.ConsoleBackgroundColor(env, cfg.TerminalBackground),
 			AnsiColors:         writerColors,
 			Plain:              plain,
+			TrueColor:          env.CmdFlags.TrueColor,
 		}
 		writer.Init(shell.GENERIC)
 		eng := &engine.Engine{
diff --git a/src/engine/block.go b/src/engine/block.go
index f82a8f48..5f455a81 100644
--- a/src/engine/block.go
+++ b/src/engine/block.go
@@ -67,6 +67,7 @@ func (b *Block) InitPlain(env platform.Environment, config *Config) {
 	b.writer = &ansi.Writer{
 		TerminalBackground: shell.ConsoleBackgroundColor(env, config.TerminalBackground),
 		AnsiColors:         config.MakeColors(),
+		TrueColor:          env.Flags().TrueColor,
 	}
 	b.writer.Init(shell.GENERIC)
 	b.env = env
diff --git a/src/engine/engine.go b/src/engine/engine.go
index 6bcb1c5d..8297cb68 100644
--- a/src/engine/engine.go
+++ b/src/engine/engine.go
@@ -337,7 +337,9 @@ func (e *Engine) print() string {
 		}
 		// in bash, the entire rprompt needs to be escaped for the prompt to be interpreted correctly
 		// see https://github.com/jandedobbeleer/oh-my-posh/pull/2398
-		writer := &ansi.Writer{}
+		writer := &ansi.Writer{
+			TrueColor: e.Env.Flags().TrueColor,
+		}
 		writer.Init(shell.GENERIC)
 		prompt := writer.SaveCursorPosition()
 		prompt += writer.CarriageForward()
diff --git a/src/engine/new.go b/src/engine/new.go
index 1406c8ab..fea6ac6c 100644
--- a/src/engine/new.go
+++ b/src/engine/new.go
@@ -22,6 +22,7 @@ func New(flags *platform.Flags) *Engine {
 		TerminalBackground: shell.ConsoleBackgroundColor(env, cfg.TerminalBackground),
 		AnsiColors:         cfg.MakeColors(),
 		Plain:              flags.Plain,
+		TrueColor:          env.CmdFlags.TrueColor,
 	}
 	ansiWriter.Init(env.Shell())
 
diff --git a/src/platform/shell.go b/src/platform/shell.go
index f3fd3cf4..153b5921 100644
--- a/src/platform/shell.go
+++ b/src/platform/shell.go
@@ -71,6 +71,7 @@ type Flags struct {
 	PromptCount   int
 	Cleared       bool
 	Version       string
+	TrueColor     bool
 }
 
 type CommandError struct {
@@ -300,6 +301,11 @@ func (env *Shell) Init() {
 	if env.CmdFlags.Plain {
 		log.Plain()
 	}
+	trueColor := true
+	if env.Getenv("TERM_PROGRAM") == "Apple_Terminal" {
+		trueColor = false
+	}
+	env.CmdFlags.TrueColor = trueColor
 	env.fileCache = &fileCache{}
 	env.fileCache.Init(env.CachePath())
 	env.resolveConfigPath()


Test patch:
diff --git a/src/ansi/ansi_writer_test.go b/src/ansi/ansi_writer_test.go
index 886374b0..ffa33dde 100644
--- a/src/ansi/ansi_writer_test.go
+++ b/src/ansi/ansi_writer_test.go
@@ -203,6 +203,7 @@ func TestWriteANSIColors(t *testing.T) {
 			Colors:             tc.Colors,
 			TerminalBackground: tc.TerminalBackground,
 			AnsiColors:         &DefaultColors{},
+			TrueColor:          true,
 		}
 		renderer.Init(shell.GENERIC)
 		renderer.Write(tc.Colors.Background, tc.Colors.Foreground, tc.Input)
diff --git a/src/ansi/colors_test.go b/src/ansi/colors_test.go
index 018d08eb..9c59a64b 100644
--- a/src/ansi/colors_test.go
+++ b/src/ansi/colors_test.go
@@ -1,9 +1,12 @@
 package ansi
 
 import (
+	"errors"
 	"testing"
 
 	"github.com/alecthomas/assert"
+	"github.com/jandedobbeleer/oh-my-posh/src/mock"
+	"github.com/jandedobbeleer/oh-my-posh/src/platform"
 )
 
 func TestGetAnsiFromColorString(t *testing.T) {
@@ -12,6 +15,7 @@ func TestGetAnsiFromColorString(t *testing.T) {
 		Expected   Color
 		Color      string
 		Background bool
+		Color256   bool
 	}{
 		{Case: "256 color", Expected: Color("38;5;99"), Color: "99", Background: false},
 		{Case: "256 color", Expected: Color("38;5;122"), Color: "122", Background: false},
@@ -23,27 +27,33 @@ func TestGetAnsiFromColorString(t *testing.T) {
 		{Case: "Base 8 background", Expected: Color("41"), Color: "red", Background: true},
 		{Case: "Base 16 foreground", Expected: Color("91"), Color: "lightRed", Background: false},
 		{Case: "Base 16 backround", Expected: Color("101"), Color: "lightRed", Background: true},
+		{Case: "Non true color TERM", Expected: Color("38;5;146"), Color: "#AABBCC", Color256: true},
 	}
 	for _, tc := range cases {
 		ansiColors := &DefaultColors{}
-		ansiColor := ansiColors.ToColor(tc.Color, tc.Background)
+		ansiColor := ansiColors.ToColor(tc.Color, tc.Background, !tc.Color256)
 		assert.Equal(t, tc.Expected, ansiColor, tc.Case)
 	}
 }
 
 func TestMakeColors(t *testing.T) {
-	colors := MakeColors(nil, false, "", nil)
+	env := &mock.MockedEnvironment{}
+	env.On("Flags").Return(&platform.Flags{
+		TrueColor: true,
+	})
+	env.On("WindowsRegistryKeyValue", `HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM\ColorizationColor`).Return(&platform.WindowsRegistryValue{}, errors.New("err"))
+	colors := MakeColors(nil, false, "", env)
 	assert.IsType(t, &DefaultColors{}, colors)
 
-	colors = MakeColors(nil, true, "", nil)
+	colors = MakeColors(nil, true, "", env)
 	assert.IsType(t, &CachedColors{}, colors)
 	assert.IsType(t, &DefaultColors{}, colors.(*CachedColors).ansiColors)
 
-	colors = MakeColors(testPalette, false, "", nil)
+	colors = MakeColors(testPalette, false, "", env)
 	assert.IsType(t, &PaletteColors{}, colors)
 	assert.IsType(t, &DefaultColors{}, colors.(*PaletteColors).ansiColors)
 
-	colors = MakeColors(testPalette, true, "", nil)
+	colors = MakeColors(testPalette, true, "", env)
 	assert.IsType(t, &CachedColors{}, colors)
 	assert.IsType(t, &PaletteColors{}, colors.(*CachedColors).ansiColors)
 	assert.IsType(t, &DefaultColors{}, colors.(*CachedColors).ansiColors.(*PaletteColors).ansiColors)
diff --git a/src/engine/engine_test.go b/src/engine/engine_test.go
index 9d9d31f9..ec38192e 100644
--- a/src/engine/engine_test.go
+++ b/src/engine/engine_test.go
@@ -105,6 +105,7 @@ func engineRender() {
 	writer := &ansi.Writer{
 		TerminalBackground: shell.ConsoleBackgroundColor(env, cfg.TerminalBackground),
 		AnsiColors:         writerColors,
+		TrueColor:          env.CmdFlags.TrueColor,
 	}
 	writer.Init(shell.GENERIC)
 	engine := &Engine{