Commit 846b17ebe7
846b17ebe7687ac4d34c9bffc2276da0a38ce28e
parent: cf58aba82d
Verified · cmc ci/build: success
cmc <hello@cleberg.net> · 2026-08-26T22:56:28Z
web: diff syntax highlighting was never applying
chroma scopes every token rule under .chroma, and the per-line markup
has no wrapper of its own, so the token classes matched nothing and each
line inherited one flat colour. The cell is the wrapper now.
That flat colour came from details.difffold .add/.del, meant for the
+N/-M counts in the fold summary but also matching tr.add/tr.del; those
are scoped to the summary.
The old test asserted the token classes existed, which they did — the
new one checks the cell is a chroma wrapper and that the stylesheet has
rules to match.
e2e/diffweb_test.go
+20
| @@ -82,6 +82,15 @@ func TestDiffRendering(t *testing.T) { |
| 82 | 82 | if !strings.Contains(body, "class=\"k\"") && !strings.Contains(body, "class=\"kd\"") { |
| 83 | 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 | 94 | // The +/- markers are CSS, so a copied selection is real source. |
| 86 | 95 | if strings.Contains(body, `<td class="src">+`) { |
| 87 | 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 | 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. |
| 122 | func 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); } |
| 917 | 917 | summary .fpath { font-family: var(--mono); overflow-wrap: anywhere; } |
| 918 | 918 | summary .fpath .was { color: var(--muted); } |
| 919 | 919 | summary .fstat { margin-left: auto; font-family: var(--mono); white-space: nowrap; } |
| 920 | | details.difffold .add { color: var(--ok); } |
| 921 | | details.difffold .del { color: var(--bad); } |
| 920 | details.difffold summary .add { color: var(--ok); } |
| 921 | details.difffold summary .del { color: var(--bad); } |
| 922 | 922 | details.difffold > .none { padding: var(--sp-3); margin: 0; } |
| 923 | 923 | details.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 | 128 | {{else}}<div class="tablewrap"><table class="difftable"> |
| 129 | 129 | {{range .Lines}}{{if eq .Class "hunk"}}<tr class="hunk"><td class="ln" colspan="2"></td><td class="src">{{.Text}}</td></tr> |
| 130 | 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 | 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 | 133 | {{end}}{{end}} |
| 134 | 134 | </table></div>{{end}} |