From 4dffded6c0079bd179476c85a2603112368d61fb Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 10 Jul 2021 12:22:48 +0200 Subject: [PATCH] Add support for hyperlink OSC --- style.go | 58 +++++++++++++------- terminfo/f/foot/foot.go | 118 +++++++++++++++++++++------------------- terminfo/terminfo.go | 6 ++ tscreen.go | 5 ++ 4 files changed, 110 insertions(+), 77 deletions(-) diff --git a/style.go b/style.go index 8359e28c..e63fdffc 100644 --- a/style.go +++ b/style.go @@ -23,9 +23,10 @@ package tcell // // To use Style, just declare a variable of its type. type Style struct { - fg Color - bg Color - attrs AttrMask + fg Color + bg Color + attrs AttrMask + hyperlink string } // StyleDefault represents a default style, based upon the context. @@ -39,9 +40,10 @@ var styleInvalid = Style{attrs: AttrInvalid} // as requested. ColorDefault can be used to select the global default. func (s Style) Foreground(c Color) Style { return Style{ - fg: c, - bg: s.bg, - attrs: s.attrs, + fg: c, + bg: s.bg, + attrs: s.attrs, + hyperlink: s.hyperlink, } } @@ -49,9 +51,10 @@ func (s Style) Foreground(c Color) Style { // as requested. ColorDefault can be used to select the global default. func (s Style) Background(c Color) Style { return Style{ - fg: s.fg, - bg: c, - attrs: s.attrs, + fg: s.fg, + bg: c, + attrs: s.attrs, + hyperlink: s.hyperlink, } } @@ -64,23 +67,26 @@ func (s Style) Decompose() (fg Color, bg Color, attr AttrMask) { func (s Style) setAttrs(attrs AttrMask, on bool) Style { if on { return Style{ - fg: s.fg, - bg: s.bg, - attrs: s.attrs | attrs, + fg: s.fg, + bg: s.bg, + attrs: s.attrs | attrs, + hyperlink: s.hyperlink, } } return Style{ - fg: s.fg, - bg: s.bg, - attrs: s.attrs &^ attrs, + fg: s.fg, + bg: s.bg, + attrs: s.attrs &^ attrs, + hyperlink: s.hyperlink, } } // Normal returns the style with all attributes disabled. func (s Style) Normal() Style { return Style{ - fg: s.fg, - bg: s.bg, + fg: s.fg, + bg: s.bg, + hyperlink: s.hyperlink, } } @@ -130,8 +136,20 @@ func (s Style) StrikeThrough(on bool) Style { // specified. func (s Style) Attributes(attrs AttrMask) Style { return Style{ - fg: s.fg, - bg: s.bg, - attrs: attrs, + fg: s.fg, + bg: s.bg, + attrs: attrs, + hyperlink: s.hyperlink, + } +} + +// Hyperlink returns a new style based on s, with its hyperlink set to the +// specified URL. An empty string disables the hyperlink. +func (s Style) Hyperlink(url string) Style { + return Style{ + fg: s.fg, + bg: s.bg, + attrs: s.attrs, + hyperlink: url, } } diff --git a/terminfo/f/foot/foot.go b/terminfo/f/foot/foot.go index fb734cbd..af4b96a8 100644 --- a/terminfo/f/foot/foot.go +++ b/terminfo/f/foot/foot.go @@ -5,65 +5,69 @@ package foot import "github.com/gdamore/tcell/v2/terminfo" func init() { + const stringTerminator = "\x1b\\" + const hyperlink = "\x1b]8;;" // foot terminal emulator terminfo.AddTerminfo(&terminfo.Terminfo{ - Name: "foot", - Columns: 80, - Lines: 24, - Colors: 256, - Bell: "\a", - Clear: "\x1b[H\x1b[2J", - EnterCA: "\x1b[?1049h\x1b[22;0;0t", - ExitCA: "\x1b[?1049l\x1b[23;0;0t", - ShowCursor: "\x1b[?12l\x1b[?25h", - HideCursor: "\x1b[?25l", - AttrOff: "\x1b(B\x1b[m", - Underline: "\x1b[4m", - Bold: "\x1b[1m", - Dim: "\x1b[2m", - Italic: "\x1b[3m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - EnterKeypad: "\x1b[?1h\x1b=", - ExitKeypad: "\x1b[?1l\x1b>", - SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38:5:%p1%d%;m", - SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48:5:%p1%d%;m", - SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38:5:%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48:5:%p2%d%;m", - ResetFgBg: "\x1b[39;49m", - AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", - EnterAcs: "\x1b(0", - ExitAcs: "\x1b(B", - StrikeThrough: "\x1b[9m", - Mouse: "\x1b[M", - SetCursor: "\x1b[%i%p1%d;%p2%dH", - CursorBack1: "\b", - CursorUp1: "\x1b[A", - KeyUp: "\x1bOA", - KeyDown: "\x1bOB", - KeyRight: "\x1bOC", - KeyLeft: "\x1bOD", - KeyInsert: "\x1b[2~", - KeyDelete: "\x1b[3~", - KeyBackspace: "\u007f", - KeyHome: "\x1bOH", - KeyEnd: "\x1bOF", - KeyPgUp: "\x1b[5~", - KeyPgDn: "\x1b[6~", - KeyF1: "\x1bOP", - KeyF2: "\x1bOQ", - KeyF3: "\x1bOR", - KeyF4: "\x1bOS", - KeyF5: "\x1b[15~", - KeyF6: "\x1b[17~", - KeyF7: "\x1b[18~", - KeyF8: "\x1b[19~", - KeyF9: "\x1b[20~", - KeyF10: "\x1b[21~", - KeyF11: "\x1b[23~", - KeyF12: "\x1b[24~", - KeyBacktab: "\x1b[Z", - Modifiers: 1, - AutoMargin: true, + Name: "foot", + Columns: 80, + Lines: 24, + Colors: 256, + Bell: "\a", + Clear: "\x1b[H\x1b[2J", + EnterCA: "\x1b[?1049h\x1b[22;0;0t", + ExitCA: "\x1b[?1049l\x1b[23;0;0t", + ShowCursor: "\x1b[?12l\x1b[?25h", + HideCursor: "\x1b[?25l", + AttrOff: "\x1b(B\x1b[m", + Underline: "\x1b[4m", + Bold: "\x1b[1m", + Dim: "\x1b[2m", + Italic: "\x1b[3m", + Blink: "\x1b[5m", + Reverse: "\x1b[7m", + EnterKeypad: "\x1b[?1h\x1b=", + ExitKeypad: "\x1b[?1l\x1b>", + SetFg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38:5:%p1%d%;m", + SetBg: "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48:5:%p1%d%;m", + SetFgBg: "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38:5:%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48:5:%p2%d%;m", + ResetFgBg: "\x1b[39;49m", + AltChars: "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + EnterAcs: "\x1b(0", + ExitAcs: "\x1b(B", + StrikeThrough: "\x1b[9m", + Mouse: "\x1b[M", + SetCursor: "\x1b[%i%p1%d;%p2%dH", + CursorBack1: "\b", + CursorUp1: "\x1b[A", + KeyUp: "\x1bOA", + KeyDown: "\x1bOB", + KeyRight: "\x1bOC", + KeyLeft: "\x1bOD", + KeyInsert: "\x1b[2~", + KeyDelete: "\x1b[3~", + KeyBackspace: "\u007f", + KeyHome: "\x1bOH", + KeyEnd: "\x1bOF", + KeyPgUp: "\x1b[5~", + KeyPgDn: "\x1b[6~", + KeyF1: "\x1bOP", + KeyF2: "\x1bOQ", + KeyF3: "\x1bOR", + KeyF4: "\x1bOS", + KeyF5: "\x1b[15~", + KeyF6: "\x1b[17~", + KeyF7: "\x1b[18~", + KeyF8: "\x1b[19~", + KeyF9: "\x1b[20~", + KeyF10: "\x1b[21~", + KeyF11: "\x1b[23~", + KeyF12: "\x1b[24~", + KeyBacktab: "\x1b[Z", + StartHyperlink: hyperlink + "%s" + stringTerminator, + EndHyperlink: hyperlink + stringTerminator, + Modifiers: 1, + AutoMargin: true, }) } diff --git a/terminfo/terminfo.go b/terminfo/terminfo.go index 193e06e3..5a86e43c 100644 --- a/terminfo/terminfo.go +++ b/terminfo/terminfo.go @@ -227,6 +227,8 @@ type Terminfo struct { CursorSteadyUnderline string CursorBlinkingBar string CursorSteadyBar string + StartHyperlink string + EndHyperlink string } const ( @@ -726,6 +728,10 @@ func (t *Terminfo) TColor(fi, bi int) string { return rv } +func (t *Terminfo) TStartHyperlink(url string) string { + return fmt.Sprintf(t.StartHyperlink, url) +} + var ( dblock sync.Mutex terminfos = make(map[string]*Terminfo) diff --git a/tscreen.go b/tscreen.go index 2c161cc1..fe1be838 100644 --- a/tscreen.go +++ b/tscreen.go @@ -749,6 +749,11 @@ func (t *tScreen) drawCell(x, y int) int { if attrs&AttrStrikeThrough != 0 { t.TPuts(ti.StrikeThrough) } + if style.hyperlink != "" && ti.StartHyperlink != "" { + t.TPuts(ti.TStartHyperlink(style.hyperlink)) + } else if t.curstyle.hyperlink != "" && ti.StartHyperlink != "" { + t.TPuts(ti.EndHyperlink) + } t.curstyle = style } // now emit runes - taking care to not overrun width with a