refactor(terminal): make truecolor logic internal

This commit is contained in:
Jan De Dobbeleer 2024-07-01 09:54:03 +02:00 committed by Jan De Dobbeleer
parent 0c8b8021b8
commit dbc0a370b2
14 changed files with 81 additions and 37 deletions

View file

@ -70,7 +70,6 @@ Exports the config to an image file using customized output options.`,
terminal.Init(shell.GENERIC) terminal.Init(shell.GENERIC)
terminal.BackgroundColor = shell.ConsoleBackgroundColor(env, cfg.TerminalBackground) terminal.BackgroundColor = shell.ConsoleBackgroundColor(env, cfg.TerminalBackground)
terminal.AnsiColors = cfg.MakeColors() terminal.AnsiColors = cfg.MakeColors()
terminal.TrueColor = env.CmdFlags.TrueColor
eng := &engine.Engine{ eng := &engine.Engine{
Config: cfg, Config: cfg,

View file

@ -44,7 +44,6 @@ var debugCmd = &cobra.Command{
terminal.BackgroundColor = shell.ConsoleBackgroundColor(env, cfg.TerminalBackground) terminal.BackgroundColor = shell.ConsoleBackgroundColor(env, cfg.TerminalBackground)
terminal.AnsiColors = cfg.MakeColors() terminal.AnsiColors = cfg.MakeColors()
terminal.Plain = plain terminal.Plain = plain
terminal.TrueColor = env.CmdFlags.TrueColor
eng := &engine.Engine{ eng := &engine.Engine{
Config: cfg, Config: cfg,

View file

@ -69,7 +69,6 @@ func (b *Block) InitPlain(env platform.Environment, config *Config) {
terminal.Init(shell.GENERIC) terminal.Init(shell.GENERIC)
terminal.BackgroundColor = shell.ConsoleBackgroundColor(env, config.TerminalBackground) terminal.BackgroundColor = shell.ConsoleBackgroundColor(env, config.TerminalBackground)
terminal.AnsiColors = config.MakeColors() terminal.AnsiColors = config.MakeColors()
terminal.TrueColor = env.Flags().TrueColor
b.env = env b.env = env
b.executeSegmentLogic() b.executeSegmentLogic()

View file

@ -136,11 +136,11 @@ func (e *Engine) newline() {
} }
func (e *Engine) isWarp() bool { func (e *Engine) isWarp() bool {
return e.Env.Getenv("TERM_PROGRAM") == "WarpTerminal" return terminal.Program == terminal.Warp
} }
func (e *Engine) isIterm() bool { func (e *Engine) isIterm() bool {
return e.Env.Getenv("TERM_PROGRAM") == "iTerm.app" return terminal.Program == terminal.ITerm
} }
func (e *Engine) shouldFill(filler string, remaining, blockLength int) (string, bool) { func (e *Engine) shouldFill(filler string, remaining, blockLength int) (string, bool) {

View file

@ -118,7 +118,6 @@ func engineRender() {
terminal.Init(shell.GENERIC) terminal.Init(shell.GENERIC)
terminal.BackgroundColor = shell.ConsoleBackgroundColor(env, cfg.TerminalBackground) terminal.BackgroundColor = shell.ConsoleBackgroundColor(env, cfg.TerminalBackground)
terminal.AnsiColors = cfg.MakeColors() terminal.AnsiColors = cfg.MakeColors()
terminal.TrueColor = env.CmdFlags.TrueColor
engine := &Engine{ engine := &Engine{
Config: cfg, Config: cfg,

View file

@ -28,7 +28,6 @@ func New(flags *platform.Flags) *Engine {
terminal.BackgroundColor = shell.ConsoleBackgroundColor(env, cfg.TerminalBackground) terminal.BackgroundColor = shell.ConsoleBackgroundColor(env, cfg.TerminalBackground)
terminal.AnsiColors = cfg.MakeColors() terminal.AnsiColors = cfg.MakeColors()
terminal.Plain = flags.Plain terminal.Plain = flags.Plain
terminal.TrueColor = env.CmdFlags.TrueColor
eng := &Engine{ eng := &Engine{
Config: cfg, Config: cfg,

View file

@ -106,7 +106,6 @@ func (e *Engine) Primary() string {
// see https://github.com/jandedobbeleer/oh-my-posh/pull/2398 // see https://github.com/jandedobbeleer/oh-my-posh/pull/2398
terminal.Init(shell.GENERIC) terminal.Init(shell.GENERIC)
terminal.TrueColor = e.Env.Flags().TrueColor
prompt := terminal.SaveCursorPosition() prompt := terminal.SaveCursorPosition()
prompt += strings.Repeat(" ", space) prompt += strings.Repeat(" ", space)

View file

@ -58,7 +58,6 @@ type Flags struct {
HasTransient bool HasTransient bool
PromptCount int PromptCount int
Cleared bool Cleared bool
TrueColor bool
NoExitCode bool NoExitCode bool
Column int Column int
} }
@ -216,24 +215,24 @@ func (env *Shell) Init() {
if env.CmdFlags == nil { if env.CmdFlags == nil {
env.CmdFlags = &Flags{} env.CmdFlags = &Flags{}
} }
if env.CmdFlags.Debug { if env.CmdFlags.Debug {
log.Enable() log.Enable()
} }
if env.CmdFlags.Plain { if env.CmdFlags.Plain {
log.Plain() log.Plain()
} }
trueColor := true
if env.Getenv("TERM_PROGRAM") == "Apple_Terminal" {
trueColor = false
}
env.CmdFlags.TrueColor = trueColor
env.fileCache = &fileCache{} env.fileCache = &fileCache{}
env.fileCache.Init(env.CachePath()) env.fileCache.Init(env.CachePath())
env.resolveConfigPath() env.resolveConfigPath()
env.cmdCache = &commandCache{ env.cmdCache = &commandCache{
commands: NewConcurrentMap(), commands: NewConcurrentMap(),
} }
env.tmplCache = &TemplateCache{} env.tmplCache = &TemplateCache{}
env.SetPromptCount() env.SetPromptCount()
} }

View file

@ -16,7 +16,7 @@ import (
// This can include a valid hex color in the format `#FFFFFF`, // 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`. // but also a name of one of the first 16 ANSI colors like `lightBlue`.
type ColorString interface { type ColorString interface {
ToColor(colorString string, isBackground bool, trueColor bool) Color ToColor(colorString string, isBackground bool) Color
} }
type ColorSet struct { type ColorSet struct {
@ -153,45 +153,57 @@ const (
backgroundIndex = 1 backgroundIndex = 1
) )
func (d *DefaultColors) ToColor(colorString string, isBackground, trueColor bool) Color { func (d *DefaultColors) ToColor(colorString string, isBackground bool) Color {
if len(colorString) == 0 { if len(colorString) == 0 {
return emptyColor return emptyColor
} }
if colorString == Transparent { if colorString == Transparent {
return transparentColor return transparentColor
} }
if colorString == Accent { if colorString == Accent {
if d.accent == nil { if d.accent == nil {
return emptyColor return emptyColor
} }
if isBackground { if isBackground {
return Color(d.accent.Background) return Color(d.accent.Background)
} }
return Color(d.accent.Foreground) return Color(d.accent.Foreground)
} }
colorFromName, err := getAnsiColorFromName(colorString, isBackground) colorFromName, err := getAnsiColorFromName(colorString, isBackground)
if err == nil { if err == nil {
return colorFromName return colorFromName
} }
if !strings.HasPrefix(colorString, "#") { if !strings.HasPrefix(colorString, "#") {
val, err := strconv.ParseUint(colorString, 10, 64) val, err := strconv.ParseUint(colorString, 10, 64)
if err != nil || val > 255 { if err != nil || val > 255 {
return emptyColor return emptyColor
} }
c256 := color.C256(uint8(val), isBackground) c256 := color.C256(uint8(val), isBackground)
return Color(c256.String()) return Color(c256.String())
} }
style := color.HEX(colorString, isBackground) style := color.HEX(colorString, isBackground)
if !style.IsEmpty() { if !style.IsEmpty() {
if trueColor { if trueColor {
return Color(style.String()) return Color(style.String())
} }
return Color(style.C256().String()) return Color(style.C256().String())
} }
if colorInt, err := strconv.ParseInt(colorString, 10, 8); err == nil { if colorInt, err := strconv.ParseInt(colorString, 10, 8); err == nil {
c := color.C256(uint8(colorInt), isBackground) c := color.C256(uint8(colorInt), isBackground)
return Color(c.String()) return Color(c.String())
} }
return emptyColor return emptyColor
} }
@ -202,8 +214,10 @@ func getAnsiColorFromName(colorName string, isBackground bool) (Color, error) {
if isBackground { if isBackground {
return colorCodes[backgroundIndex], nil return colorCodes[backgroundIndex], nil
} }
return colorCodes[foregroundIndex], nil return colorCodes[foregroundIndex], nil
} }
return "", fmt.Errorf("color name %s does not exist", colorName) return "", fmt.Errorf("color name %s does not exist", colorName)
} }
@ -219,12 +233,14 @@ type PaletteColors struct {
palette Palette palette Palette
} }
func (p *PaletteColors) ToColor(colorString string, isBackground, trueColor bool) Color { func (p *PaletteColors) ToColor(colorString string, isBackground bool) Color {
paletteColor, err := p.palette.ResolveColor(colorString) paletteColor, err := p.palette.ResolveColor(colorString)
if err != nil { if err != nil {
return emptyColor return emptyColor
} }
ansiColor := p.ansiColors.ToColor(paletteColor, isBackground, trueColor)
ansiColor := p.ansiColors.ToColor(paletteColor, isBackground)
return ansiColor return ansiColor
} }
@ -241,7 +257,7 @@ type cachedColorKey struct {
isBackground bool isBackground bool
} }
func (c *CachedColors) ToColor(colorString string, isBackground, trueColor bool) Color { func (c *CachedColors) ToColor(colorString string, isBackground bool) Color {
if c.colorCache == nil { if c.colorCache == nil {
c.colorCache = make(map[cachedColorKey]Color) c.colorCache = make(map[cachedColorKey]Color)
} }
@ -249,7 +265,7 @@ func (c *CachedColors) ToColor(colorString string, isBackground, trueColor bool)
if ansiColor, hit := c.colorCache[key]; hit { if ansiColor, hit := c.colorCache[key]; hit {
return ansiColor return ansiColor
} }
ansiColor := c.ansiColors.ToColor(colorString, isBackground, trueColor) ansiColor := c.ansiColors.ToColor(colorString, isBackground)
c.colorCache[key] = ansiColor c.colorCache[key] = ansiColor
return ansiColor return ansiColor
} }

