oh-my-posh/src/writer_ansi.go

221 lines
6.4 KiB
Go
Raw Normal View History

2020-12-17 23:59:45 -08:00
package main
import (
"errors"
"fmt"
"strings"
"github.com/gookit/color"
)
var (
// Map for color names and their respective foreground [0] or background [1] color codes
2021-05-21 11:01:08 -07:00
colorMap = map[string][2]string{
2020-12-17 23:59:45 -08:00
"black": {"30", "40"},
"red": {"31", "41"},
"green": {"32", "42"},
"yellow": {"33", "43"},
"blue": {"34", "44"},
"magenta": {"35", "45"},
"cyan": {"36", "46"},
"white": {"37", "47"},
"default": {"39", "49"},
"darkGray": {"90", "100"},
"lightRed": {"91", "101"},
"lightGreen": {"92", "102"},
"lightYellow": {"93", "103"},
"lightBlue": {"94", "104"},
"lightMagenta": {"95", "105"},
"lightCyan": {"96", "106"},
"lightWhite": {"97", "107"},
}
)
2021-11-02 06:39:51 -07:00
const (
colorRegex = `<(?P<foreground>[^,>]+)?,?(?P<background>[^>]+)?>(?P<content>[^<]*)<\/>`
)
2020-12-17 23:59:45 -08:00
// Returns the color code for a given color name
func getColorFromName(colorName string, isBackground bool) (string, error) {
colorMapOffset := 0
if isBackground {
colorMapOffset = 1
}
if colorCodes, found := colorMap[colorName]; found {
return colorCodes[colorMapOffset], nil
}
return "", errors.New("color name does not exist")
}
2021-11-02 06:39:51 -07:00
type promptWriter interface {
2021-04-20 12:30:46 -07:00
write(background, foreground, text string)
string() string
reset()
setColors(background, foreground string)
2021-09-25 11:49:32 -07:00
setParentColors(background, foreground string)
clearParentColors()
2021-04-20 12:30:46 -07:00
}
2021-11-02 06:39:51 -07:00
// AnsiWriter writes colorized strings
type AnsiWriter struct {
builder strings.Builder
2021-04-20 12:30:46 -07:00
ansi *ansiUtils
terminalBackground string
Colors *Color
ParentColors *Color
2021-09-25 11:49:32 -07:00
}
type Color struct {
Background string
Foreground string
2020-12-17 23:59:45 -08:00
}
const (
// Transparent implies a transparent color
Transparent = "transparent"
// ParentBackground takes the previous segment's background color
ParentBackground = "parentBackground"
// ParentForeground takes the previous segment's color
ParentForeground = "parentForeground"
// Background takes the current segment's background color
Background = "background"
// Foreground takes the current segment's foreground color
Foreground = "foreground"
2020-12-17 23:59:45 -08:00
)
2021-11-02 06:39:51 -07:00
func (a *AnsiWriter) setColors(background, foreground string) {
a.Colors = &Color{
Background: background,
Foreground: foreground,
}
}
2021-11-02 06:39:51 -07:00
func (a *AnsiWriter) setParentColors(background, foreground string) {
a.ParentColors = &Color{
2021-09-25 11:49:32 -07:00
Background: background,
Foreground: foreground,
}
}
2021-11-02 06:39:51 -07:00
func (a *AnsiWriter) clearParentColors() {
a.ParentColors = nil
}
2020-12-17 23:59:45 -08:00
// Gets the ANSI color code for a given color string.
// 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`.
2021-11-02 06:39:51 -07:00
func (a *AnsiWriter) getAnsiFromColorString(colorString string, isBackground bool) string {
2021-09-25 01:52:12 -07:00
if colorString == Transparent || len(colorString) == 0 {
return colorString
}
2020-12-17 23:59:45 -08:00
colorFromName, err := getColorFromName(colorString, isBackground)
if err == nil {
return colorFromName
}
style := color.HEX(colorString, isBackground)
2021-06-08 11:16:55 -07:00
if style.IsEmpty() {
return ""
}
return style.String()
2020-12-17 23:59:45 -08:00
}
2021-11-02 06:39:51 -07:00
func (a *AnsiWriter) writeColoredText(background, foreground, text string) {
// Avoid emitting empty strings with color codes
2021-09-28 11:19:24 -07:00
if text == "" || (foreground == Transparent && background == Transparent) {
return
}
2021-09-25 01:52:12 -07:00
// default to white fg if empty, empty backgrond is supported
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)
2021-09-25 01:52:12 -07:00
coloredText := fmt.Sprintf(a.ansi.colorFull, background, fgAnsiColor, text)
a.builder.WriteString(coloredText)
return
}
2021-09-25 01:52:12 -07:00
if foreground == Transparent && len(background) != 0 {
coloredText := fmt.Sprintf(a.ansi.colorTransparent, background, text)
a.builder.WriteString(coloredText)
return
2021-09-25 01:52:12 -07:00
} else if len(background) == 0 || background == Transparent {
coloredText := fmt.Sprintf(a.ansi.colorSingle, foreground, text)
a.builder.WriteString(coloredText)
return
2020-12-17 23:59:45 -08:00
}
2021-09-25 01:52:12 -07:00
coloredText := fmt.Sprintf(a.ansi.colorFull, background, foreground, text)
a.builder.WriteString(coloredText)
2020-12-17 23:59:45 -08:00
}
2021-11-02 06:39:51 -07:00
func (a *AnsiWriter) writeAndRemoveText(background, foreground, text, textToRemove, parentText string) string {
2020-12-17 23:59:45 -08:00
a.writeColoredText(background, foreground, text)
return strings.Replace(parentText, textToRemove, "", 1)
}
2021-11-02 06:39:51 -07:00
func (a *AnsiWriter) write(background, foreground, text string) {
2021-06-08 10:29:09 -07:00
if len(text) == 0 {
return
}
2021-09-25 01:52:12 -07:00
getAnsiColors := func(background, foreground string) (string, string) {
getColorString := func(color string) string {
if color == Background {
color = a.Colors.Background
} else if color == Foreground {
color = a.Colors.Foreground
} else if color == ParentBackground && a.ParentColors != nil {
color = a.ParentColors.Background
} else if color == ParentForeground && a.ParentColors != nil {
color = a.ParentColors.Foreground
} else if (color == ParentForeground || color == ParentBackground) && a.ParentColors == nil {
color = Transparent
}
return color
}
background = getColorString(background)
foreground = getColorString(foreground)
2021-09-25 01:52:12 -07:00
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)
2021-04-20 12:30:46 -07:00
text = a.ansi.formatText(text)
text = a.ansi.generateHyperlink(text)
2020-12-17 23:59:45 -08:00
// first we match for any potentially valid colors enclosed in <>
2021-11-02 06:39:51 -07:00
match := findAllNamedRegexMatch(colorRegex, text)
2020-12-17 23:59:45 -08:00
for i := range match {
2021-09-25 01:52:12 -07:00
fg := match[i]["foreground"]
bg := match[i]["background"]
if fg == Transparent && len(bg) == 0 {
bg = background
2020-12-17 23:59:45 -08:00
}
2021-09-25 01:52:12 -07:00
bg, fg = getAnsiColors(bg, fg)
// set colors if they are empty
if len(bg) == 0 {
bg = bgAnsi
2020-12-17 23:59:45 -08:00
}
2021-09-25 01:52:12 -07:00
if len(fg) == 0 {
fg = fgAnsi
2020-12-17 23:59:45 -08:00
}
escapedTextSegment := match[i]["text"]
innerText := match[i]["content"]
textBeforeColorOverride := strings.Split(text, escapedTextSegment)[0]
2021-09-25 01:52:12 -07:00
text = a.writeAndRemoveText(bgAnsi, fgAnsi, textBeforeColorOverride, textBeforeColorOverride, text)
text = a.writeAndRemoveText(bg, fg, innerText, escapedTextSegment, text)
2020-12-17 23:59:45 -08:00
}
// color the remaining part of text with background and foreground
2021-09-25 01:52:12 -07:00
a.writeColoredText(bgAnsi, fgAnsi, text)
2020-12-17 23:59:45 -08:00
}
2021-11-02 06:39:51 -07:00
func (a *AnsiWriter) string() string {
return a.builder.String()
2020-12-17 23:59:45 -08:00
}
2021-11-02 06:39:51 -07:00
func (a *AnsiWriter) reset() {
a.builder.Reset()
2020-12-17 23:59:45 -08:00
}