routes/template.go (view raw)
1package routes
2
3import (
4 "bytes"
5 "html/template"
6 "io"
7 "log"
8 "net/http"
9 "path/filepath"
10 "strings"
11
12 "git.icyphox.sh/legit/git"
13 "github.com/alecthomas/chroma/v2/formatters/html"
14 "github.com/alecthomas/chroma/v2/lexers"
15 "github.com/alecthomas/chroma/v2/styles"
16)
17
18func (d *deps) Write404(w http.ResponseWriter) {
19 tpath := filepath.Join(d.c.Dirs.Templates, "*")
20 t := template.Must(template.ParseGlob(tpath))
21 w.WriteHeader(404)
22 if err := t.ExecuteTemplate(w, "404", nil); err != nil {
23 log.Printf("404 template: %s", err)
24 }
25}
26
27func (d *deps) Write500(w http.ResponseWriter) {
28 tpath := filepath.Join(d.c.Dirs.Templates, "*")
29 t := template.Must(template.ParseGlob(tpath))
30 w.WriteHeader(500)
31 if err := t.ExecuteTemplate(w, "500", nil); err != nil {
32 log.Printf("500 template: %s", err)
33 }
34}
35
36func (d *deps) listFiles(files []git.NiceTree, data map[string]any, w http.ResponseWriter) {
37 tpath := filepath.Join(d.c.Dirs.Templates, "*")
38 t := template.Must(template.ParseGlob(tpath))
39
40 data["files"] = files
41 data["meta"] = d.c.Meta
42
43 if err := t.ExecuteTemplate(w, "tree", data); err != nil {
44 log.Println(err)
45 return
46 }
47}
48
49func countLines(r io.Reader) (int, error) {
50 buf := make([]byte, 32*1024)
51 bufLen := 0
52 count := 0
53 nl := []byte{'\n'}
54
55 for {
56 c, err := r.Read(buf)
57 if c > 0 {
58 bufLen += c
59 }
60 count += bytes.Count(buf[:c], nl)
61
62 switch {
63 case err == io.EOF:
64 /* handle last line not having a newline at the end */
65 if bufLen >= 1 && buf[(bufLen-1)%(32*1024)] != '\n' {
66 count++
67 }
68 return count, nil
69 case err != nil:
70 return 0, err
71 }
72 }
73}
74
75func (d *deps) showFileWithHighlight(name, content string, data map[string]any, w http.ResponseWriter) {
76 tpath := filepath.Join(d.c.Dirs.Templates, "*")
77 t := template.Must(template.ParseGlob(tpath))
78
79 lexer := lexers.Get(name)
80 if lexer == nil {
81 lexer = lexers.Get(".txt")
82 }
83
84 style := styles.Get(d.c.Meta.SyntaxHighlight)
85 if style == nil {
86 style = styles.Get("monokailight")
87 }
88
89 formatter := html.New(
90 html.WithLineNumbers(true),
91 html.WithLinkableLineNumbers(true, "L"),
92 )
93
94 iterator, err := lexer.Tokenise(nil, content)
95 if err != nil {
96 d.Write500(w)
97 return
98 }
99
100 var code bytes.Buffer
101 err = formatter.Format(&code, style, iterator)
102 if err != nil {
103 d.Write500(w)
104 return
105 }
106
107 data["content"] = template.HTML(code.String())
108 data["meta"] = d.c.Meta
109 data["chroma"] = true
110
111 if err := t.ExecuteTemplate(w, "file", data); err != nil {
112 log.Println(err)
113 return
114 }
115}
116
117func (d *deps) showFile(content string, data map[string]any, w http.ResponseWriter) {
118 tpath := filepath.Join(d.c.Dirs.Templates, "*")
119 t := template.Must(template.ParseGlob(tpath))
120
121 lc, err := countLines(strings.NewReader(content))
122 if err != nil {
123 // Non-fatal, we'll just skip showing line numbers in the template.
124 log.Printf("counting lines: %s", err)
125 }
126
127 lines := make([]int, lc)
128 if lc > 0 {
129 for i := range lines {
130 lines[i] = i + 1
131 }
132 }
133
134 data["linecount"] = lines
135 data["content"] = content
136 data["meta"] = d.c.Meta
137 data["chroma"] = false
138
139 if err := t.ExecuteTemplate(w, "file", data); err != nil {
140 log.Println(err)
141 return
142 }
143}
144
145func (d *deps) showRaw(content string, w http.ResponseWriter) {
146 w.WriteHeader(http.StatusOK)
147 w.Header().Set("Content-Type", "text/plain")
148 w.Write([]byte(content))
149 return
150}