mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2025-02-02 05:41:10 -08:00
refactor: separate ansi formats
This commit is contained in:
parent
e9c65948c1
commit
59282c088d
|
@ -9,12 +9,6 @@ import (
|
||||||
"github.com/gookit/color"
|
"github.com/gookit/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
type colorFormats struct {
|
|
||||||
single string
|
|
||||||
full string
|
|
||||||
transparent string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Map for color names and their respective foreground [0] or background [1] color codes
|
// Map for color names and their respective foreground [0] or background [1] color codes
|
||||||
colorMap map[string][2]string = map[string][2]string{
|
colorMap map[string][2]string = map[string][2]string{
|
||||||
|
@ -53,7 +47,7 @@ func getColorFromName(colorName string, isBackground bool) (string, error) {
|
||||||
// AnsiColor writes colorized strings
|
// AnsiColor writes colorized strings
|
||||||
type AnsiColor struct {
|
type AnsiColor struct {
|
||||||
buffer *bytes.Buffer
|
buffer *bytes.Buffer
|
||||||
formats *colorFormats
|
formats *ansiFormats
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -61,24 +55,6 @@ const (
|
||||||
Transparent = "transparent"
|
Transparent = "transparent"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *AnsiColor) init(shell string) {
|
|
||||||
a.formats = &colorFormats{}
|
|
||||||
switch shell {
|
|
||||||
case zsh:
|
|
||||||
a.formats.single = "%%{\x1b[%sm%%}%s%%{\x1b[0m%%}"
|
|
||||||
a.formats.full = "%%{\x1b[%sm\x1b[%sm%%}%s%%{\x1b[0m%%}"
|
|
||||||
a.formats.transparent = "%%{\x1b[%s;49m\x1b[7m%%}%s%%{\x1b[m\x1b[0m%%}"
|
|
||||||
case bash:
|
|
||||||
a.formats.single = "\\[\x1b[%sm\\]%s\\[\x1b[0m\\]"
|
|
||||||
a.formats.full = "\\[\x1b[%sm\x1b[%sm\\]%s\\[\x1b[0m\\]"
|
|
||||||
a.formats.transparent = "\\[\x1b[%s;49m\x1b[7m\\]%s\\[\x1b[m\x1b[0m\\]"
|
|
||||||
default:
|
|
||||||
a.formats.single = "\x1b[%sm%s\x1b[0m"
|
|
||||||
a.formats.full = "\x1b[%sm\x1b[%sm%s\x1b[0m"
|
|
||||||
a.formats.transparent = "\x1b[%s;49m\x1b[7m%s\x1b[m\x1b[0m"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets the ANSI color code for a given color string.
|
// Gets the ANSI color code for a given color string.
|
||||||
// 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`.
|
||||||
|
@ -99,14 +75,14 @@ func (a *AnsiColor) writeColoredText(background, foreground, text string) {
|
||||||
var coloredText string
|
var coloredText string
|
||||||
if foreground == Transparent && background != "" {
|
if foreground == Transparent && background != "" {
|
||||||
ansiColor := a.getAnsiFromColorString(background, false)
|
ansiColor := a.getAnsiFromColorString(background, false)
|
||||||
coloredText = fmt.Sprintf(a.formats.transparent, ansiColor, text)
|
coloredText = fmt.Sprintf(a.formats.colorTransparent, ansiColor, text)
|
||||||
} else if background == "" || background == Transparent {
|
} else if background == "" || background == Transparent {
|
||||||
ansiColor := a.getAnsiFromColorString(foreground, false)
|
ansiColor := a.getAnsiFromColorString(foreground, false)
|
||||||
coloredText = fmt.Sprintf(a.formats.single, ansiColor, text)
|
coloredText = fmt.Sprintf(a.formats.colorSingle, ansiColor, text)
|
||||||
} else if foreground != "" && background != "" {
|
} else if foreground != "" && background != "" {
|
||||||
bgAnsiColor := a.getAnsiFromColorString(background, true)
|
bgAnsiColor := a.getAnsiFromColorString(background, true)
|
||||||
fgAnsiColor := a.getAnsiFromColorString(foreground, false)
|
fgAnsiColor := a.getAnsiFromColorString(foreground, false)
|
||||||
coloredText = fmt.Sprintf(a.formats.full, bgAnsiColor, fgAnsiColor, text)
|
coloredText = fmt.Sprintf(a.formats.colorFull, bgAnsiColor, fgAnsiColor, text)
|
||||||
}
|
}
|
||||||
a.buffer.WriteString(coloredText)
|
a.buffer.WriteString(coloredText)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,50 +13,60 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWriteAndRemoveText(t *testing.T) {
|
func TestWriteAndRemoveText(t *testing.T) {
|
||||||
|
formats := &ansiFormats{}
|
||||||
|
formats.init("pwsh")
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
|
formats: formats,
|
||||||
}
|
}
|
||||||
renderer.init("pwsh")
|
|
||||||
text := renderer.writeAndRemoveText("#193549", "#fff", "This is white, ", "This is white, ", inputText)
|
text := renderer.writeAndRemoveText("#193549", "#fff", "This is white, ", "This is white, ", inputText)
|
||||||
assert.Equal(t, "<#ff5733>this is orange</>, white again", text)
|
assert.Equal(t, "<#ff5733>this is orange</>, white again", text)
|
||||||
assert.NotContains(t, renderer.string(), "<#ff5733>")
|
assert.NotContains(t, renderer.string(), "<#ff5733>")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteAndRemoveTextColored(t *testing.T) {
|
func TestWriteAndRemoveTextColored(t *testing.T) {
|
||||||
|
formats := &ansiFormats{}
|
||||||
|
formats.init("pwsh")
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
|
formats: formats,
|
||||||
}
|
}
|
||||||
renderer.init("pwsh")
|
|
||||||
text := renderer.writeAndRemoveText("#193549", "#ff5733", "this is orange", "<#ff5733>this is orange</>", inputText)
|
text := renderer.writeAndRemoveText("#193549", "#ff5733", "this is orange", "<#ff5733>this is orange</>", inputText)
|
||||||
assert.Equal(t, "This is white, , white again", text)
|
assert.Equal(t, "This is white, , white again", text)
|
||||||
assert.NotContains(t, renderer.string(), "<#ff5733>")
|
assert.NotContains(t, renderer.string(), "<#ff5733>")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteColorOverride(t *testing.T) {
|
func TestWriteColorOverride(t *testing.T) {
|
||||||
|
formats := &ansiFormats{}
|
||||||
|
formats.init("pwsh")
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
|
formats: formats,
|
||||||
}
|
}
|
||||||
renderer.init("pwsh")
|
|
||||||
renderer.write("#193549", "#ff5733", inputText)
|
renderer.write("#193549", "#ff5733", inputText)
|
||||||
assert.NotContains(t, renderer.string(), "<#ff5733>")
|
assert.NotContains(t, renderer.string(), "<#ff5733>")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteColorOverrideBackground(t *testing.T) {
|
func TestWriteColorOverrideBackground(t *testing.T) {
|
||||||
|
formats := &ansiFormats{}
|
||||||
|
formats.init("pwsh")
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
|
formats: formats,
|
||||||
}
|
}
|
||||||
text := "This is white, <,#000000>this is black</>, white again"
|
text := "This is white, <,#000000>this is black</>, white again"
|
||||||
renderer.init("pwsh")
|
|
||||||
renderer.write("#193549", "#ff5733", text)
|
renderer.write("#193549", "#ff5733", text)
|
||||||
assert.NotContains(t, renderer.string(), "000000")
|
assert.NotContains(t, renderer.string(), "000000")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteColorOverrideBackground16(t *testing.T) {
|
func TestWriteColorOverrideBackground16(t *testing.T) {
|
||||||
|
formats := &ansiFormats{}
|
||||||
|
formats.init("pwsh")
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
|
formats: formats,
|
||||||
}
|
}
|
||||||
text := "This is default <,white> this background is changed</> default again"
|
text := "This is default <,white> this background is changed</> default again"
|
||||||
renderer.init("pwsh")
|
|
||||||
renderer.write("#193549", "#ff5733", text)
|
renderer.write("#193549", "#ff5733", text)
|
||||||
assert.NotContains(t, renderer.string(), "white")
|
assert.NotContains(t, renderer.string(), "white")
|
||||||
assert.NotContains(t, renderer.string(), "</>")
|
assert.NotContains(t, renderer.string(), "</>")
|
||||||
|
@ -64,159 +74,116 @@ func TestWriteColorOverrideBackground16(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteColorOverrideBoth(t *testing.T) {
|
func TestWriteColorOverrideBoth(t *testing.T) {
|
||||||
|
formats := &ansiFormats{}
|
||||||
|
formats.init("pwsh")
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
|
formats: formats,
|
||||||
}
|
}
|
||||||
text := "This is white, <#000000,#ffffff>this is black</>, white again"
|
text := "This is white, <#000000,#ffffff>this is black</>, white again"
|
||||||
renderer.init("pwsh")
|
|
||||||
renderer.write("#193549", "#ff5733", text)
|
renderer.write("#193549", "#ff5733", text)
|
||||||
assert.NotContains(t, renderer.string(), "ffffff")
|
assert.NotContains(t, renderer.string(), "ffffff")
|
||||||
assert.NotContains(t, renderer.string(), "000000")
|
assert.NotContains(t, renderer.string(), "000000")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteColorOverrideBoth16(t *testing.T) {
|
func TestWriteColorOverrideBoth16(t *testing.T) {
|
||||||
|
formats := &ansiFormats{}
|
||||||
|
formats.init("pwsh")
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
|
formats: formats,
|
||||||
}
|
}
|
||||||
text := "This is white, <black,white>this is black</>, white again"
|
text := "This is white, <black,white>this is black</>, white again"
|
||||||
renderer.init("pwsh")
|
|
||||||
renderer.write("#193549", "#ff5733", text)
|
renderer.write("#193549", "#ff5733", text)
|
||||||
assert.NotContains(t, renderer.string(), "<black,white>")
|
assert.NotContains(t, renderer.string(), "<black,white>")
|
||||||
assert.NotContains(t, renderer.string(), "</>")
|
assert.NotContains(t, renderer.string(), "</>")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteColorOverrideDouble(t *testing.T) {
|
func TestWriteColorOverrideDouble(t *testing.T) {
|
||||||
|
formats := &ansiFormats{}
|
||||||
|
formats.init("pwsh")
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
|
formats: formats,
|
||||||
}
|
}
|
||||||
text := "<#ffffff>jan</>@<#ffffff>Jans-MBP</>"
|
text := "<#ffffff>jan</>@<#ffffff>Jans-MBP</>"
|
||||||
renderer.init("pwsh")
|
|
||||||
renderer.write("#193549", "#ff5733", text)
|
renderer.write("#193549", "#ff5733", text)
|
||||||
assert.NotContains(t, renderer.string(), "<#ffffff>")
|
assert.NotContains(t, renderer.string(), "<#ffffff>")
|
||||||
assert.NotContains(t, renderer.string(), "</>")
|
assert.NotContains(t, renderer.string(), "</>")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteColorTransparent(t *testing.T) {
|
func TestWriteColorTransparent(t *testing.T) {
|
||||||
|
formats := &ansiFormats{}
|
||||||
|
formats.init("pwsh")
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
|
formats: formats,
|
||||||
}
|
}
|
||||||
renderer.init("pwsh")
|
|
||||||
text := "This is white"
|
text := "This is white"
|
||||||
renderer.writeColoredText("#193549", Transparent, text)
|
renderer.writeColoredText("#193549", Transparent, text)
|
||||||
t.Log(renderer.string())
|
t.Log(renderer.string())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteColorName(t *testing.T) {
|
func TestWriteColorName(t *testing.T) {
|
||||||
// given
|
formats := &ansiFormats{}
|
||||||
|
formats.init("pwsh")
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
|
formats: formats,
|
||||||
}
|
}
|
||||||
renderer.init("pwsh")
|
|
||||||
text := "This is white, <red>this is red</>, white again"
|
text := "This is white, <red>this is red</>, white again"
|
||||||
|
|
||||||
// when
|
|
||||||
renderer.write("#193549", "red", text)
|
renderer.write("#193549", "red", text)
|
||||||
|
|
||||||
// then
|
|
||||||
assert.NotContains(t, renderer.string(), "<red>")
|
assert.NotContains(t, renderer.string(), "<red>")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteColorInvalid(t *testing.T) {
|
func TestWriteColorInvalid(t *testing.T) {
|
||||||
// given
|
formats := &ansiFormats{}
|
||||||
|
formats.init("pwsh")
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
|
formats: formats,
|
||||||
}
|
}
|
||||||
renderer.init("pwsh")
|
|
||||||
text := "This is white, <invalid>this is orange</>, white again"
|
text := "This is white, <invalid>this is orange</>, white again"
|
||||||
|
|
||||||
// when
|
|
||||||
renderer.write("#193549", "invalid", text)
|
renderer.write("#193549", "invalid", text)
|
||||||
|
|
||||||
// then
|
|
||||||
assert.Contains(t, renderer.string(), "<invalid>")
|
assert.Contains(t, renderer.string(), "<invalid>")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLenWithoutANSI(t *testing.T) {
|
|
||||||
text := "\x1b[44mhello\x1b[0m"
|
|
||||||
renderer := &AnsiColor{
|
|
||||||
buffer: new(bytes.Buffer),
|
|
||||||
}
|
|
||||||
renderer.init("pwsh")
|
|
||||||
strippedLength := lenWithoutANSI(text, "zsh")
|
|
||||||
assert.Equal(t, 5, strippedLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLenWithoutANSIZsh(t *testing.T) {
|
|
||||||
text := "%{\x1b[44m%}hello%{\x1b[0m%}"
|
|
||||||
renderer := &AnsiColor{
|
|
||||||
buffer: new(bytes.Buffer),
|
|
||||||
}
|
|
||||||
renderer.init("zsh")
|
|
||||||
strippedLength := lenWithoutANSI(text, "zsh")
|
|
||||||
assert.Equal(t, 5, strippedLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetAnsiFromColorStringBg(t *testing.T) {
|
func TestGetAnsiFromColorStringBg(t *testing.T) {
|
||||||
// given
|
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
}
|
}
|
||||||
|
|
||||||
// when
|
|
||||||
colorCode := renderer.getAnsiFromColorString("blue", true)
|
colorCode := renderer.getAnsiFromColorString("blue", true)
|
||||||
|
|
||||||
// then
|
|
||||||
assert.Equal(t, color.BgBlue.Code(), colorCode)
|
assert.Equal(t, color.BgBlue.Code(), colorCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetAnsiFromColorStringFg(t *testing.T) {
|
func TestGetAnsiFromColorStringFg(t *testing.T) {
|
||||||
// given
|
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
}
|
}
|
||||||
|
|
||||||
// when
|
|
||||||
colorCode := renderer.getAnsiFromColorString("red", false)
|
colorCode := renderer.getAnsiFromColorString("red", false)
|
||||||
|
|
||||||
// then
|
|
||||||
assert.Equal(t, color.FgRed.Code(), colorCode)
|
assert.Equal(t, color.FgRed.Code(), colorCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetAnsiFromColorStringHex(t *testing.T) {
|
func TestGetAnsiFromColorStringHex(t *testing.T) {
|
||||||
// given
|
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
}
|
}
|
||||||
|
|
||||||
// when
|
|
||||||
colorCode := renderer.getAnsiFromColorString("#AABBCC", false)
|
colorCode := renderer.getAnsiFromColorString("#AABBCC", false)
|
||||||
|
|
||||||
// then
|
|
||||||
assert.Equal(t, color.HEX("#AABBCC").Code(), colorCode)
|
assert.Equal(t, color.HEX("#AABBCC").Code(), colorCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetAnsiFromColorStringInvalidFg(t *testing.T) {
|
func TestGetAnsiFromColorStringInvalidFg(t *testing.T) {
|
||||||
// given
|
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
}
|
}
|
||||||
|
|
||||||
// when
|
|
||||||
colorCode := renderer.getAnsiFromColorString("invalid", false)
|
colorCode := renderer.getAnsiFromColorString("invalid", false)
|
||||||
|
|
||||||
// then
|
|
||||||
assert.Equal(t, "", colorCode)
|
assert.Equal(t, "", colorCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetAnsiFromColorStringInvalidBg(t *testing.T) {
|
func TestGetAnsiFromColorStringInvalidBg(t *testing.T) {
|
||||||
// given
|
|
||||||
renderer := &AnsiColor{
|
renderer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
}
|
}
|
||||||
|
|
||||||
// when
|
|
||||||
colorCode := renderer.getAnsiFromColorString("invalid", true)
|
colorCode := renderer.getAnsiFromColorString("invalid", true)
|
||||||
|
|
||||||
// then
|
|
||||||
assert.Equal(t, "", colorCode)
|
assert.Equal(t, "", colorCode)
|
||||||
}
|
}
|
||||||
|
|
87
src/ansi_formats.go
Normal file
87
src/ansi_formats.go
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/text/unicode/norm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ansiFormats struct {
|
||||||
|
shell string
|
||||||
|
linechange string
|
||||||
|
left string
|
||||||
|
right string
|
||||||
|
creset string
|
||||||
|
clearOEL string
|
||||||
|
saveCursorPosition string
|
||||||
|
restoreCursorPosition string
|
||||||
|
title string
|
||||||
|
colorSingle string
|
||||||
|
colorFull string
|
||||||
|
colorTransparent string
|
||||||
|
escapeLeft string
|
||||||
|
escapeRight string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ansiFormats) init(shell string) {
|
||||||
|
a.shell = shell
|
||||||
|
switch shell {
|
||||||
|
case zsh:
|
||||||
|
a.linechange = "%%{\x1b[%d%s%%}"
|
||||||
|
a.left = "%%{\x1b[%dC%%}"
|
||||||
|
a.right = "%%{\x1b[%dD%%}"
|
||||||
|
a.creset = "%{\x1b[0m%}"
|
||||||
|
a.clearOEL = "%{\x1b[K%}"
|
||||||
|
a.saveCursorPosition = "%{\x1b7%}"
|
||||||
|
a.restoreCursorPosition = "%{\x1b8%}"
|
||||||
|
a.title = "%%{\033]0;%s\007%%}"
|
||||||
|
a.colorSingle = "%%{\x1b[%sm%%}%s%%{\x1b[0m%%}"
|
||||||
|
a.colorFull = "%%{\x1b[%sm\x1b[%sm%%}%s%%{\x1b[0m%%}"
|
||||||
|
a.colorTransparent = "%%{\x1b[%s;49m\x1b[7m%%}%s%%{\x1b[m\x1b[0m%%}"
|
||||||
|
a.escapeLeft = "%{"
|
||||||
|
a.escapeRight = "%}"
|
||||||
|
case bash:
|
||||||
|
a.linechange = "\\[\x1b[%d%s\\]"
|
||||||
|
a.left = "\\[\x1b[%dC\\]"
|
||||||
|
a.right = "\\[\x1b[%dD\\]"
|
||||||
|
a.creset = "\\[\x1b[0m\\]"
|
||||||
|
a.clearOEL = "\\[\x1b[K\\]"
|
||||||
|
a.saveCursorPosition = "\\[\x1b7\\]"
|
||||||
|
a.restoreCursorPosition = "\\[\x1b8\\]"
|
||||||
|
a.title = "\\[\033]0;%s\007\\]"
|
||||||
|
a.colorSingle = "\\[\x1b[%sm\\]%s\\[\x1b[0m\\]"
|
||||||
|
a.colorFull = "\\[\x1b[%sm\x1b[%sm\\]%s\\[\x1b[0m\\]"
|
||||||
|
a.colorTransparent = "\\[\x1b[%s;49m\x1b[7m\\]%s\\[\x1b[m\x1b[0m\\]"
|
||||||
|
a.escapeLeft = "\\["
|
||||||
|
a.escapeRight = "\\]"
|
||||||
|
default:
|
||||||
|
a.linechange = "\x1b[%d%s"
|
||||||
|
a.left = "\x1b[%dC"
|
||||||
|
a.right = "\x1b[%dD"
|
||||||
|
a.creset = "\x1b[0m"
|
||||||
|
a.clearOEL = "\x1b[K"
|
||||||
|
a.saveCursorPosition = "\x1b7"
|
||||||
|
a.restoreCursorPosition = "\x1b8"
|
||||||
|
a.title = "\033]0;%s\007"
|
||||||
|
a.colorSingle = "\x1b[%sm%s\x1b[0m"
|
||||||
|
a.colorFull = "\x1b[%sm\x1b[%sm%s\x1b[0m"
|
||||||
|
a.colorTransparent = "\x1b[%s;49m\x1b[7m%s\x1b[m\x1b[0m"
|
||||||
|
a.escapeLeft = ""
|
||||||
|
a.escapeRight = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ansiFormats) lenWithoutANSI(text string) int {
|
||||||
|
rANSI := "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
|
||||||
|
stripped := replaceAllString(rANSI, text, "")
|
||||||
|
stripped = strings.ReplaceAll(stripped, a.escapeLeft, "")
|
||||||
|
stripped = strings.ReplaceAll(stripped, a.escapeRight, "")
|
||||||
|
var i norm.Iter
|
||||||
|
i.InitString(norm.NFD, stripped)
|
||||||
|
var count int
|
||||||
|
for !i.Done() {
|
||||||
|
i.Next()
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
25
src/ansi_formats_test.go
Normal file
25
src/ansi_formats_test.go
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLenWithoutAnsi(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
Text string
|
||||||
|
ShellName string
|
||||||
|
Expected int
|
||||||
|
}{
|
||||||
|
{Text: "%{\x1b[44m%}hello%{\x1b[0m%}", ShellName: zsh, Expected: 5},
|
||||||
|
{Text: "\x1b[44mhello\x1b[0m", ShellName: pwsh, Expected: 5},
|
||||||
|
{Text: "\\[\x1b[44m\\]hello\\[\x1b[0m\\]", ShellName: bash, Expected: 5},
|
||||||
|
}
|
||||||
|
for _, tc := range cases {
|
||||||
|
a := ansiFormats{}
|
||||||
|
a.init(tc.ShellName)
|
||||||
|
strippedLength := a.lenWithoutANSI(tc.Text)
|
||||||
|
assert.Equal(t, 5, strippedLength)
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,78 +3,12 @@ package main
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/text/unicode/norm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func lenWithoutANSI(text, shell string) int {
|
|
||||||
rANSI := "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
|
|
||||||
stripped := replaceAllString(rANSI, text, "")
|
|
||||||
switch shell {
|
|
||||||
case zsh:
|
|
||||||
stripped = strings.ReplaceAll(stripped, "%{", "")
|
|
||||||
stripped = strings.ReplaceAll(stripped, "%}", "")
|
|
||||||
case bash:
|
|
||||||
stripped = strings.ReplaceAll(stripped, "\\[", "")
|
|
||||||
stripped = strings.ReplaceAll(stripped, "\\]", "")
|
|
||||||
}
|
|
||||||
var i norm.Iter
|
|
||||||
i.InitString(norm.NFD, stripped)
|
|
||||||
var count int
|
|
||||||
for !i.Done() {
|
|
||||||
i.Next()
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
type formats struct {
|
|
||||||
linechange string
|
|
||||||
left string
|
|
||||||
right string
|
|
||||||
creset string
|
|
||||||
clearOEL string
|
|
||||||
saveCursorPosition string
|
|
||||||
restoreCursorPosition string
|
|
||||||
}
|
|
||||||
|
|
||||||
// AnsiRenderer exposes functionality using ANSI
|
// AnsiRenderer exposes functionality using ANSI
|
||||||
type AnsiRenderer struct {
|
type AnsiRenderer struct {
|
||||||
buffer *bytes.Buffer
|
buffer *bytes.Buffer
|
||||||
formats *formats
|
formats *ansiFormats
|
||||||
shell string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *AnsiRenderer) init(shell string) {
|
|
||||||
r.shell = shell
|
|
||||||
r.formats = &formats{}
|
|
||||||
switch shell {
|
|
||||||
case zsh:
|
|
||||||
r.formats.linechange = "%%{\x1b[%d%s%%}"
|
|
||||||
r.formats.left = "%%{\x1b[%dC%%}"
|
|
||||||
r.formats.right = "%%{\x1b[%dD%%}"
|
|
||||||
r.formats.creset = "%{\x1b[0m%}"
|
|
||||||
r.formats.clearOEL = "%{\x1b[K%}"
|
|
||||||
r.formats.saveCursorPosition = "%{\x1b7%}"
|
|
||||||
r.formats.restoreCursorPosition = "%{\x1b8%}"
|
|
||||||
case bash:
|
|
||||||
r.formats.linechange = "\\[\x1b[%d%s\\]"
|
|
||||||
r.formats.left = "\\[\x1b[%dC\\]"
|
|
||||||
r.formats.right = "\\[\x1b[%dD\\]"
|
|
||||||
r.formats.creset = "\\[\x1b[0m\\]"
|
|
||||||
r.formats.clearOEL = "\\[\x1b[K\\]"
|
|
||||||
r.formats.saveCursorPosition = "\\[\x1b7\\]"
|
|
||||||
r.formats.restoreCursorPosition = "\\[\x1b8\\]"
|
|
||||||
default:
|
|
||||||
r.formats.linechange = "\x1b[%d%s"
|
|
||||||
r.formats.left = "\x1b[%dC"
|
|
||||||
r.formats.right = "\x1b[%dD"
|
|
||||||
r.formats.creset = "\x1b[0m"
|
|
||||||
r.formats.clearOEL = "\x1b[K"
|
|
||||||
r.formats.saveCursorPosition = "\x1b7"
|
|
||||||
r.formats.restoreCursorPosition = "\x1b8"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *AnsiRenderer) carriageForward() {
|
func (r *AnsiRenderer) carriageForward() {
|
||||||
|
@ -82,7 +16,7 @@ func (r *AnsiRenderer) carriageForward() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *AnsiRenderer) setCursorForRightWrite(text string, offset int) {
|
func (r *AnsiRenderer) setCursorForRightWrite(text string, offset int) {
|
||||||
strippedLen := lenWithoutANSI(text, r.shell) + -offset
|
strippedLen := r.formats.lenWithoutANSI(text) + -offset
|
||||||
r.buffer.WriteString(fmt.Sprintf(r.formats.right, strippedLen))
|
r.buffer.WriteString(fmt.Sprintf(r.formats.right, strippedLen))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import "fmt"
|
||||||
type consoleTitle struct {
|
type consoleTitle struct {
|
||||||
env environmentInfo
|
env environmentInfo
|
||||||
settings *Settings
|
settings *Settings
|
||||||
|
formats *ansiFormats
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConsoleTitleStyle defines how to show the title in the console window
|
// ConsoleTitleStyle defines how to show the title in the console window
|
||||||
|
@ -18,25 +19,14 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t *consoleTitle) getConsoleTitle() string {
|
func (t *consoleTitle) getConsoleTitle() string {
|
||||||
|
var title string
|
||||||
switch t.settings.ConsoleTitleStyle {
|
switch t.settings.ConsoleTitleStyle {
|
||||||
case FullPath:
|
case FullPath:
|
||||||
return t.formatConsoleTitle(t.env.getcwd())
|
title = t.env.getcwd()
|
||||||
case FolderName:
|
case FolderName:
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
return t.formatConsoleTitle(base(t.env.getcwd(), t.env))
|
title = base(t.env.getcwd(), t.env)
|
||||||
}
|
}
|
||||||
}
|
return fmt.Sprintf(t.formats.title, title)
|
||||||
|
|
||||||
func (t *consoleTitle) formatConsoleTitle(title string) string {
|
|
||||||
var format string
|
|
||||||
switch t.env.getShellName() {
|
|
||||||
case zsh:
|
|
||||||
format = "%%{\033]0;%s\007%%}"
|
|
||||||
case bash:
|
|
||||||
format = "\\[\033]0;%s\007\\]"
|
|
||||||
default:
|
|
||||||
format = "\033]0;%s\007"
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(format, title)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,10 +26,12 @@ func TestGetConsoleTitle(t *testing.T) {
|
||||||
env.On("getcwd", nil).Return(tc.Cwd)
|
env.On("getcwd", nil).Return(tc.Cwd)
|
||||||
env.On("homeDir", nil).Return("/usr/home")
|
env.On("homeDir", nil).Return("/usr/home")
|
||||||
env.On("getPathSeperator", nil).Return(tc.PathSeperator)
|
env.On("getPathSeperator", nil).Return(tc.PathSeperator)
|
||||||
env.On("getShellName", nil).Return(tc.ShellName)
|
formats := &ansiFormats{}
|
||||||
|
formats.init(tc.ShellName)
|
||||||
ct := &consoleTitle{
|
ct := &consoleTitle{
|
||||||
env: env,
|
env: env,
|
||||||
settings: settings,
|
settings: settings,
|
||||||
|
formats: formats,
|
||||||
}
|
}
|
||||||
got := ct.getConsoleTitle()
|
got := ct.getConsoleTitle()
|
||||||
assert.Equal(t, tc.Expected, got)
|
assert.Equal(t, tc.Expected, got)
|
||||||
|
|
|
@ -10,6 +10,7 @@ type engine struct {
|
||||||
env environmentInfo
|
env environmentInfo
|
||||||
color *AnsiColor
|
color *AnsiColor
|
||||||
renderer *AnsiRenderer
|
renderer *AnsiRenderer
|
||||||
|
consoleTitle *consoleTitle
|
||||||
activeBlock *Block
|
activeBlock *Block
|
||||||
activeSegment *Segment
|
activeSegment *Segment
|
||||||
previousActiveSegment *Segment
|
previousActiveSegment *Segment
|
||||||
|
@ -149,11 +150,7 @@ func (e *engine) render() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if e.settings.ConsoleTitle {
|
if e.settings.ConsoleTitle {
|
||||||
title := &consoleTitle{
|
e.renderer.print(e.consoleTitle.getConsoleTitle())
|
||||||
env: e.env,
|
|
||||||
settings: e.settings,
|
|
||||||
}
|
|
||||||
e.renderer.print(title.getConsoleTitle())
|
|
||||||
}
|
}
|
||||||
e.renderer.creset()
|
e.renderer.creset()
|
||||||
if e.settings.FinalSpace {
|
if e.settings.FinalSpace {
|
||||||
|
|
24
src/main.go
24
src/main.go
|
@ -131,19 +131,27 @@ func main() {
|
||||||
if *args.Shell != "" {
|
if *args.Shell != "" {
|
||||||
shell = *args.Shell
|
shell = *args.Shell
|
||||||
}
|
}
|
||||||
|
formats := &ansiFormats{}
|
||||||
|
formats.init(shell)
|
||||||
renderer := &AnsiRenderer{
|
renderer := &AnsiRenderer{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
|
formats: formats,
|
||||||
}
|
}
|
||||||
colorer := &AnsiColor{
|
colorer := &AnsiColor{
|
||||||
buffer: new(bytes.Buffer),
|
buffer: new(bytes.Buffer),
|
||||||
|
formats: formats,
|
||||||
}
|
}
|
||||||
renderer.init(shell)
|
title := &consoleTitle{
|
||||||
colorer.init(shell)
|
|
||||||
engine := &engine{
|
|
||||||
settings: settings,
|
|
||||||
env: env,
|
env: env,
|
||||||
color: colorer,
|
settings: settings,
|
||||||
renderer: renderer,
|
formats: formats,
|
||||||
|
}
|
||||||
|
engine := &engine{
|
||||||
|
settings: settings,
|
||||||
|
env: env,
|
||||||
|
color: colorer,
|
||||||
|
renderer: renderer,
|
||||||
|
consoleTitle: title,
|
||||||
}
|
}
|
||||||
engine.render()
|
engine.render()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue