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("\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("\n")
}
}
}
b.WriteString("\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 }