1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
package render
import (
"html/template"
"strings"
"github.com/go-git/go-git/v5/plumbing/format/diff"
"github.com/go-git/go-git/v5/plumbing/object"
)
// Diff renders a go-git commit patch as a class-tagged HTML table: a header row
// per file then added/removed/context lines, each tagged with a CSS class
// (.diff-add/.diff-del/.diff-ctx) defined in style.css. No inline styles, so
// the strict CSP is preserved.
func Diff(p *object.Patch) template.HTML {
var b strings.Builder
b.WriteString(`<table class="diff">` + "\n")
for _, fp := range p.FilePatches() {
from, to := fp.Files()
b.WriteString(`<tr class="file-head"><td colspan="2">`)
b.WriteString(template.HTMLEscapeString(fileHeader(from, to, fp.IsBinary())))
b.WriteString("</td></tr>\n")
if fp.IsBinary() {
continue
}
for _, chunk := range fp.Chunks() {
class, prefix := chunkStyle(chunk.Type())
lines := strings.Split(strings.TrimSuffix(chunk.Content(), "\n"), "\n")
for _, line := range lines {
b.WriteString(`<tr class="` + class + `"><td>` + prefix + `</td><td>`)
b.WriteString(template.HTMLEscapeString(line))
b.WriteString("</td></tr>\n")
}
}
}
b.WriteString("</table>\n")
return template.HTML(b.String())
}
func chunkStyle(op diff.Operation) (class, prefix string) {
switch op {
case diff.Add:
return "diff-add", "+"
case diff.Delete:
return "diff-del", "-"
default:
return "diff-ctx", " "
}
}
func fileHeader(from, to diff.File, binary bool) string {
var name string
switch {
case from == nil && to != nil:
name = "added " + to.Path()
case from != nil && to == nil:
name = "deleted " + from.Path()
case from != nil && to != nil && from.Path() != to.Path():
name = from.Path() + " → " + to.Path()
case to != nil:
name = to.Path()
case from != nil:
name = from.Path()
}
if binary {
name += " (binary)"
}
return name
}
|