Skip to content

Commit

Permalink
feat: ansi text formats
Browse files Browse the repository at this point in the history
resolves #440
  • Loading branch information
JanDeDobbeleer committed Feb 27, 2021
1 parent 2fd823c commit 3fbf785
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 0 deletions.
11 changes: 11 additions & 0 deletions docs/docs/configuration.md
Expand Up @@ -335,6 +335,17 @@ Oh my Posh mainly supports three different color types being

`darkGray` `lightRed` `lightGreen` `lightYellow` `lightBlue` `lightMagenta` `lightCyan` `lightWhite`

### Text decorations

You can make use of the following syntax to decorate text:

- `<b>bold</b>`: renders `bold` as bold text
- `<u>underline</u>`: renders `underline` as underlined text
- `<i>italic</i>`: renders `italic` as italic text
- `<s>strikethrough</s>`: renders `strikethrough` as strikethrough text

This can be used in templates and icons/text inside your config.

### Hyperlinks

The engine has the ability to render hyperlinks. Your terminal has to support it and the option
Expand Down
1 change: 1 addition & 0 deletions src/ansi_color.go
Expand Up @@ -92,6 +92,7 @@ func (a *AnsiColor) writeAndRemoveText(background, foreground, text, textToRemov
}

func (a *AnsiColor) write(background, foreground, text string) {
text = a.formats.formatText(text)
// first we match for any potentially valid colors enclosed in <>
match := findAllNamedRegexMatch(`<(?P<foreground>[^,>]+)?,?(?P<background>[^>]+)?>(?P<content>[^<]*)<\/>`, text)
for i := range match {
Expand Down
36 changes: 36 additions & 0 deletions src/ansi_formats.go
Expand Up @@ -24,6 +24,10 @@ type ansiFormats struct {
escapeRight string
hyperlink string
osc99 string
bold string
italic string
underline string
strikethrough string
}

func (a *ansiFormats) init(shell string) {
Expand All @@ -45,6 +49,10 @@ func (a *ansiFormats) init(shell string) {
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%%}"
case bash:
a.linechange = "\\[\x1b[%d%s\\]"
a.left = "\\[\x1b[%dC\\]"
Expand All @@ -61,6 +69,10 @@ func (a *ansiFormats) init(shell string) {
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\\]"
default:
a.linechange = "\x1b[%d%s"
a.left = "\x1b[%dC"
Expand All @@ -77,6 +89,11 @@ func (a *ansiFormats) init(shell string) {
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"
a.strikethrough = "\x1b[9m%s\x1b[29m"
}
}

Expand Down Expand Up @@ -106,3 +123,22 @@ func (a *ansiFormats) generateHyperlink(text string) string {
// replace original text by the new one
return strings.Replace(text, results["all"], hyperlink, 1)
}

func (a *ansiFormats) 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
}
20 changes: 20 additions & 0 deletions src/ansi_formats_test.go
Expand Up @@ -77,3 +77,23 @@ func TestGenerateHyperlinkWithUrlNoName(t *testing.T) {
assert.Equal(t, tc.Expected, hyperlinkText)
}
}

func TestFormatText(t *testing.T) {
cases := []struct {
Case string
Text string
Expected string
}{
{Case: "single format", Text: "This <b>is</b> white", Expected: "This \x1b[1mis\x1b[22m white"},
{Case: "double format", Text: "This <b>is</b> white, this <b>is</b> orange", Expected: "This \x1b[1mis\x1b[22m white, this \x1b[1mis\x1b[22m orange"},
{Case: "underline", Text: "This <u>is</u> white", Expected: "This \x1b[4mis\x1b[24m white"},
{Case: "italic", Text: "This <i>is</i> white", Expected: "This \x1b[3mis\x1b[23m white"},
{Case: "strikethrough", Text: "This <s>is</s> white", Expected: "This \x1b[9mis\x1b[29m white"},
}
for _, tc := range cases {
a := ansiFormats{}
a.init("")
formattedText := a.formatText(tc.Text)
assert.Equal(t, tc.Expected, formattedText, tc.Case)
}
}

0 comments on commit 3fbf785

Please sign in to comment.