mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-12-26 19:39:39 -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
|
||||
length int
|
||||
|
||||
foreground Color
|
||||
background Color
|
||||
currentForeground Color
|
||||
currentBackground Color
|
||||
runes []rune
|
||||
transparent bool
|
||||
invisible bool
|
||||
foreground Color
|
||||
background Color
|
||||
current ColorHistory
|
||||
runes []rune
|
||||
transparent bool
|
||||
invisible bool
|
||||
|
||||
shell string
|
||||
format string
|
||||
|
@ -303,7 +302,8 @@ func (w *Writer) Write(background, foreground, text string) {
|
|||
colorOverride = false
|
||||
}
|
||||
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()
|
||||
|
@ -343,8 +343,7 @@ func (w *Writer) Write(background, foreground, text string) {
|
|||
w.writeEscapedAnsiString(resetStyle.End)
|
||||
|
||||
// reset current
|
||||
w.currentBackground = ""
|
||||
w.currentForeground = ""
|
||||
w.current.Pop()
|
||||
}
|
||||
|
||||
func (w *Writer) String() (string, int) {
|
||||
|
@ -378,11 +377,11 @@ func (w *Writer) writeSegmentColors() {
|
|||
// use correct starting colors
|
||||
bg := w.background
|
||||
fg := w.foreground
|
||||
if !w.currentBackground.IsEmpty() {
|
||||
bg = w.currentBackground
|
||||
if !w.current.Background().IsEmpty() {
|
||||
bg = w.current.Background()
|
||||
}
|
||||
if !w.currentForeground.IsEmpty() {
|
||||
fg = w.currentForeground
|
||||
if !w.current.Foreground().IsEmpty() {
|
||||
fg = w.current.Foreground()
|
||||
}
|
||||
|
||||
// ignore processing fully tranparent colors
|
||||
|
@ -408,45 +407,14 @@ func (w *Writer) writeSegmentColors() {
|
|||
}
|
||||
|
||||
// set current colors
|
||||
w.currentBackground = bg
|
||||
w.currentForeground = fg
|
||||
w.current.Add(bg, fg)
|
||||
}
|
||||
|
||||
func (w *Writer) writeColorOverrides(match map[string]string, background string, i int) int {
|
||||
position := i
|
||||
// check color reset first
|
||||
if match[ANCHOR] == resetStyle.AnchorEnd {
|
||||
// make sure to reset the colors if needed
|
||||
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
|
||||
return w.endColorOverride(position)
|
||||
}
|
||||
|
||||
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 {
|
||||
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
|
||||
w.invisible = w.currentForeground.IsTransparent() && w.currentBackground.IsTransparent()
|
||||
w.invisible = fg.IsTransparent() && bg.IsTransparent()
|
||||
if w.invisible {
|
||||
return position
|
||||
}
|
||||
|
||||
// make sure we have colors
|
||||
if w.currentForeground.IsEmpty() {
|
||||
w.currentForeground = w.foreground
|
||||
if fg.IsEmpty() {
|
||||
fg = w.foreground
|
||||
}
|
||||
if w.currentBackground.IsEmpty() {
|
||||
w.currentBackground = w.background
|
||||
if bg.IsEmpty() {
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
if w.currentForeground.IsTransparent() && !w.currentBackground.IsTransparent() {
|
||||
if w.current.Foreground().IsTransparent() && !w.current.Background().IsTransparent() {
|
||||
w.transparent = true
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(transparent, w.currentBackground))
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(transparent, w.current.Background()))
|
||||
return position
|
||||
}
|
||||
|
||||
if w.currentBackground != w.background {
|
||||
if w.current.Background() != w.background {
|
||||
// end the colors in case we have a transparent background
|
||||
if w.currentBackground.IsTransparent() {
|
||||
if w.current.Background().IsTransparent() {
|
||||
w.writeEscapedAnsiString(backgroundEnd)
|
||||
} else {
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.currentBackground))
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Background()))
|
||||
}
|
||||
}
|
||||
|
||||
if w.currentForeground != w.foreground {
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.currentForeground))
|
||||
if w.current.Foreground() != w.foreground {
|
||||
w.writeEscapedAnsiString(fmt.Sprintf(colorise, w.current.Foreground()))
|
||||
}
|
||||
|
||||
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) {
|
||||
if len(background) == 0 {
|
||||
background = Background
|
||||
|
|
|
@ -76,8 +76,9 @@ func (w *Writer) replaceHyperlink(text string) string {
|
|||
return strings.Replace(text, results["ALL"], linkText, 1)
|
||||
}
|
||||
|
||||
// we only care about the length of the text part
|
||||
w.length += runewidth.StringWidth(linkText)
|
||||
// we only care about the length of the actual text part
|
||||
characters := w.trimAnsi(linkText)
|
||||
w.length += runewidth.StringWidth(characters)
|
||||
|
||||
if w.Plain {
|
||||
return linkText
|
||||
|
|
|
@ -195,6 +195,12 @@ func TestWriteANSIColors(t *testing.T) {
|
|||
Expected: "\x1b[40m\x1b[30mtest\x1b[0m",
|
||||
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 {
|
||||
|
|
|
@ -19,6 +19,61 @@ type ColorString interface {
|
|||
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.
|
||||
// Example: "38;2;255;255;255", "48;2;255;255;255", "31", "95".
|
||||
type Color string
|
||||
|
|
Loading…
Reference in a new issue