package main import ( "bytes" "html" "html/template" "net/http" "os" "path/filepath" "strings" "github.com/sergi/go-diff/diffmatchpatch" ) func diffHandler(w http.ResponseWriter, r *http.Request, name string) { p, err := loadPage(name) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } p.handleTitle(true) p.renderHTML() renderTemplate(w, p.Dir(), "diff", p) } // Diff computes the diff for a page. At this point, renderHTML has already been called so the Name is escaped. func (p *Page) Diff() template.HTML { fp := filepath.FromSlash(p.Name) a := fp + ".md~" t1, err := os.ReadFile(a) if err != nil { return template.HTML("Cannot read " + a + ", so the page is new.") } b := fp + ".md" t2, err := os.ReadFile(b) if err != nil { return template.HTML("Cannot read " + b + ", so the page was deleted.") } dmp := diffmatchpatch.New() diffs := dmp.DiffMain(string(t1), string(t2), false) return template.HTML(diff2html(dmp.DiffCleanupSemantic(diffs))) } func diff2html(diffs []diffmatchpatch.Diff) string { var buf bytes.Buffer for _, item := range diffs { text := strings.ReplaceAll(html.EscapeString(item.Text), "\n", "
") switch item.Type { case diffmatchpatch.DiffInsert: _, _ = buf.WriteString("") _, _ = buf.WriteString(text) _, _ = buf.WriteString("") case diffmatchpatch.DiffDelete: _, _ = buf.WriteString("") _, _ = buf.WriteString(text) _, _ = buf.WriteString("") case diffmatchpatch.DiffEqual: _, _ = buf.WriteString("") _, _ = buf.WriteString(text) _, _ = buf.WriteString("") } } return buf.String() }