web: diff syntax highlighting was never applying !87

merged merged by cmc on 2026-08-26 22:56 UTC · krz/gitbay:diff-highlight into main

3 files changed, +23 −3

Layout: unified · split

e2e/diffweb_test.go +20
@@ -82,6 +82,15 @@ func TestDiffRendering(t *testing.T) {
82 if !strings.Contains(body, "class=\"k\"") && !strings.Contains(body, "class=\"kd\"") { 82 if !strings.Contains(body, "class=\"k\"") && !strings.Contains(body, "class=\"kd\"") {
83 t.Error("diff content is not syntax highlighted") 83 t.Error("diff content is not syntax highlighted")
84 } 84 }
85 // Token classes alone prove nothing: chroma scopes every colour rule
86 // under .chroma, and the per-line markup has no wrapper of its own, so
87 // the cell has to be that wrapper or the classes are inert.
88 if !strings.Contains(body, `<td class="src chroma">`) {
89 t.Error("diff cell is not a chroma wrapper, so token classes go unstyled")
90 }
91 if !strings.Contains(stylesheet(t, inst), ".chroma .k") {
92 t.Error("stylesheet has no chroma token rules to match")
93 }
85 // The +/- markers are CSS, so a copied selection is real source. 94 // The +/- markers are CSS, so a copied selection is real source.
86 if strings.Contains(body, `<td class="src">+`) { 95 if strings.Contains(body, `<td class="src">+`) {
87 t.Error("diff markers are in the markup, not the stylesheet") 96 t.Error("diff markers are in the markup, not the stylesheet")
@@ -107,3 +116,14 @@ func jsonField(blob, name string) string {
107 } 116 }
108 return rest[:j] 117 return rest[:j]
109} 118}
119
120// stylesheet fetches the served CSS, so a test can check that a rule the
121// markup depends on actually exists.
122func stylesheet(t *testing.T, inst *instance) string {
123 t.Helper()
124 status, body := inst.get(t, "/static/style.css")
125 if status != 200 {
126 t.Fatalf("stylesheet: %d", status)
127 }
128 return body
129}
internal/web/static/style.css +2 −2
@@ -917,8 +917,8 @@ details.difffold summary:hover { background: var(--fill-subtle); }
917summary .fpath { font-family: var(--mono); overflow-wrap: anywhere; } 917summary .fpath { font-family: var(--mono); overflow-wrap: anywhere; }
918summary .fpath .was { color: var(--muted); } 918summary .fpath .was { color: var(--muted); }
919summary .fstat { margin-left: auto; font-family: var(--mono); white-space: nowrap; } 919summary .fstat { margin-left: auto; font-family: var(--mono); white-space: nowrap; }
920details.difffold .add { color: var(--ok); } 920details.difffold summary .add { color: var(--ok); }
921details.difffold .del { color: var(--bad); } 921details.difffold summary .del { color: var(--bad); }
922details.difffold > .none { padding: var(--sp-3); margin: 0; } 922details.difffold > .none { padding: var(--sp-3); margin: 0; }
923details.difffold .tablewrap { border-radius: 0 0 var(--r-md) var(--r-md); } 923details.difffold .tablewrap { border-radius: 0 0 var(--r-md) var(--r-md); }
924 924
internal/web/templates/layout.html +1 −1
@@ -128,7 +128,7 @@
128 {{else}}<div class="tablewrap"><table class="difftable"> 128 {{else}}<div class="tablewrap"><table class="difftable">
129 {{range .Lines}}{{if eq .Class "hunk"}}<tr class="hunk"><td class="ln" colspan="2"></td><td class="src">{{.Text}}</td></tr> 129 {{range .Lines}}{{if eq .Class "hunk"}}<tr class="hunk"><td class="ln" colspan="2"></td><td class="src">{{.Text}}</td></tr>
130 {{else if eq .Class "meta"}}<tr class="dmeta"><td class="ln" colspan="2"></td><td class="src">{{.Text}}</td></tr> 130 {{else if eq .Class "meta"}}<tr class="dmeta"><td class="ln" colspan="2"></td><td class="src">{{.Text}}</td></tr>
131 {{else}}<tr class="{{.Class}}"><td class="ln">{{if .OldLine}}{{.OldLine}}{{end}}</td><td class="ln">{{if .NewLine}}{{.NewLine}}{{end}}</td><td class="src">{{if .Code}}{{.Code}}{{else}}{{.Content}}{{end}}</td></tr> 131 {{else}}<tr class="{{.Class}}"><td class="ln">{{if .OldLine}}{{.OldLine}}{{end}}</td><td class="ln">{{if .NewLine}}{{.NewLine}}{{end}}</td><td class="src chroma">{{if .Code}}{{.Code}}{{else}}{{.Content}}{{end}}</td></tr>
132 {{end}}{{range .Threads}}<tr class="threadrow"><td colspan="3"><div class="thread{{if .Resolved}} resolved{{end}}">{{if .Resolved}}<p class="threadstate">resolved by {{.Resolved}}</p>{{end}}{{range .Comments}}<p class="commenthead"><strong>{{.Author}}</strong> <span class="when">{{when .CreatedAt}}</span></p><div class="rendered">{{.BodyHTML}}</div>{{end}}{{template "threadact" dict "ID" .ID "Resolved" .Resolved "Base" $base "Can" $can}}</div></td></tr> 132 {{end}}{{range .Threads}}<tr class="threadrow"><td colspan="3"><div class="thread{{if .Resolved}} resolved{{end}}">{{if .Resolved}}<p class="threadstate">resolved by {{.Resolved}}</p>{{end}}{{range .Comments}}<p class="commenthead"><strong>{{.Author}}</strong> <span class="when">{{when .CreatedAt}}</span></p><div class="rendered">{{.BodyHTML}}</div>{{end}}{{template "threadact" dict "ID" .ID "Resolved" .Resolved "Base" $base "Can" $can}}</div></td></tr>
133 {{end}}{{end}} 133 {{end}}{{end}}
134 </table></div>{{end}} 134 </table></div>{{end}}