From 80be2418ff2615b454c5edf2819861f58eecf271 Mon Sep 17 00:00:00 2001 From: Joe Mou Date: Thu, 3 Sep 2026 01:26:44 -0400 Subject: Anchor blob lines so #L fragments work Wrap each rendered line in a , for both syntax-highlighted and plain-text blobs, and shade the line a fragment targets. --- public/static/style.css | 4 ++++ src/app.test.ts | 23 +++++++++++++++++++++++ views/blob.eta | 6 ++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/public/static/style.css b/public/static/style.css index a83e128..1d277b7 100644 --- a/public/static/style.css +++ b/public/static/style.css @@ -286,6 +286,10 @@ object.pdf { pre.box { /* Instead of scrolling overflow for long lines, grow the box. */ width: fit-content; + + span:target { + background: var(--shade-background); + } } .overview, .release, .wiki { diff --git a/src/app.test.ts b/src/app.test.ts index 3ecebd0..265465e 100644 --- a/src/app.test.ts +++ b/src/app.test.ts @@ -124,6 +124,29 @@ describe("blob view", () => { assert.match(html, /This file cannot be displayed\./); }); + it("should give each syntax-highlighted line an anchor", () => { + const html = eta.render("blob.eta", { + ...blob, + path: "src/app.ts", + htmlLines: ['const x;', "x++;"], + }); + + assert.match( + html, + /const<\/span> x;<\/span>\nx\+\+;<\/span><\/code>/, + ); + }); + + it("should give each plain-text line an anchor", () => { + const html = eta.render("blob.eta", { + ...blob, + path: "notes.txt", + textLines: ["a < b", "c"], + }); + + assert.match(html, /a < b<\/span>\nc<\/span><\/code>/); + }); + it("should offer the raw file when there is nothing to display", () => { const html = eta.render("blob.eta", blob); diff --git a/views/blob.eta b/views/blob.eta index 444508e..be9a57e 100644 --- a/views/blob.eta +++ b/views/blob.eta @@ -22,9 +22,11 @@ <% } else if (it.htmlContent) { %>
<%~ it.htmlContent %>
<% } else if (it.htmlLines) { %> -
<%~ it.htmlLines.join("\n") %>
+
<% for (const [i, line] of it.htmlLines.entries()) {
+    %><%= i ? "\n" : "" %><%~ line %><% } %>
<% } else if (it.textLines) { %> -
<%= it.textLines.join("\n") %>
+
<% for (const [i, line] of it.textLines.entries()) {
+    %><%= i ? "\n" : "" %><%= line %><% } %>
<% } else { %>
This file cannot be displayed.
<% } %> -- cgit v1.3.1