Skip to content

Commit

Permalink
hyperlinks: add support for optional id parameter
Browse files Browse the repository at this point in the history
OSC8 escape sequences allow for marking up hyperlinks in the terminal.
An optional `id` parameter is defined to allow applications to signal to
the terminal that (potentially) broken character sequences belong to the
same URL and should be treated as if they were connected.

Add support for optional id parameters, set by the application. Update
test case for ti.EnterUrl.

Fixes: gdamore#568
Reference: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda#hover-underlining-and-the-id-parameter
  • Loading branch information
rockorager committed Oct 15, 2022
1 parent 96bb70f commit 5129c3e
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 4 deletions.
21 changes: 21 additions & 0 deletions style.go
Expand Up @@ -27,6 +27,7 @@ type Style struct {
bg Color
attrs AttrMask
url string
urlId string
}

// StyleDefault represents a default style, based upon the context.
Expand All @@ -44,6 +45,7 @@ func (s Style) Foreground(c Color) Style {
bg: s.bg,
attrs: s.attrs,
url: s.url,
urlId: s.urlId,
}
}

Expand All @@ -55,6 +57,7 @@ func (s Style) Background(c Color) Style {
bg: c,
attrs: s.attrs,
url: s.url,
urlId: s.urlId,
}
}

Expand All @@ -71,13 +74,15 @@ func (s Style) setAttrs(attrs AttrMask, on bool) Style {
bg: s.bg,
attrs: s.attrs | attrs,
url: s.url,
urlId: s.urlId,
}
}
return Style{
fg: s.fg,
bg: s.bg,
attrs: s.attrs &^ attrs,
url: s.url,
urlId: s.urlId,
}
}

Expand Down Expand Up @@ -139,6 +144,7 @@ func (s Style) Attributes(attrs AttrMask) Style {
bg: s.bg,
attrs: attrs,
url: s.url,
urlId: s.urlId,
}
}

Expand All @@ -151,5 +157,20 @@ func (s Style) Url(url string) Style {
bg: s.bg,
attrs: s.attrs,
url: url,
urlId: s.urlId,
}
}

// UrlId returns a style with the UrlId set. If the provided UrlId is not empty,
// any marked up Url with this style will be given the UrlId also. If the
// terminal supports it, any tex with the same UrlId will be grouped as if it
// were one Url, even if it spans multiple lines.
func (s Style) UrlId(id string) Style {
return Style{
fg: s.fg,
bg: s.bg,
attrs: s.attrs,
url: s.url,
urlId: "id:" + id,
}
}
7 changes: 5 additions & 2 deletions terminfo/terminfo_test.go
Expand Up @@ -36,11 +36,10 @@ var testTerminfo = &Terminfo{
Mouse: "\x1b[M",
SetCursor: "\x1b[%i%p1%d;%p2%dH",
PadChar: "\x00",
EnterUrl: "\x1b]8;;%p1%s\x1b\\",
EnterUrl: "\x1b]8;%p2%s;%p1%s\x1b\\",
}

func TestTerminfoExpansion(t *testing.T) {

ti := testTerminfo

// Tests %i and basic parameter strings too
Expand Down Expand Up @@ -140,6 +139,10 @@ func TestStringParameter(t *testing.T) {
if s != "\x1b]8;;https://example.org/test\x1b\\" {
t.Errorf("Result string failed: %s", s)
}
s = ti.TParm(ti.EnterUrl, "https://example.org/test", "id:1234")
if s != "\x1b]8;id:1234;https://example.org/test\x1b\\" {
t.Errorf("Result string failed: %s", s)
}
}

func BenchmarkSetFgBg(b *testing.B) {
Expand Down
4 changes: 2 additions & 2 deletions tscreen.go
Expand Up @@ -346,7 +346,7 @@ func (t *tScreen) prepareExtendedOSC() {
t.enterUrl = t.ti.EnterUrl
t.exitUrl = t.ti.ExitUrl
} else if t.ti.Mouse != "" {
t.enterUrl = "\x1b]8;;%p1%s\x1b\\"
t.enterUrl = "\x1b]8;%p2%s;%p1%s\x1b\\"
t.exitUrl = "\x1b]8;;\x1b\\"
}

Expand Down Expand Up @@ -794,7 +794,7 @@ func (t *tScreen) drawCell(x, y int) int {
// URL string can be long, so don't send it unless we really need to
if t.enterUrl != "" && t.curstyle != style {
if style.url != "" {
t.TPuts(ti.TParm(t.enterUrl, style.url))
t.TPuts(ti.TParm(t.enterUrl, style.url, style.urlId))
} else {
t.TPuts(t.exitUrl)
}
Expand Down

0 comments on commit 5129c3e

Please sign in to comment.