mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-12-31 13:57:26 -08:00
e618163da3
new http_timeout param for segments executing http request doc updated json schema updated(missing data for owm + timeout) hyperlink replacement regex updated to support http/https, fix an inssue when a segment with hyperlink was on the right prompt(pwsh)
208 lines
6.5 KiB
Go
208 lines
6.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
ansiRegex = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"
|
|
)
|
|
|
|
type ansiUtils struct {
|
|
shell string
|
|
linechange string
|
|
left string
|
|
right string
|
|
creset string
|
|
clearBelow string
|
|
clearLine string
|
|
saveCursorPosition string
|
|
restoreCursorPosition string
|
|
title string
|
|
colorSingle string
|
|
colorFull string
|
|
colorTransparent string
|
|
escapeLeft string
|
|
escapeRight string
|
|
hyperlink string
|
|
osc99 string
|
|
bold string
|
|
italic string
|
|
underline string
|
|
strikethrough string
|
|
bashFormat string
|
|
shellReservedKeywords []shellKeyWordReplacement
|
|
}
|
|
|
|
type shellKeyWordReplacement struct {
|
|
text string
|
|
replacement string
|
|
}
|
|
|
|
func (a *ansiUtils) init(shell string) {
|
|
a.shell = shell
|
|
a.bashFormat = "\\[%s\\]"
|
|
switch shell {
|
|
case zsh:
|
|
a.linechange = "%%{\x1b[%d%s%%}"
|
|
a.right = "%%{\x1b[%dC%%}"
|
|
a.left = "%%{\x1b[%dD%%}"
|
|
a.creset = "%{\x1b[0m%}"
|
|
a.clearBelow = "%{\x1b[0J%}"
|
|
a.clearLine = "%{\x1b[K%}"
|
|
a.saveCursorPosition = "%{\x1b7%}"
|
|
a.restoreCursorPosition = "%{\x1b8%}"
|
|
a.title = "%%{\x1b]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[0m%%}"
|
|
a.escapeLeft = "%{"
|
|
a.escapeRight = "%}"
|
|
a.hyperlink = "%%{\x1b]8;;%s\x1b\\%%}%s%%{\x1b]8;;\x1b\\%%}"
|
|
a.osc99 = "%%{\x1b]9;9;\"%s\"\x1b\\%%}"
|
|
a.bold = "%%{\x1b[1m%%}%s%%{\x1b[22m%%}"
|
|
a.italic = "%%{\x1b[3m%%}%s%%{\x1b[23m%%}"
|
|
a.underline = "%%{\x1b[4m%%}%s%%{\x1b[24m%%}"
|
|
a.strikethrough = "%%{\x1b[9m%%}%s%%{\x1b[29m%%}"
|
|
// escape double quotes and variable expansion
|
|
a.shellReservedKeywords = append(a.shellReservedKeywords, shellKeyWordReplacement{"\\", "\\\\"}, shellKeyWordReplacement{"%", "%%"})
|
|
case bash:
|
|
a.linechange = "\\[\x1b[%d%s\\]"
|
|
a.right = "\\[\x1b[%dC\\]"
|
|
a.left = "\\[\x1b[%dD\\]"
|
|
a.creset = "\\[\x1b[0m\\]"
|
|
a.clearBelow = "\\[\x1b[0J\\]"
|
|
a.clearLine = "\\[\x1b[K\\]"
|
|
a.saveCursorPosition = "\\[\x1b7\\]"
|
|
a.restoreCursorPosition = "\\[\x1b8\\]"
|
|
a.title = "\\[\x1b]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[0m\\]"
|
|
a.escapeLeft = "\\["
|
|
a.escapeRight = "\\]"
|
|
a.hyperlink = "\\[\x1b]8;;%s\x1b\\\\\\]%s\\[\x1b]8;;\x1b\\\\\\]"
|
|
a.osc99 = "\\[\x1b]9;9;\"%s\"\x1b\\\\\\]"
|
|
a.bold = "\\[\x1b[1m\\]%s\\[\x1b[22m\\]"
|
|
a.italic = "\\[\x1b[3m\\]%s\\[\x1b[23m\\]"
|
|
a.underline = "\\[\x1b[4m\\]%s\\[\x1b[24m\\]"
|
|
a.strikethrough = "\\[\x1b[9m\\]%s\\[\x1b[29m\\]"
|
|
// escape backslashes to avoid replacements
|
|
// https://tldp.org/HOWTO/Bash-Prompt-HOWTO/bash-prompt-escape-sequences.html
|
|
a.shellReservedKeywords = append(a.shellReservedKeywords, shellKeyWordReplacement{"\\", "\\\\"})
|
|
default:
|
|
a.linechange = "\x1b[%d%s"
|
|
a.right = "\x1b[%dC"
|
|
a.left = "\x1b[%dD"
|
|
a.creset = "\x1b[0m"
|
|
a.clearBelow = "\x1b[0J"
|
|
a.clearLine = "\x1b[K"
|
|
a.saveCursorPosition = "\x1b7"
|
|
a.restoreCursorPosition = "\x1b8"
|
|
a.title = "\x1b]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[0m"
|
|
a.escapeLeft = ""
|
|
a.escapeRight = ""
|
|
a.hyperlink = "\x1b]8;;%s\x1b\\%s\x1b]8;;\x1b\\"
|
|
a.osc99 = "\x1b]9;9;\"%s\"\x1b\\"
|
|
a.bold = "\x1b[1m%s\x1b[22m"
|
|
a.italic = "\x1b[3m%s\x1b[23m"
|
|
a.underline = "\x1b[4m%s\x1b[24m"
|
|
a.strikethrough = "\x1b[9m%s\x1b[29m"
|
|
}
|
|
// common replacement for all shells
|
|
a.shellReservedKeywords = append(a.shellReservedKeywords, shellKeyWordReplacement{"`", "'"})
|
|
}
|
|
|
|
func (a *ansiUtils) lenWithoutANSI(text string) int {
|
|
if len(text) == 0 {
|
|
return 0
|
|
}
|
|
// replace hyperlinks(file/http/https)
|
|
matches := findAllNamedRegexMatch(`(?P<STR>\x1b]8;;(file|http|https):\/\/(.+?)\x1b\\(?P<URL>.+?)\x1b]8;;\x1b\\)`, text)
|
|
for _, match := range matches {
|
|
text = strings.ReplaceAll(text, match[str], match[url])
|
|
}
|
|
// replace console title
|
|
matches = findAllNamedRegexMatch(`(?P<STR>\x1b\]0;(.+)\007)`, text)
|
|
for _, match := range matches {
|
|
text = strings.ReplaceAll(text, match[str], "")
|
|
}
|
|
stripped := replaceAllString(ansiRegex, text, "")
|
|
stripped = strings.ReplaceAll(stripped, a.escapeLeft, "")
|
|
stripped = strings.ReplaceAll(stripped, a.escapeRight, "")
|
|
runeText := []rune(stripped)
|
|
return len(runeText)
|
|
}
|
|
|
|
func (a *ansiUtils) generateHyperlink(text string) string {
|
|
// hyperlink matching
|
|
results := findNamedRegexMatch("(?P<all>(?:\\[(?P<name>.+)\\])(?:\\((?P<url>.*)\\)))", text)
|
|
if len(results) != 3 {
|
|
return text
|
|
}
|
|
// build hyperlink ansi
|
|
hyperlink := fmt.Sprintf(a.hyperlink, results["url"], results["name"])
|
|
// replace original text by the new one
|
|
return strings.Replace(text, results["all"], hyperlink, 1)
|
|
}
|
|
|
|
func (a *ansiUtils) formatText(text string) string {
|
|
results := findAllNamedRegexMatch("(?P<context><(?P<format>[buis])>(?P<text>[^<]+)</[buis]>)", text)
|
|
for _, result := range results {
|
|
var formatted string
|
|
switch result["format"] {
|
|
case "b":
|
|
formatted = fmt.Sprintf(a.bold, result["text"])
|
|
case "u":
|
|
formatted = fmt.Sprintf(a.underline, result["text"])
|
|
case "i":
|
|
formatted = fmt.Sprintf(a.italic, result["text"])
|
|
case "s":
|
|
formatted = fmt.Sprintf(a.strikethrough, result["text"])
|
|
}
|
|
text = strings.Replace(text, result["context"], formatted, 1)
|
|
}
|
|
return text
|
|
}
|
|
|
|
func (a *ansiUtils) carriageForward() string {
|
|
return fmt.Sprintf(a.right, 1000)
|
|
}
|
|
|
|
func (a *ansiUtils) getCursorForRightWrite(text string, offset int) string {
|
|
strippedLen := a.lenWithoutANSI(text) + -offset
|
|
return fmt.Sprintf(a.left, strippedLen)
|
|
}
|
|
|
|
func (a *ansiUtils) changeLine(numberOfLines int) string {
|
|
position := "B"
|
|
if numberOfLines < 0 {
|
|
position = "F"
|
|
numberOfLines = -numberOfLines
|
|
}
|
|
return fmt.Sprintf(a.linechange, numberOfLines, position)
|
|
}
|
|
|
|
func (a *ansiUtils) consolePwd(pwd string) string {
|
|
if strings.HasSuffix(pwd, ":") {
|
|
pwd += "\\"
|
|
}
|
|
return fmt.Sprintf(a.osc99, pwd)
|
|
}
|
|
|
|
func (a *ansiUtils) clearAfter() string {
|
|
return a.clearLine + a.clearBelow
|
|
}
|
|
|
|
func (a *ansiUtils) escapeText(text string) string {
|
|
// what to escape/replace is different per shell
|
|
for _, s := range a.shellReservedKeywords {
|
|
text = strings.ReplaceAll(text, s.text, s.replacement)
|
|
}
|
|
return text
|
|
}
|