Skip to content

Commit 393cf89

Browse files
authored
Merge pull request #65 from jesseduffield/auto-render-hyper-links
Add AutoRenderHyperLinks functionality to View
2 parents bfab49c + 93a6890 commit 393cf89

File tree

1 file changed

+61
-0
lines changed

1 file changed

+61
-0
lines changed

view.go

+61
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,10 @@ type View struct {
183183
// if true, the user can scroll all the way past the last item until it appears at the top of the view
184184
CanScrollPastBottom bool
185185

186+
// if true, the view will automatically recognize https: URLs in the content written to it and render
187+
// them as hyperlinks
188+
AutoRenderHyperLinks bool
189+
186190
// if true, the view will underline hyperlinks only when the cursor is on
187191
// them; otherwise, they will always be underlined
188192
UnderlineHyperLinksOnlyOnHover bool
@@ -780,6 +784,7 @@ func (v *View) writeRunes(p []rune) {
780784
for _, r := range p {
781785
switch r {
782786
case '\n':
787+
v.autoRenderHyperlinksInCurrentLine()
783788
if c, ok := v.readCell(v.wx+1, v.wy); !ok || c.chr == 0 {
784789
v.writeCells(v.wx, v.wy, []cell{{
785790
chr: 0,
@@ -793,6 +798,7 @@ func (v *View) writeRunes(p []rune) {
793798
v.lines = append(v.lines, nil)
794799
}
795800
case '\r':
801+
v.autoRenderHyperlinksInCurrentLine()
796802
if c, ok := v.readCell(v.wx, v.wy); !ok || c.chr == 0 {
797803
v.writeCells(v.wx, v.wy, []cell{{
798804
chr: 0,
@@ -829,6 +835,61 @@ func (v *View) writeString(s string) {
829835
v.writeRunes([]rune(s))
830836
}
831837

838+
func findSubstring(line []cell, substringToFind []rune) int {
839+
for i := 0; i < len(line)-len(substringToFind); i++ {
840+
for j := 0; j < len(substringToFind); j++ {
841+
if line[i+j].chr != substringToFind[j] {
842+
break
843+
}
844+
if j == len(substringToFind)-1 {
845+
return i
846+
}
847+
}
848+
}
849+
return -1
850+
}
851+
852+
func (v *View) autoRenderHyperlinksInCurrentLine() {
853+
if !v.AutoRenderHyperLinks {
854+
return
855+
}
856+
857+
// We need a heuristic to find the end of a hyperlink. Searching for the
858+
// first character that is not a valid URI character is not quite good
859+
// enough, because in markdown it's common to have a hyperlink followed by a
860+
// ')', so we want to stop there. Hopefully URLs containing ')' are uncommon
861+
// enough that this is not a problem.
862+
lineEndCharacters := map[rune]bool{
863+
'\000': true,
864+
' ': true,
865+
'\n': true,
866+
'>': true,
867+
'"': true,
868+
')': true,
869+
}
870+
line := v.lines[v.wy]
871+
start := 0
872+
for {
873+
linkStart := findSubstring(line[start:], []rune("https://"))
874+
if linkStart == -1 {
875+
break
876+
}
877+
linkStart += start
878+
link := ""
879+
linkEnd := linkStart
880+
for ; linkEnd < len(line); linkEnd++ {
881+
if _, ok := lineEndCharacters[line[linkEnd].chr]; ok {
882+
break
883+
}
884+
link += string(line[linkEnd].chr)
885+
}
886+
for i := linkStart; i < linkEnd; i++ {
887+
v.lines[v.wy][i].hyperlink = link
888+
}
889+
start = linkEnd
890+
}
891+
}
892+
832893
// parseInput parses char by char the input written to the View. It returns nil
833894
// while processing ESC sequences. Otherwise, it returns a cell slice that
834895
// contains the processed data.

0 commit comments

Comments
 (0)