mirror of
https://github.com/JanDeDobbeleer/oh-my-posh.git
synced 2024-11-10 04:54:03 -08:00
fix(hyperlink): use unique format to avoid conflicts
BREAKING CHANGE: this can cause existing manual hyperlinks (`[text](link)`) to stop working. To fix, change those to the following syntax: `«text»(link)` resolves #3353
This commit is contained in:
parent
7ce487d6b1
commit
4f83452e84
|
@ -112,7 +112,7 @@ type Writer struct {
|
|||
// hyperlink
|
||||
hasHyperlink bool
|
||||
hyperlinkBuilder strings.Builder
|
||||
squareIndex, roundCount int
|
||||
bracketIndex, roundCount int
|
||||
hyperlinkState string
|
||||
}
|
||||
|
||||
|
@ -302,7 +302,7 @@ func (w *Writer) Write(background, foreground, text string) {
|
|||
w.runes = []rune(text)
|
||||
|
||||
// only run hyperlink logic when we have to
|
||||
w.hasHyperlink = strings.Count(text, "[")+strings.Count(text, "]")+strings.Count(text, "(")+strings.Count(text, ")") >= 4
|
||||
w.hasHyperlink = strings.Count(text, "«")+strings.Count(text, "»")+strings.Count(text, "(")+strings.Count(text, ")") >= 4
|
||||
|
||||
for i := 0; i < len(w.runes); i++ {
|
||||
s := w.runes[i]
|
||||
|
|
|
@ -20,7 +20,7 @@ func (w *Writer) write(i int, s rune) {
|
|||
return
|
||||
}
|
||||
|
||||
if s == '[' && w.hyperlinkState == OTHER {
|
||||
if s == '«' && w.hyperlinkState == OTHER {
|
||||
w.hyperlinkState = TEXT
|
||||
w.hyperlinkBuilder.WriteRune(s)
|
||||
return
|
||||
|
@ -35,12 +35,12 @@ func (w *Writer) write(i int, s rune) {
|
|||
w.hyperlinkBuilder.WriteRune(s)
|
||||
|
||||
switch s {
|
||||
case ']':
|
||||
case '»':
|
||||
// potential end of text part of hyperlink
|
||||
w.squareIndex = i
|
||||
w.bracketIndex = i
|
||||
case '(':
|
||||
// split into link part
|
||||
if w.squareIndex == i-1 {
|
||||
if w.bracketIndex == i-1 {
|
||||
w.hyperlinkState = LINK
|
||||
}
|
||||
if w.hyperlinkState == LINK {
|
||||
|
@ -63,7 +63,7 @@ func (w *Writer) write(i int, s rune) {
|
|||
|
||||
func (w *Writer) replaceHyperlink(text string) string {
|
||||
// hyperlink matching
|
||||
results := regex.FindNamedRegexMatch("(?P<ALL>(?:\\[(?P<TEXT>.+)\\])(?:\\((?P<URL>.*)\\)))", text)
|
||||
results := regex.FindNamedRegexMatch("(?P<ALL>(?:«(?P<TEXT>.+)»)(?:\\((?P<URL>.*)\\)))", text)
|
||||
if len(results) != 3 {
|
||||
return text
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ func TestGenerateHyperlinkWithUrl(t *testing.T) {
|
|||
Expected string
|
||||
}{
|
||||
{
|
||||
Text: "[google](http://www.google.be) [maps (2/2)](http://maps.google.be)",
|
||||
Text: "«google»(http://www.google.be) «maps (2/2)»(http://maps.google.be)",
|
||||
ShellName: shell.FISH,
|
||||
Expected: "\x1b[47m\x1b[30m\x1b]8;;http://www.google.be\x1b\\google\x1b]8;;\x1b\\ \x1b]8;;http://maps.google.be\x1b\\maps (2/2)\x1b]8;;\x1b\\\x1b[0m",
|
||||
},
|
||||
|
@ -46,18 +46,23 @@ func TestGenerateHyperlinkWithUrl(t *testing.T) {
|
|||
ShellName: shell.PWSH,
|
||||
Expected: "\x1b[47m\x1b[30min \x1b[0m\x1b[30m\x1b[1mpwsh \x1b[22m\x1b[47m \x1b[0m",
|
||||
},
|
||||
{Text: "[google](http://www.google.be)", ShellName: shell.ZSH, Expected: "%{\x1b[47m%}%{\x1b[30m%}%{\x1b]8;;http://www.google.be\x1b\\%}google%{\x1b]8;;\x1b\\%}%{\x1b[0m%}"},
|
||||
{Text: "[google](http://www.google.be)", ShellName: shell.PWSH, Expected: "\x1b[47m\x1b[30m\x1b]8;;http://www.google.be\x1b\\google\x1b]8;;\x1b\\\x1b[0m"},
|
||||
{Text: "«google»(http://www.google.be)", ShellName: shell.ZSH, Expected: "%{\x1b[47m%}%{\x1b[30m%}%{\x1b]8;;http://www.google.be\x1b\\%}google%{\x1b]8;;\x1b\\%}%{\x1b[0m%}"},
|
||||
{Text: "«google»(http://www.google.be)", ShellName: shell.PWSH, Expected: "\x1b[47m\x1b[30m\x1b]8;;http://www.google.be\x1b\\google\x1b]8;;\x1b\\\x1b[0m"},
|
||||
{
|
||||
Text: "[google](http://www.google.be)",
|
||||
Text: "«google»(http://www.google.be)",
|
||||
ShellName: shell.BASH,
|
||||
Expected: "\\[\x1b[47m\\]\\[\x1b[30m\\]\\[\x1b]8;;http://www.google.be\x1b\\\\\\]google\\[\x1b]8;;\x1b\\\\\\]\\[\x1b[0m\\]",
|
||||
},
|
||||
{
|
||||
Text: "[google](http://www.google.be) [maps](http://maps.google.be)",
|
||||
Text: "«google»(http://www.google.be) «maps»(http://maps.google.be)",
|
||||
ShellName: shell.FISH,
|
||||
Expected: "\x1b[47m\x1b[30m\x1b]8;;http://www.google.be\x1b\\google\x1b]8;;\x1b\\ \x1b]8;;http://maps.google.be\x1b\\maps\x1b]8;;\x1b\\\x1b[0m",
|
||||
},
|
||||
{
|
||||
Text: "[]«google»(http://www.google.be)[]",
|
||||
ShellName: shell.FISH,
|
||||
Expected: "\x1b[47m\x1b[30m[]\x1b]8;;http://www.google.be\x1b\\google\x1b]8;;\x1b\\[]\x1b[0m",
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
a := Writer{
|
||||
|
@ -76,9 +81,9 @@ func TestGenerateHyperlinkWithUrlNoName(t *testing.T) {
|
|||
ShellName string
|
||||
Expected string
|
||||
}{
|
||||
{Text: "[](http://www.google.be)", ShellName: shell.ZSH, Expected: "%{\x1b[47m%}%{\x1b[30m%}[](http://www.google.be)%{\x1b[0m%}"},
|
||||
{Text: "[](http://www.google.be)", ShellName: shell.PWSH, Expected: "\x1b[47m\x1b[30m[](http://www.google.be)\x1b[0m"},
|
||||
{Text: "[](http://www.google.be)", ShellName: shell.BASH, Expected: "\\[\x1b[47m\\]\\[\x1b[30m\\][](http://www.google.be)\\[\x1b[0m\\]"},
|
||||
{Text: "«»(http://www.google.be)", ShellName: shell.ZSH, Expected: "%{\x1b[47m%}%{\x1b[30m%}«»(http://www.google.be)%{\x1b[0m%}"},
|
||||
{Text: "«»(http://www.google.be)", ShellName: shell.PWSH, Expected: "\x1b[47m\x1b[30m«»(http://www.google.be)\x1b[0m"},
|
||||
{Text: "«»(http://www.google.be)", ShellName: shell.BASH, Expected: "\\[\x1b[47m\\]\\[\x1b[30m\\]«»(http://www.google.be)\\[\x1b[0m\\]"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
a := Writer{
|
||||
|
@ -97,10 +102,10 @@ func TestGenerateFileLink(t *testing.T) {
|
|||
Expected string
|
||||
}{
|
||||
{
|
||||
Text: `[Posh](file:C:/Program Files (x86)/Common Files/Microsoft Shared/Posh)`,
|
||||
Text: `«Posh»(file:C:/Program Files (x86)/Common Files/Microsoft Shared/Posh)`,
|
||||
Expected: "\x1b[47m\x1b[30m\x1b]8;;file:C:/Program Files (x86)/Common Files/Microsoft Shared/Posh\x1b\\Posh\x1b]8;;\x1b\\\x1b[0m",
|
||||
},
|
||||
{Text: `[Windows](file:C:/Windows)`, Expected: "\x1b[47m\x1b[30m\x1b]8;;file:C:/Windows\x1b\\Windows\x1b]8;;\x1b\\\x1b[0m"},
|
||||
{Text: `«Windows»(file:C:/Windows)`, Expected: "\x1b[47m\x1b[30m\x1b]8;;file:C:/Windows\x1b\\Windows\x1b]8;;\x1b\\\x1b[0m"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
a := Writer{
|
||||
|
|
|
@ -226,13 +226,13 @@ func TestWriteLength(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Case: "Bold with color override and link",
|
||||
Input: "<b><#ffffff>test</></b> [url](https://example.com)",
|
||||
Input: "<b><#ffffff>test</></b> «url»(https://example.com)",
|
||||
Expected: 8,
|
||||
Colors: &Colors{Foreground: "black", Background: ParentBackground},
|
||||
},
|
||||
{
|
||||
Case: "Bold with color override and link and leading/trailing spaces",
|
||||
Input: " <b><#ffffff>test</></b> [url](https://example.com) ",
|
||||
Input: " <b><#ffffff>test</></b> «url»(https://example.com) ",
|
||||
Expected: 10,
|
||||
Colors: &Colors{Foreground: "black", Background: ParentBackground},
|
||||
},
|
||||
|
|
|
@ -29,7 +29,7 @@ type Brewfather struct {
|
|||
DaysBottled uint
|
||||
DaysBottledOrFermented *uint // help avoid chronic template logic - code will point this to one of above or be nil depending on status
|
||||
|
||||
URL string // URL of batch page to open if hyperlink enabled on the segment and URL formatting used in template: [name](link)
|
||||
URL string // URL of batch page to open if hyperlink enabled on the segment and URL formatting used in template: «text»(link)
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
|
@ -207,7 +207,7 @@ func TestOWMSegmentIcons(t *testing.T) {
|
|||
env := &mock.MockedEnvironment{}
|
||||
|
||||
response := fmt.Sprintf(`{"weather":[{"icon":"%s"}],"main":{"temp":20.3}}`, tc.IconID)
|
||||
expectedString := fmt.Sprintf("[%s (20°C)](http://api.openweathermap.org/data/2.5/weather?q=AMSTERDAM,NL&units=metric&appid=key)", tc.ExpectedIconString)
|
||||
expectedString := fmt.Sprintf("«%s (20°C)»(http://api.openweathermap.org/data/2.5/weather?q=AMSTERDAM,NL&units=metric&appid=key)", tc.ExpectedIconString)
|
||||
|
||||
env.On("HTTPRequest", OWMAPIURL).Return([]byte(response), nil)
|
||||
|
||||
|
@ -222,7 +222,7 @@ func TestOWMSegmentIcons(t *testing.T) {
|
|||
}
|
||||
|
||||
assert.Nil(t, o.setStatus())
|
||||
assert.Equal(t, expectedString, renderTemplate(env, "[{{.Weather}} ({{.Temperature}}{{.UnitIcon}})]({{.URL}})", o), tc.Case)
|
||||
assert.Equal(t, expectedString, renderTemplate(env, "«{{.Weather}} ({{.Temperature}}{{.UnitIcon}})»({{.URL}})", o), tc.Case)
|
||||
}
|
||||
}
|
||||
func TestOWMSegmentFromCache(t *testing.T) {
|
||||
|
@ -250,7 +250,7 @@ func TestOWMSegmentFromCache(t *testing.T) {
|
|||
|
||||
func TestOWMSegmentFromCacheWithHyperlink(t *testing.T) {
|
||||
response := fmt.Sprintf(`{"weather":[{"icon":"%s"}],"main":{"temp":20}}`, "01d")
|
||||
expectedString := fmt.Sprintf("[%s (20°C)](http://api.openweathermap.org/data/2.5/weather?q=AMSTERDAM,NL&units=metric&appid=key)", "\ufa98")
|
||||
expectedString := fmt.Sprintf("«%s (20°C)»(http://api.openweathermap.org/data/2.5/weather?q=AMSTERDAM,NL&units=metric&appid=key)", "\ufa98")
|
||||
|
||||
env := &mock.MockedEnvironment{}
|
||||
cache := &mock.MockedCache{}
|
||||
|
@ -269,5 +269,5 @@ func TestOWMSegmentFromCacheWithHyperlink(t *testing.T) {
|
|||
env.On("Cache").Return(cache)
|
||||
|
||||
assert.Nil(t, o.setStatus())
|
||||
assert.Equal(t, expectedString, renderTemplate(env, "[{{.Weather}} ({{.Temperature}}{{.UnitIcon}})]({{.URL}})", o))
|
||||
assert.Equal(t, expectedString, renderTemplate(env, "«{{.Weather}} ({{.Temperature}}{{.UnitIcon}})»({{.URL}})", o))
|
||||
}
|
||||
|
|
|
@ -14,9 +14,9 @@ func url(text, url string) (string, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("[%s](%s)", text, url), nil
|
||||
return fmt.Sprintf("«%s»(%s)", text, url), nil
|
||||
}
|
||||
|
||||
func path(text, path string) (string, error) {
|
||||
return fmt.Sprintf("[%s](file:%s)", text, path), nil
|
||||
return fmt.Sprintf("«%s»(file:%s)", text, path), nil
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ func TestUrl(t *testing.T) {
|
|||
Template string
|
||||
ShouldError bool
|
||||
}{
|
||||
{Case: "valid url", Expected: "[link](https://ohmyposh.dev)", Template: `{{ url "link" "https://ohmyposh.dev" }}`},
|
||||
{Case: "valid url", Expected: "«link»(https://ohmyposh.dev)", Template: `{{ url "link" "https://ohmyposh.dev" }}`},
|
||||
{Case: "invalid url", Expected: "", Template: `{{ url "link" "Foo" }}`, ShouldError: true},
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ func TestPath(t *testing.T) {
|
|||
Expected string
|
||||
Template string
|
||||
}{
|
||||
{Case: "valid path", Expected: "[link](file:/test/test)", Template: `{{ path "link" "/test/test" }}`},
|
||||
{Case: "valid path", Expected: "«link»(file:/test/test)", Template: `{{ path "link" "/test/test" }}`},
|
||||
}
|
||||
|
||||
env := &mock.MockedEnvironment{}
|
||||
|
|
|
@ -137,8 +137,8 @@ Hyperlink formatting example
|
|||
|
||||
```json
|
||||
{
|
||||
// General format: [Text](Url)
|
||||
"template": "[{{.StatusIcon}} {{if .DaysBottledOrFermented}}{{.DaysBottledOrFermented}}d{{end}} {{.Recipe.Name}}]({{.URL}})"
|
||||
// General format: «text»(link)
|
||||
"template": "«{{.StatusIcon}} {{if .DaysBottledOrFermented}}{{.DaysBottledOrFermented}}d{{end}} {{.Recipe.Name}}»({{.URL}})"
|
||||
}
|
||||
```
|
||||
|
||||
|
|
Loading…
Reference in a new issue