Skip to content

Commit 387c6ef

Browse files
committed
Support hyperlinks (OSC 8) in the main window
Close #2557
1 parent 581734c commit 387c6ef

File tree

5 files changed

+41
-13
lines changed

5 files changed

+41
-13
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ CHANGELOG
1717
# ...
1818
'
1919
```
20+
- Hyperlinks (OSC 8) are now supported in the preview window and in the main window
21+
```sh
22+
printf '<< \e]8;;http://github.com/junegunn/fzf\e\\Link to \e[32mfz\e[0mf\e]8;;\e\\ >>' | fzf --ansi
23+
24+
fzf --preview "printf '<< \e]8;;http://github.com/junegunn/fzf\e\\Link to \e[32mfz\e[0mf\e]8;;\e\\ >>'"
25+
```
2026
- Fixed `--tmux bottom` when the status line is not at the bottom
2127
- Fixed extra scroll offset in multi-line mode (`--read0` or `--wrap`)
2228
- Added fallback `ps` command for `kill` completion on Cygwin

src/result.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type colorOffset struct {
1616
offset [2]int32
1717
color tui.ColorPair
1818
match bool
19+
url *url
1920
}
2021

2122
type Result struct {
@@ -177,8 +178,11 @@ func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme,
177178
if curr != 0 && idx > start {
178179
if curr < 0 {
179180
color := colMatch
181+
var url *url
180182
if curr < -1 && theme.Colored {
181-
origColor := ansiToColorPair(itemColors[-curr-2], colMatch)
183+
ansi := itemColors[-curr-2]
184+
url = ansi.color.url
185+
origColor := ansiToColorPair(ansi, colMatch)
182186
// hl or hl+ only sets the foreground color, so colMatch is the
183187
// combination of either [hl and bg] or [hl+ and bg+].
184188
//
@@ -194,13 +198,14 @@ func (result *Result) colorOffsets(matchOffsets []Offset, theme *tui.ColorTheme,
194198
}
195199
}
196200
colors = append(colors, colorOffset{
197-
offset: [2]int32{int32(start), int32(idx)}, color: color, match: true})
201+
offset: [2]int32{int32(start), int32(idx)}, color: color, match: true, url: url})
198202
} else {
199203
ansi := itemColors[curr-1]
200204
colors = append(colors, colorOffset{
201205
offset: [2]int32{int32(start), int32(idx)},
202206
color: ansiToColorPair(ansi, colBase),
203-
match: false})
207+
match: false,
208+
url: ansi.color.url})
204209
}
205210
}
206211
}

src/terminal.go

+12
Original file line numberDiff line numberDiff line change
@@ -2460,15 +2460,24 @@ func (t *Terminal) printColoredString(window tui.Window, text []rune, offsets []
24602460
var substr string
24612461
var prefixWidth int
24622462
maxOffset := int32(len(text))
2463+
var url *url
24632464
for _, offset := range offsets {
24642465
b := util.Constrain32(offset.offset[0], index, maxOffset)
24652466
e := util.Constrain32(offset.offset[1], index, maxOffset)
2467+
if url != nil && offset.url == nil {
2468+
url = nil
2469+
window.LinkEnd()
2470+
}
24662471

24672472
substr, prefixWidth = t.processTabs(text[index:b], prefixWidth)
24682473
window.CPrint(colBase, substr)
24692474

24702475
if b < e {
24712476
substr, prefixWidth = t.processTabs(text[b:e], prefixWidth)
2477+
if url == nil && offset.url != nil {
2478+
url = offset.url
2479+
window.LinkBegin(url.uri, url.params)
2480+
}
24722481
window.CPrint(offset.color, substr)
24732482
}
24742483

@@ -2477,6 +2486,9 @@ func (t *Terminal) printColoredString(window tui.Window, text []rune, offsets []
24772486
break
24782487
}
24792488
}
2489+
if url != nil {
2490+
window.LinkEnd()
2491+
}
24802492
if index < maxOffset {
24812493
substr, _ = t.processTabs(text[index:], prefixWidth)
24822494
window.CPrint(colBase, substr)

src/tui/light.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -1030,13 +1030,13 @@ func cleanse(str string) string {
10301030
func (w *LightWindow) CPrint(pair ColorPair, text string) {
10311031
_, code := w.csiColor(pair.Fg(), pair.Bg(), pair.Attr())
10321032
w.stderrInternal(cleanse(text), false, code)
1033-
w.csi("m")
1033+
w.csi("0m")
10341034
}
10351035

10361036
func (w *LightWindow) cprint2(fg Color, bg Color, attr Attr, text string) {
10371037
hasColors, code := w.csiColor(fg, bg, attr)
10381038
if hasColors {
1039-
defer w.csi("m")
1039+
defer w.csi("0m")
10401040
}
10411041
w.stderrInternal(cleanse(text), false, code)
10421042
}
@@ -1141,7 +1141,7 @@ func (w *LightWindow) CFill(fg Color, bg Color, attr Attr, text string) FillRetu
11411141
bg = w.bg
11421142
}
11431143
if hasColors, resetCode := w.csiColor(fg, bg, attr); hasColors {
1144-
defer w.csi("m")
1144+
defer w.csi("0m")
11451145
return w.fill(text, resetCode)
11461146
}
11471147
return w.fill(text, w.setBg())

src/tui/tcell.go

+12-7
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,16 @@ func (w *TcellWindow) Print(text string) {
604604
w.printString(text, w.normal)
605605
}
606606

607+
func (w *TcellWindow) withUrl(style tcell.Style) tcell.Style {
608+
if w.uri != nil {
609+
style = style.Url(*w.uri)
610+
if md := regexp.MustCompile(`id=([^:]+)`).FindStringSubmatch(*w.params); len(md) > 1 {
611+
style = style.UrlId(md[1])
612+
}
613+
}
614+
return style
615+
}
616+
607617
func (w *TcellWindow) printString(text string, pair ColorPair) {
608618
lx := 0
609619
a := pair.Attr()
@@ -618,6 +628,7 @@ func (w *TcellWindow) printString(text string, pair ColorPair) {
618628
Blink(a&Attr(tcell.AttrBlink) != 0).
619629
Dim(a&Attr(tcell.AttrDim) != 0)
620630
}
631+
style = w.withUrl(style)
621632

622633
gr := uniseg.NewGraphemes(text)
623634
for gr.Next() {
@@ -668,13 +679,7 @@ func (w *TcellWindow) fillString(text string, pair ColorPair) FillReturn {
668679
Underline(a&Attr(tcell.AttrUnderline) != 0).
669680
StrikeThrough(a&Attr(tcell.AttrStrikeThrough) != 0).
670681
Italic(a&Attr(tcell.AttrItalic) != 0)
671-
672-
if w.uri != nil {
673-
style = style.Url(*w.uri)
674-
if md := regexp.MustCompile(`id=([^:]+)`).FindStringSubmatch(*w.params); len(md) > 1 {
675-
style = style.UrlId(md[1])
676-
}
677-
}
682+
style = w.withUrl(style)
678683

679684
gr := uniseg.NewGraphemes(text)
680685
Loop:

0 commit comments

Comments
 (0)