all repos — legit @ e108c4e924105ef88e73f0fe45069454b66ccd12

web frontend for git, written in go

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}