From 5129c3efb398707473fa26e55f9e508f0860a6e6 Mon Sep 17 00:00:00 2001 From: Tim Culverhouse Date: Sat, 15 Oct 2022 07:57:10 -0500 Subject: [PATCH] hyperlinks: add support for optional id parameter 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: https://github.com/gdamore/tcell/issues/568 Reference: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda#hover-underlining-and-the-id-parameter --- style.go | 21 +++++++++++++++++++++ terminfo/terminfo_test.go | 7 +++++-- tscreen.go | 4 ++-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/style.go b/style.go index ad4b47f2..bb4d4a84 100644 --- a/style.go +++ b/style.go @@ -27,6 +27,7 @@ type Style struct { bg Color attrs AttrMask url string + urlId string } // StyleDefault represents a default style, based upon the context. @@ -44,6 +45,7 @@ func (s Style) Foreground(c Color) Style { bg: s.bg, attrs: s.attrs, url: s.url, + urlId: s.urlId, } } @@ -55,6 +57,7 @@ func (s Style) Background(c Color) Style { bg: c, attrs: s.attrs, url: s.url, + urlId: s.urlId, } } @@ -71,6 +74,7 @@ func (s Style) setAttrs(attrs AttrMask, on bool) Style { bg: s.bg, attrs: s.attrs | attrs, url: s.url, + urlId: s.urlId, } } return Style{ @@ -78,6 +82,7 @@ func (s Style) setAttrs(attrs AttrMask, on bool) Style { bg: s.bg, attrs: s.attrs &^ attrs, url: s.url, + urlId: s.urlId, } } @@ -139,6 +144,7 @@ func (s Style) Attributes(attrs AttrMask) Style { bg: s.bg, attrs: attrs, url: s.url, + urlId: s.urlId, } } @@ -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, } } diff --git a/terminfo/terminfo_test.go b/terminfo/terminfo_test.go index ae23d14b..962289ca 100644 --- a/terminfo/terminfo_test.go +++ b/terminfo/terminfo_test.go @@ -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 @@ -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) { diff --git a/tscreen.go b/tscreen.go index dcde34ed..c5a16cdb 100644 --- a/tscreen.go +++ b/tscreen.go @@ -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\\" } @@ -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) }