oh-my-posh/ansi_renderer.go
2020-12-19 19:21:29 +01:00

137 lines
3.4 KiB
Go

package main
import (
"bytes"
"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
title string
creset string
clearOEL string
saveCursorPosition string
restoreCursorPosition string
}
// AnsiRenderer exposes functionality using ANSI
type AnsiRenderer struct {
buffer *bytes.Buffer
formats *formats
shell string
}
const (
zsh = "zsh"
bash = "bash"
pwsh = "pwsh"
powershell5 = "powershell"
)
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.title = "%%{\033]0;%s\007%%}"
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.title = "\\[\033]0;%s\007\\]"
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.title = "\033]0;%s\007"
r.formats.creset = "\x1b[0m"
r.formats.clearOEL = "\x1b[K"
r.formats.saveCursorPosition = "\x1b7"
r.formats.restoreCursorPosition = "\x1b8"
}
}
func (r *AnsiRenderer) carriageForward() {
r.buffer.WriteString(fmt.Sprintf(r.formats.left, 1000))
}
func (r *AnsiRenderer) setCursorForRightWrite(text string, offset int) {
strippedLen := lenWithoutANSI(text, r.shell) + -offset
r.buffer.WriteString(fmt.Sprintf(r.formats.right, strippedLen))
}
func (r *AnsiRenderer) changeLine(numberOfLines int) {
position := "B"
if numberOfLines < 0 {
position = "F"
numberOfLines = -numberOfLines
}
r.buffer.WriteString(fmt.Sprintf(r.formats.linechange, numberOfLines, position))
}
func (r *AnsiRenderer) setConsoleTitle(title string) {
r.buffer.WriteString(fmt.Sprintf(r.formats.title, title))
}
func (r *AnsiRenderer) creset() {
r.buffer.WriteString(r.formats.creset)
}
func (r *AnsiRenderer) print(text string) {
r.buffer.WriteString(text)
r.clearEOL()
}
func (r *AnsiRenderer) clearEOL() {
r.buffer.WriteString(r.formats.clearOEL)
}
func (r *AnsiRenderer) string() string {
return r.buffer.String()
}
func (r *AnsiRenderer) saveCursorPosition() {
r.buffer.WriteString(r.formats.saveCursorPosition)
}
func (r *AnsiRenderer) restoreCursorPosition() {
r.buffer.WriteString(r.formats.restoreCursorPosition)
}