View file

@ -31,16 +31,15 @@ func TestGetAnsiFromColorString(t *testing.T) {
} }
for _, tc := range cases { for _, tc := range cases {
ansiColors := &DefaultColors{} ansiColors := &DefaultColors{}
ansiColor := ansiColors.ToColor(tc.Color, tc.Background, !tc.Color256) trueColor = !tc.Color256
ansiColor := ansiColors.ToColor(tc.Color, tc.Background)
assert.Equal(t, tc.Expected, ansiColor, tc.Case) assert.Equal(t, tc.Expected, ansiColor, tc.Case)
} }
} }
func TestMakeColors(t *testing.T) { func TestMakeColors(t *testing.T) {
env := &mock.MockedEnvironment{} 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")) env.On("WindowsRegistryKeyValue", `HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM\ColorizationColor`).Return(&platform.WindowsRegistryValue{}, errors.New("err"))
colors := MakeColors(nil, false, "", env) colors := MakeColors(nil, false, "", env)
assert.IsType(t, &DefaultColors{}, colors) assert.IsType(t, &DefaultColors{}, colors)

View file

@ -12,8 +12,9 @@ func (d *DefaultColors) SetAccentColor(_ platform.Environment, defaultColor stri
if len(defaultColor) == 0 { if len(defaultColor) == 0 {
return return
} }
d.accent = &Colors{ d.accent = &Colors{
Foreground: string(d.ToColor(defaultColor, false, env.Flags().TrueColor)), Foreground: string(d.ToColor(defaultColor, false)),
Background: string(d.ToColor(defaultColor, true, env.Flags().TrueColor)), Background: string(d.ToColor(defaultColor, true)),
} }
} }

View file

@ -12,11 +12,13 @@ func GetAccentColor(env platform.Environment) (*RGB, error) {
if env == nil { if env == nil {
return nil, errors.New("unable to get color without environment") return nil, errors.New("unable to get color without environment")
} }
// see https://stackoverflow.com/questions/3560890/vista-7-how-to-get-glass-color // see https://stackoverflow.com/questions/3560890/vista-7-how-to-get-glass-color
value, err := env.WindowsRegistryKeyValue(`HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM\ColorizationColor`) value, err := env.WindowsRegistryKeyValue(`HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM\ColorizationColor`)
if err != nil || value.ValueType != platform.DWORD { if err != nil || value.ValueType != platform.DWORD {
return nil, err return nil, err
} }
return &RGB{ return &RGB{
R: byte(value.DWord >> 16), R: byte(value.DWord >> 16),
G: byte(value.DWord >> 8), G: byte(value.DWord >> 8),
@ -28,13 +30,16 @@ func (d *DefaultColors) SetAccentColor(env platform.Environment, defaultColor st
rgb, err := GetAccentColor(env) rgb, err := GetAccentColor(env)
if err != nil { if err != nil {
d.accent = &Colors{ d.accent = &Colors{
Foreground: string(d.ToColor(defaultColor, false, env.Flags().TrueColor)), Foreground: string(d.ToColor(defaultColor, false)),
Background: string(d.ToColor(defaultColor, true, env.Flags().TrueColor)), Background: string(d.ToColor(defaultColor, true)),
} }
return return
} }
foreground := color.RGB(rgb.R, rgb.G, rgb.B, false) foreground := color.RGB(rgb.R, rgb.G, rgb.B, false)
background := color.RGB(rgb.R, rgb.G, rgb.B, true) background := color.RGB(rgb.R, rgb.G, rgb.B, true)
d.accent = &Colors{ d.accent = &Colors{
Foreground: foreground.String(), Foreground: foreground.String(),
Background: background.String(), Background: background.String(),

View file

@ -2,8 +2,10 @@ package terminal
import ( import (
"fmt" "fmt"
"os"
"strings" "strings"
"github.com/jandedobbeleer/oh-my-posh/src/log"
"github.com/jandedobbeleer/oh-my-posh/src/regex" "github.com/jandedobbeleer/oh-my-posh/src/regex"
"github.com/jandedobbeleer/oh-my-posh/src/shell" "github.com/jandedobbeleer/oh-my-posh/src/shell"
"github.com/mattn/go-runewidth" "github.com/mattn/go-runewidth"
@ -46,9 +48,10 @@ var (
AnsiColors ColorString AnsiColors ColorString
Plain bool Plain bool
TrueColor bool
Interactive bool Interactive bool
trueColor bool
builder strings.Builder builder strings.Builder
length int length int
@ -63,7 +66,8 @@ var (
lastRune rune lastRune rune
shellName string Shell string
Program string
formats *shellFormats formats *shellFormats
) )
@ -129,12 +133,24 @@ const (
hyperLinkEnd = "</LINK>" hyperLinkEnd = "</LINK>"
hyperLinkText = "<TEXT>" hyperLinkText = "<TEXT>"
hyperLinkTextEnd = "</TEXT>" hyperLinkTextEnd = "</TEXT>"
WindowsTerminal = "Windows Terminal"
Warp = "WarpTerminal"
ITerm = "iTerm.app"
AppleTerminal = "Apple_Terminal"
Unknown = "Unknown"
) )
func Init(sh string) { func Init(sh string) {
shellName = sh Shell = sh
Program = getTerminalName()
switch shellName { log.Debug("Terminal shell: %s", Shell)
log.Debug("Terminal program: %s", Program)
trueColor = Program != AppleTerminal
switch Shell {
case shell.BASH: case shell.BASH:
formats = &shellFormats{ formats = &shellFormats{
escape: "\\[%s\\]", escape: "\\[%s\\]",
@ -203,7 +219,7 @@ func Init(sh string) {
} }
} }
if shellName == shell.ZSH { if Shell == shell.ZSH {
formats.escapeSequences = map[rune]rune{ formats.escapeSequences = map[rune]rune{
96: 92, // backtick 96: 92, // backtick
37: 37, // % 37: 37, // %
@ -211,6 +227,20 @@ func Init(sh string) {
} }
} }
func getTerminalName() string {
Program = os.Getenv("TERM_PROGRAM")
if len(Program) != 0 {
return Program
}
wtSession := os.Getenv("WT_SESSION")
if len(wtSession) != 0 {
return WindowsTerminal
}
return Unknown
}
func SetColors(background, foreground string) { func SetColors(background, foreground string) {
CurrentColors = &Colors{ CurrentColors = &Colors{
Background: background, Background: background,
@ -281,7 +311,7 @@ func FormatTitle(title string) string {
} }
// we have to do this to prevent bash/zsh from misidentifying escape sequences // we have to do this to prevent bash/zsh from misidentifying escape sequences
switch shellName { switch Shell {
case shell.BASH: case shell.BASH:
title = strings.NewReplacer("`", "\\`", `\`, `\\`).Replace(title) title = strings.NewReplacer("`", "\\`", `\`, `\\`).Replace(title)
case shell.ZSH: case shell.ZSH:
@ -339,7 +369,7 @@ func Write(background, foreground, text string) {
// default to white foreground // default to white foreground
if foregroundColor.IsEmpty() { if foregroundColor.IsEmpty() {
foregroundColor = AnsiColors.ToColor("white", false, TrueColor) foregroundColor = AnsiColors.ToColor("white", false)
} }
// validate if we start with a color override // validate if we start with a color override
@ -451,7 +481,7 @@ func writeEscapedAnsiString(text string) {
} }
func getAnsiFromColorString(colorString string, isBackground bool) Color { func getAnsiFromColorString(colorString string, isBackground bool) Color {
return AnsiColors.ToColor(colorString, isBackground, TrueColor) return AnsiColors.ToColor(colorString, isBackground)
} }
func write(s rune) { func write(s rune) {

View file

@ -226,7 +226,7 @@ func TestWriteANSIColors(t *testing.T) {
CurrentColors = tc.Colors CurrentColors = tc.Colors
BackgroundColor = tc.TerminalBackground BackgroundColor = tc.TerminalBackground
AnsiColors = &DefaultColors{} AnsiColors = &DefaultColors{}
TrueColor = true trueColor = true
Write(tc.Colors.Background, tc.Colors.Foreground, tc.Input) Write(tc.Colors.Background, tc.Colors.Foreground, tc.Input)