refactor: color logic performance

This commit is contained in:
Jan De Dobbeleer 2021-09-25 10:52:12 +02:00 committed by Jan De Dobbeleer
parent fa782250dd
commit b3f537cb76
4 changed files with 45 additions and 42 deletions

View file

@ -67,6 +67,9 @@ const (
// 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`.
func (a *AnsiColor) getAnsiFromColorString(colorString string, isBackground bool) string { func (a *AnsiColor) getAnsiFromColorString(colorString string, isBackground bool) string {
if colorString == Transparent || len(colorString) == 0 {
return colorString
}
colorFromName, err := getColorFromName(colorString, isBackground) colorFromName, err := getColorFromName(colorString, isBackground)
if err == nil { if err == nil {
return colorFromName return colorFromName
@ -83,27 +86,26 @@ func (a *AnsiColor) writeColoredText(background, foreground, text string) {
if text == "" { if text == "" {
return return
} }
if foreground == Transparent && background != "" && a.terminalBackground != "" { // default to white fg if empty, empty backgrond is supported
bgAnsiColor := a.getAnsiFromColorString(background, true) if len(foreground) == 0 {
foreground = a.getAnsiFromColorString("white", false)
}
if foreground == Transparent && len(background) != 0 && len(a.terminalBackground) != 0 {
fgAnsiColor := a.getAnsiFromColorString(a.terminalBackground, false) fgAnsiColor := a.getAnsiFromColorString(a.terminalBackground, false)
coloredText := fmt.Sprintf(a.ansi.colorFull, bgAnsiColor, fgAnsiColor, text) coloredText := fmt.Sprintf(a.ansi.colorFull, background, fgAnsiColor, text)
a.builder.WriteString(coloredText) a.builder.WriteString(coloredText)
return return
} }
if foreground == Transparent && background != "" { if foreground == Transparent && len(background) != 0 {
ansiColor := a.getAnsiFromColorString(background, false) coloredText := fmt.Sprintf(a.ansi.colorTransparent, background, text)
coloredText := fmt.Sprintf(a.ansi.colorTransparent, ansiColor, text)
a.builder.WriteString(coloredText) a.builder.WriteString(coloredText)
return return
} else if background == "" || background == Transparent { } else if len(background) == 0 || background == Transparent {
ansiColor := a.getAnsiFromColorString(foreground, false) coloredText := fmt.Sprintf(a.ansi.colorSingle, foreground, text)
coloredText := fmt.Sprintf(a.ansi.colorSingle, ansiColor, text)
a.builder.WriteString(coloredText) a.builder.WriteString(coloredText)
return return
} }
bgAnsiColor := a.getAnsiFromColorString(background, true) coloredText := fmt.Sprintf(a.ansi.colorFull, background, foreground, text)
fgAnsiColor := a.getAnsiFromColorString(foreground, false)
coloredText := fmt.Sprintf(a.ansi.colorFull, bgAnsiColor, fgAnsiColor, text)
a.builder.WriteString(coloredText) a.builder.WriteString(coloredText)
} }
@ -116,6 +118,16 @@ func (a *AnsiColor) write(background, foreground, text string) {
if len(text) == 0 { if len(text) == 0 {
return return
} }
getAnsiColors := func(background, foreground string) (string, string) {
inverted := foreground == Transparent && len(background) != 0
background = a.getAnsiFromColorString(background, !inverted)
foreground = a.getAnsiFromColorString(foreground, false)
return background, foreground
}
bgAnsi, fgAnsi := getAnsiColors(background, foreground)
text = a.ansi.escapeText(text) text = a.ansi.escapeText(text)
text = a.ansi.formatText(text) text = a.ansi.formatText(text)
text = a.ansi.generateHyperlink(text) text = a.ansi.generateHyperlink(text)
@ -123,29 +135,27 @@ func (a *AnsiColor) write(background, foreground, text string) {
// first we match for any potentially valid colors enclosed in <> // first we match for any potentially valid colors enclosed in <>
match := findAllNamedRegexMatch(`<(?P<foreground>[^,>]+)?,?(?P<background>[^>]+)?>(?P<content>[^<]*)<\/>`, text) match := findAllNamedRegexMatch(`<(?P<foreground>[^,>]+)?,?(?P<background>[^>]+)?>(?P<content>[^<]*)<\/>`, text)
for i := range match { for i := range match {
extractedForegroundColor := match[i]["foreground"] fg := match[i]["foreground"]
extractedBackgroundColor := match[i]["background"] bg := match[i]["background"]
if col := a.getAnsiFromColorString(extractedForegroundColor, false); col == "" && extractedForegroundColor != Transparent && len(extractedBackgroundColor) == 0 { if fg == Transparent && len(bg) == 0 {
continue // we skip invalid colors bg = background
} }
if col := a.getAnsiFromColorString(extractedBackgroundColor, false); col == "" && extractedBackgroundColor != Transparent && len(extractedForegroundColor) == 0 { bg, fg = getAnsiColors(bg, fg)
continue // we skip invalid colors // set colors if they are empty
if len(bg) == 0 {
bg = bgAnsi
} }
// reuse function colors if only one was specified if len(fg) == 0 {
if len(extractedBackgroundColor) == 0 { fg = fgAnsi
extractedBackgroundColor = background
}
if len(extractedForegroundColor) == 0 {
extractedForegroundColor = foreground
} }
escapedTextSegment := match[i]["text"] escapedTextSegment := match[i]["text"]
innerText := match[i]["content"] innerText := match[i]["content"]
textBeforeColorOverride := strings.Split(text, escapedTextSegment)[0] textBeforeColorOverride := strings.Split(text, escapedTextSegment)[0]
text = a.writeAndRemoveText(background, foreground, textBeforeColorOverride, textBeforeColorOverride, text) text = a.writeAndRemoveText(bgAnsi, fgAnsi, textBeforeColorOverride, textBeforeColorOverride, text)
text = a.writeAndRemoveText(extractedBackgroundColor, extractedForegroundColor, innerText, escapedTextSegment, text) text = a.writeAndRemoveText(bg, fg, innerText, escapedTextSegment, text)
} }
// color the remaining part of text with background and foreground // color the remaining part of text with background and foreground
a.writeColoredText(background, foreground, text) a.writeColoredText(bgAnsi, fgAnsi, text)
} }
func (a *AnsiColor) string() string { func (a *AnsiColor) string() string {

View file

@ -133,7 +133,7 @@ func TestWriteColorInvalid(t *testing.T) {
} }
text := "This is white, <invalid>this is orange</>, white again" text := "This is white, <invalid>this is orange</>, white again"
renderer.write("#193549", "invalid", text) renderer.write("#193549", "invalid", text)
assert.Contains(t, renderer.string(), "<invalid>") assert.NotContains(t, renderer.string(), "<invalid>")
} }
func TestGetAnsiFromColorStringBg(t *testing.T) { func TestGetAnsiFromColorStringBg(t *testing.T) {

View file

@ -82,17 +82,19 @@ func (b *Block) setStringValues() {
} }
func (b *Block) foreground() string { func (b *Block) foreground() string {
if b.previousActiveSegment != nil && b.activeSegment.foreground() == Inherit { color := b.activeSegment.foreground()
if b.previousActiveSegment != nil && color == Inherit {
return b.previousActiveSegment.foreground() return b.previousActiveSegment.foreground()
} }
return b.activeSegment.foreground() return color
} }
func (b *Block) background() string { func (b *Block) background() string {
if b.previousActiveSegment != nil && b.activeSegment.background() == Inherit { color := b.activeSegment.background()
if b.previousActiveSegment != nil && color == Inherit {
return b.previousActiveSegment.background() return b.previousActiveSegment.background()
} }
return b.activeSegment.background() return color
} }
func (b *Block) renderSegments() string { func (b *Block) renderSegments() string {

View file

@ -1,5 +1,6 @@
{ {
"$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json", "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
"final_space": true,
"blocks": [ "blocks": [
{ {
"type": "prompt", "type": "prompt",
@ -176,16 +177,6 @@
"text": "\u276F", "text": "\u276F",
"postfix": "" "postfix": ""
} }
},
{
"type":"text",
"style": "plain",
"foreground": "transparent",
"properties": {
"prefix": "",
"text": " ",
"postfix": ""
}
} }
] ]
} }