mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-12-27 11:59:40 -08:00
feat(colors): allow nested overrides
This commit is contained in:
parent
20c11453d5
commit
346091feb8
|
@ -86,13 +86,12 @@ type Writer struct {
|
||||||
builder strings.Builder
|
builder strings.Builder
|
||||||
length int
|
length int
|
||||||
|
|
||||||
foreground Color
|
foreground Color
|
||||||
background Color
|
background Color
|
||||||
currentForeground Color
|
current ColorHistory
|
||||||
currentBackground Color
|
runes []rune
|
||||||
runes []rune
|
transparent bool
|
||||||
transparent bool
|
invisible bool
|
||||||
invisible bool
|
|
||||||
|
|
||||||
shell string
|
shell string
|
||||||
format string
|
format string
|
||||||
|
@ -303,7 +302,8 @@ func (w *Writer) Write(background, foreground, text string) {
|
||||||
colorOverride = false
|
colorOverride = false
|
||||||
}
|
}
|
||||||
if colorOverride {
|
if colorOverride {
|
||||||
w.currentBackground, w.currentForeground = w.asAnsiColors(match[BG], match[FG])
|
w.current.Add(w.asAnsiColors(match[BG], match[FG]))
|
||||||
|
// w.current.Background(), w.current.Foreground() = w.asAnsiColors(match[BG], match[FG])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.writeSegmentColors()
|
w.writeSegmentColors()
|
||||||
|
@ -343,8 +343,7 @@ func (w *Writer) Write(background, foreground, text string) {
|
||||||
w.writeEscapedAnsiString(resetStyle.End)
|
w.writeEscapedAnsiString(resetStyle.End)
|
||||||
|
|
||||||
// reset current
|
// reset current
|
||||||
w.currentBackground = ""
|
w.current.Pop()
|
||||||
w.currentForeground = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Writer) String() (string, int) {
|
func (w *Writer) String() (string, int) {
|
||||||
|
@ -378,11 +377,11 @@ func (w *Writer) writeSegmentColors() {
|
||||||
// use correct starting colors
|
// use correct starting colors
|
||||||
bg := w.background
|
bg := w.background
|
||||||
fg := w.foreground
|
fg := w.foreground
|
||||||
if !w.currentBackground.IsEmpty() {
|
if !w.current.Background().IsEmpty() {
|
||||||
bg = w.currentBackground
|
bg = w.current.Background()
|
||||||
}
|
}
|
||||||
if !w.currentForeground.IsEmpty() {
|
if !w.current.Foreground().IsEmpty() {
|
||||||
fg = w.currentForeground
|
fg = w.current.Foreground()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore processing fully tranparent colors
|
// ignore processing fully tranparent colors
|
||||||
|
@ -408,45 +407,14 @@ func (w *Writer) writeSegmentColors() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// set current colors
|
// set current colors
|
||||||
w.currentBackground = bg
|
w.current.Add(bg, fg)
|
||||||
w.currentForeground = fg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Writer) writeColorOverrides(match map[string]string, background string, i int) int {
|
func (w *Writer) writeColorOverrides(match map[string]string, background string, i int) int {
|
||||||
position := i
|
position := i
|
||||||
// check color reset first
|
// check color reset first
|
||||||
if match[ANCHOR] == resetStyle.AnchorEnd {
|
if match[ANCHOR] == resetStyle.AnchorEnd {
|
||||||
// make sure to reset the colors if needed
|
return w.endColorOverride(position)
|
||||||
position += len([]rune(resetStyle.AnchorEnd)) - 1
|
|
||||||
|
|
||||||
// do not reset when colors are identical
|
|
||||||
if w.currentBackground == w.background && w.currentForeground == w.foreground {
|
|
||||||
return position
|
|
||||||
}
|
|
||||||
|
|
||||||
// do not restore colors at the end of the string, we print it anyways
|
|
||||||
if position == len(w.runes)-1 {
|
|
||||||
return position
|
|
||||||
}
|
|
||||||
|
|
||||||
if w.transparent {
|
|
||||||
w.writeEscapedAnsiString(transparentEnd)
|
|
||||||
}
|
|
||||||
|
|
||||||
if w.background.IsClear() {
|
|
||||||
w.writeEscapedAnsiString(backgroundStyle.End)
|
|
||||||
}
|
|
||||||
|
|
||||||
if w.currentBackground != w.background && !w.background.IsClear() {
|
|
||||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.background))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (w.currentForeground != w.foreground || w.transparent) && !w.foreground.IsClear() {
|
|
||||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.foreground))
|
|
||||||
}
|
|
||||||
|
|
||||||
w.transparent = false
|
|
||||||
return position
|
|
||||||
}
|
}
|
||||||
|
|
||||||
position += len([]rune(match[ANCHOR])) - 1
|
position += len([]rune(match[ANCHOR])) - 1
|
||||||
|
@ -465,51 +433,113 @@ func (w *Writer) writeColorOverrides(match map[string]string, background string,
|
||||||
if match[FG] == Transparent && len(match[BG]) == 0 {
|
if match[FG] == Transparent && len(match[BG]) == 0 {
|
||||||
match[BG] = background
|
match[BG] = background
|
||||||
}
|
}
|
||||||
w.currentBackground, w.currentForeground = w.asAnsiColors(match[BG], match[FG])
|
|
||||||
|
bg, fg := w.asAnsiColors(match[BG], match[FG])
|
||||||
|
|
||||||
// ignore processing fully tranparent colors
|
// ignore processing fully tranparent colors
|
||||||
w.invisible = w.currentForeground.IsTransparent() && w.currentBackground.IsTransparent()
|
w.invisible = fg.IsTransparent() && bg.IsTransparent()
|
||||||
if w.invisible {
|
if w.invisible {
|
||||||
return position
|
return position
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure we have colors
|
// make sure we have colors
|
||||||
if w.currentForeground.IsEmpty() {
|
if fg.IsEmpty() {
|
||||||
w.currentForeground = w.foreground
|
fg = w.foreground
|
||||||
}
|
}
|
||||||
if w.currentBackground.IsEmpty() {
|
if bg.IsEmpty() {
|
||||||
w.currentBackground = w.background
|
bg = w.background
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.currentForeground.IsTransparent() && len(w.TerminalBackground) != 0 {
|
w.current.Add(bg, fg)
|
||||||
|
|
||||||
|
if w.current.Foreground().IsTransparent() && len(w.TerminalBackground) != 0 {
|
||||||
background := w.getAnsiFromColorString(w.TerminalBackground, false)
|
background := w.getAnsiFromColorString(w.TerminalBackground, false)
|
||||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, background))
|
w.writeEscapedAnsiString(fmt.Sprintf(colorise, background))
|
||||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.currentBackground.ToForeground()))
|
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Background().ToForeground()))
|
||||||
return position
|
return position
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.currentForeground.IsTransparent() && !w.currentBackground.IsTransparent() {
|
if w.current.Foreground().IsTransparent() && !w.current.Background().IsTransparent() {
|
||||||
w.transparent = true
|
w.transparent = true
|
||||||
w.writeEscapedAnsiString(fmt.Sprintf(transparent, w.currentBackground))
|
w.writeEscapedAnsiString(fmt.Sprintf(transparent, w.current.Background()))
|
||||||
return position
|
return position
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.currentBackground != w.background {
|
if w.current.Background() != w.background {
|
||||||
// end the colors in case we have a transparent background
|
// end the colors in case we have a transparent background
|
||||||
if w.currentBackground.IsTransparent() {
|
if w.current.Background().IsTransparent() {
|
||||||
w.writeEscapedAnsiString(backgroundEnd)
|
w.writeEscapedAnsiString(backgroundEnd)
|
||||||
} else {
|
} else {
|
||||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.currentBackground))
|
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Background()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.currentForeground != w.foreground {
|
if w.current.Foreground() != w.foreground {
|
||||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.currentForeground))
|
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Foreground()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return position
|
return position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *Writer) endColorOverride(position int) int {
|
||||||
|
// pop the last colors from the stack
|
||||||
|
defer w.current.Pop()
|
||||||
|
|
||||||
|
// make sure to reset the colors if needed
|
||||||
|
position += len([]rune(resetStyle.AnchorEnd)) - 1
|
||||||
|
|
||||||
|
// reset colors to previous when we have > 2 in stack
|
||||||
|
// - first is always the starting colors used in Write()
|
||||||
|
// - second is the first override
|
||||||
|
// as soon as we have more than 2, we can pop the last one
|
||||||
|
// and print the previous override as it wasn't ended yet
|
||||||
|
if w.current.Len() >= 3 {
|
||||||
|
fg := w.current.Foreground()
|
||||||
|
bg := w.current.Background()
|
||||||
|
|
||||||
|
w.current.Pop()
|
||||||
|
|
||||||
|
if w.current.Background() != bg {
|
||||||
|
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Background()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.current.Foreground() != fg {
|
||||||
|
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Foreground()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return position
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not reset when colors are identical
|
||||||
|
if w.current.Background() == w.background && w.current.Foreground() == w.foreground {
|
||||||
|
return position
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not restore colors at the end of the string, we print it anyways
|
||||||
|
if position == len(w.runes)-1 {
|
||||||
|
return position
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.transparent {
|
||||||
|
w.writeEscapedAnsiString(transparentEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.background.IsClear() {
|
||||||
|
w.writeEscapedAnsiString(backgroundStyle.End)
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.current.Background() != w.background && !w.background.IsClear() {
|
||||||
|
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.background))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (w.current.Foreground() != w.foreground || w.transparent) && !w.foreground.IsClear() {
|
||||||
|
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.foreground))
|
||||||
|
}
|
||||||
|
|
||||||
|
w.transparent = false
|
||||||
|
return position
|
||||||
|
}
|
||||||
|
|
||||||
func (w *Writer) asAnsiColors(background, foreground string) (Color, Color) {
|
func (w *Writer) asAnsiColors(background, foreground string) (Color, Color) {
|
||||||
if len(background) == 0 {
|
if len(background) == 0 {
|
||||||
background = Background
|
background = Background
|
||||||
|
|
|
@ -76,8 +76,9 @@ func (w *Writer) replaceHyperlink(text string) string {
|
||||||
return strings.Replace(text, results["ALL"], linkText, 1)
|
return strings.Replace(text, results["ALL"], linkText, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// we only care about the length of the text part
|
// we only care about the length of the actual text part
|
||||||
w.length += runewidth.StringWidth(linkText)
|
characters := w.trimAnsi(linkText)
|
||||||
|
w.length += runewidth.StringWidth(characters)
|
||||||
|
|
||||||
if w.Plain {
|
if w.Plain {
|
||||||
return linkText
|
return linkText
|
||||||
|
|
|
@ -195,6 +195,12 @@ func TestWriteANSIColors(t *testing.T) {
|
||||||
Expected: "\x1b[40m\x1b[30mtest\x1b[0m",
|
Expected: "\x1b[40m\x1b[30mtest\x1b[0m",
|
||||||
Colors: &Colors{Foreground: "black", Background: "white"},
|
Colors: &Colors{Foreground: "black", Background: "white"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Case: "Nested override",
|
||||||
|
Input: "hello, <red>world, <white>rabbit</> hello</>",
|
||||||
|
Expected: "\x1b[47m\x1b[30mhello, \x1b[31mworld, \x1b[37mrabbit\x1b[31m hello\x1b[0m",
|
||||||
|
Colors: &Colors{Foreground: "black", Background: "white"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
|
|
|
@ -19,6 +19,61 @@ type ColorString interface {
|
||||||
ToColor(colorString string, isBackground bool, trueColor bool) Color
|
ToColor(colorString string, isBackground bool, trueColor bool) Color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ColorSet struct {
|
||||||
|
Foreground Color
|
||||||
|
Background Color
|
||||||
|
}
|
||||||
|
|
||||||
|
type ColorHistory []*ColorSet
|
||||||
|
|
||||||
|
func (c *ColorHistory) Len() int {
|
||||||
|
return len(*c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ColorHistory) Add(background, foreground Color) {
|
||||||
|
colors := &ColorSet{
|
||||||
|
Foreground: foreground,
|
||||||
|
Background: background,
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Len() == 0 {
|
||||||
|
*c = append(*c, colors)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
last := (*c)[c.Len()-1]
|
||||||
|
// never add the same colors twice
|
||||||
|
if last.Foreground == colors.Foreground && last.Background == colors.Background {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
*c = append(*c, colors)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ColorHistory) Pop() {
|
||||||
|
if c.Len() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
*c = (*c)[:c.Len()-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ColorHistory) Background() Color {
|
||||||
|
if c.Len() == 0 {
|
||||||
|
return emptyColor
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*c)[c.Len()-1].Background
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ColorHistory) Foreground() Color {
|
||||||
|
if c.Len() == 0 {
|
||||||
|
return emptyColor
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*c)[c.Len()-1].Foreground
|
||||||
|
}
|
||||||
|
|
||||||
// Color is an ANSI color code ready to be printed to the console.
|
// Color is an ANSI color code ready to be printed to the console.
|
||||||
// Example: "38;2;255;255;255", "48;2;255;255;255", "31", "95".
|
// Example: "38;2;255;255;255", "48;2;255;255;255", "31", "95".
|
||||||
type Color string
|
type Color string
|
||||||
|
|
Loading…
Reference in a new issue