commit cd28b24

delthas  ·  2021-10-31 18:10:39 +0000 UTC
parent 9336f97
Mark hyperlinks with the OSC hyperlink terminal escape

This makes multi-line links properly clickable.

See: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
4 files changed,  +88, -3
M go.mod
M go.sum
M go.mod
+3, -0
1@@ -8,4 +8,7 @@ require (
2 	golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf
3 	golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6
4 	gopkg.in/yaml.v2 v2.3.0
5+	mvdan.cc/xurls/v2 v2.3.0
6 )
7+
8+replace github.com/gdamore/tcell/v2 => github.com/hhirtz/tcell/v2 v2.3.12-0.20210807133752-5d743c3ab0c9
M go.sum
+14, -3
 1@@ -1,13 +1,20 @@
 2 github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
 3 github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
 4-github.com/gdamore/tcell/v2 v2.3.11 h1:ECO6WqHGbKZ3HrSL7bG/zArMCmLaNr5vcjjMVnLHpzc=
 5-github.com/gdamore/tcell/v2 v2.3.11/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
 6+github.com/hhirtz/tcell/v2 v2.3.12-0.20210807133752-5d743c3ab0c9 h1:YE0ZsDHfDGR0MeB6YLSGW8tjoxOXZKX3XbB0ytGDX4M=
 7+github.com/hhirtz/tcell/v2 v2.3.12-0.20210807133752-5d743c3ab0c9/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
 8+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 9+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
10+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
11+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
12+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
13 github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
14 github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
15 github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
16 github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
17+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
18 github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
19 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
20+github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
21 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
22 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
23 golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
24@@ -16,7 +23,11 @@ golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
25 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
26 golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 h1:Vv0JUPWTyeqUq42B2WJ1FeIDjjvGKoA2Ss+Ts0lAVbs=
27 golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
28-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
29 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
30+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
31+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
32+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
33 gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
34 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
35+mvdan.cc/xurls/v2 v2.3.0 h1:59Olnbt67UKpxF1EwVBopJvkSUBmgtb468E4GVWIZ1I=
36+mvdan.cc/xurls/v2 v2.3.0/go.mod h1:AjuTy7gEiUArFMjgBBDU4SMxlfUYsRokpJQgNWOt3e4=
+6, -0
 1@@ -302,6 +302,10 @@ func (bs *BufferList) AddLine(netID, title string, notify NotifyType, line Line)
 2 	n := len(b.lines)
 3 	line.At = line.At.UTC()
 4 
 5+	if !line.Mergeable {
 6+		line.Body = line.Body.ParseURLs()
 7+	}
 8+
 9 	if line.Mergeable && n != 0 && b.lines[n-1].Mergeable {
10 		l := &b.lines[n-1]
11 		newBody := new(StyledStringBuilder)
12@@ -338,9 +342,11 @@ func (bs *BufferList) AddLines(netID, title string, before, after []Line) {
13 	b := &bs.list[idx]
14 
15 	for i := 0; i < len(before); i++ {
16+		before[i].Body = before[i].Body.ParseURLs()
17 		before[i].computeSplitPoints()
18 	}
19 	for i := 0; i < len(after); i++ {
20+		after[i].Body = after[i].Body.ParseURLs()
21 		after[i].computeSplitPoints()
22 	}
23 
+65, -0
 1@@ -6,6 +6,8 @@ import (
 2 	"strings"
 3 	"unicode/utf8"
 4 
 5+	"mvdan.cc/xurls/v2"
 6+
 7 	"github.com/gdamore/tcell/v2"
 8 	"github.com/mattn/go-runewidth"
 9 )
10@@ -115,6 +117,69 @@ func (s StyledString) Truncate(w int, tail StyledString) StyledString {
11 	return sb.StyledString()
12 }
13 
14+var urlRegex = xurls.Relaxed()
15+
16+func (s StyledString) ParseURLs() StyledString {
17+	styles := make([]rangedStyle, 0, len(s.styles))
18+
19+	urls := urlRegex.FindAllStringIndex(s.string, -1)
20+	j := 0
21+	lastStyle := rangedStyle{
22+		Start: -1,
23+		Style: tcell.StyleDefault,
24+	}
25+	for i := 0; i < len(urls); i++ {
26+		u := urls[i]
27+		ub, ue := u[0], u[1]
28+		link := s.string[u[0]:u[1]] + "?a=b"
29+		// find last style starting before or at url begin
30+		for ; j < len(s.styles); j++ {
31+			st := s.styles[j]
32+			if st.Start > ub {
33+				break
34+			}
35+			if st.Start == ub {
36+				// a style already starts at this position, edit it
37+				lastStyle.Style = lastStyle.Style.Hyperlink(link)
38+			}
39+			lastStyle = st
40+			styles = append(styles, st)
41+		}
42+		if lastStyle.Start != ub {
43+			// no style existed at this position, add one from the last style
44+			styles = append(styles, rangedStyle{
45+				Start: ub,
46+				Style: lastStyle.Style.Hyperlink(link),
47+			})
48+		}
49+		// find last style starting before or at url end
50+		for ; j < len(s.styles); j++ {
51+			st := s.styles[j]
52+			if st.Start > ue {
53+				break
54+			}
55+			if st.Start < ue {
56+				st.Style = st.Style.Hyperlink(link)
57+			}
58+			lastStyle = st
59+			styles = append(styles, st)
60+		}
61+		if lastStyle.Start != ue {
62+			// no style existed at this position, add one from the last style without the hyperlink
63+			styles = append(styles, rangedStyle{
64+				Start: ue,
65+				Style: lastStyle.Style.Hyperlink(""),
66+			})
67+		}
68+	}
69+	styles = append(styles, s.styles[j:]...)
70+
71+	return StyledString{
72+		string: s.string,
73+		styles: styles,
74+	}
75+}
76+
77 func isDigit(c byte) bool {
78 	return '0' <= c && c <= '9'
79 }