Auto-link URLs

3e9191c9ed9b130a1136009f3d7515e6b2066758
Alexis Sellier committed ago 1 parent 3192dd09
template.go +25 -0
1 1
package main
2 2
3 3
import (
4 4
	"embed"
5 5
	"fmt"
6 +
	"html"
6 7
	"html/template"
7 8
	"io"
8 9
	"path"
10 +
	"regexp"
9 11
	"strings"
10 12
	"time"
11 13
)
12 14
13 15
//go:embed templates/*.html
170 172
		if depth == 0 {
171 173
			return ""
172 174
		}
173 175
		return fmt.Sprintf("padding-left: %grem", float64(depth)*1.2)
174 176
	},
177 +
	"autolink": func(s string) template.HTML {
178 +
		var b strings.Builder
179 +
		last := 0
180 +
		for _, loc := range urlRe.FindAllStringIndex(s, -1) {
181 +
			// Strip trailing punctuation.
182 +
			end := loc[1]
183 +
			for end > loc[0] && strings.ContainsRune(".,;:!?)]", rune(s[end-1])) {
184 +
				end--
185 +
			}
186 +
			b.WriteString(html.EscapeString(s[last:loc[0]]))
187 +
			u := s[loc[0]:end]
188 +
			b.WriteString(`<a href="`)
189 +
			b.WriteString(html.EscapeString(u))
190 +
			b.WriteString(`">`)
191 +
			b.WriteString(html.EscapeString(u))
192 +
			b.WriteString(`</a>`)
193 +
			last = end
194 +
		}
195 +
		b.WriteString(html.EscapeString(s[last:]))
196 +
		return template.HTML(b.String())
197 +
	},
175 198
}
176 199
200 +
var urlRe = regexp.MustCompile(`https?://[^\s<>"'` + "`" + `\x00-\x1f]+`)
201 +
177 202
func loadTemplates() (*templateSet, error) {
178 203
	layoutContent, err := templateFS.ReadFile("templates/layout.html")
179 204
	if err != nil {
180 205
		return nil, fmt.Errorf("read layout: %w", err)
181 206
	}
templates/home.html +2 -2
47 47
      <table class="blob-code">
48 48
      <tbody>
49 49
      {{range $i, $line := .Data.ActiveBlob.Lines}}
50 50
      <tr id="L{{add $i 1}}">
51 51
        <td class="line-num"><a href="#L{{add $i 1}}">{{add $i 1}}</a></td>
52 -
        <td class="line-code"><pre{{with langClass $.Data.ActiveBlob.Name}} class="{{.}}"{{end}}>{{$line}}</pre></td>
52 +
        <td class="line-code"><pre{{with langClass $.Data.ActiveBlob.Name}} class="{{.}}"{{end}}>{{autolink $line}}</pre></td>
53 53
      </tr>
54 54
      {{end}}
55 55
      </tbody>
56 56
      </table>
57 57
      </div>
60 60
    {{else if .Data.Readme}}
61 61
    <div class="file-view">
62 62
      <div class="file-header">
63 63
        <span class="file-name">{{.Data.Readme.Name}}</span>
64 64
      </div>
65 -
      <pre>{{.Data.Readme.Content}}</pre>
65 +
      <pre>{{autolink .Data.Readme.Content}}</pre>
66 66
    </div>
67 67
    {{end}}
68 68
  </div>
69 69
</div>
70 70