From ea40aa746e6055984577923fdb9d7c47bd59084a Mon Sep 17 00:00:00 2001 From: jolheiser Date: Wed, 4 Jun 2025 10:51:03 -0500 Subject: [PATCH] permalink --- internal/git/git.go | 15 +++++++++++++++ internal/html/repo_file.go | 8 +++++++- internal/html/repo_file.js | 14 +++++++++++--- internal/http/repo.go | 10 ++++++++++ 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/internal/git/git.go b/internal/git/git.go index 72140d8..fcdb9e3 100644 --- a/internal/git/git.go +++ b/internal/git/git.go @@ -127,6 +127,21 @@ func (r Repo) Dir(ref, path string) ([]FileInfo, error) { return fis, nil } +// GetCommitFromRef returns the commit object for a given ref +func (r Repo) GetCommitFromRef(ref string) (*object.Commit, error) { + g, err := r.Git() + if err != nil { + return nil, err + } + + hash, err := g.ResolveRevision(plumbing.Revision(ref)) + if err != nil { + return nil, err + } + + return g.CommitObject(*hash) +} + // FileContent returns the content of a file in the git tree at a given ref/rev func (r Repo) FileContent(ref, file string) (string, error) { t, err := r.Tree(ref) diff --git a/internal/html/repo_file.go b/internal/html/repo_file.go index 6dd3cb1..981121c 100644 --- a/internal/html/repo_file.go +++ b/internal/html/repo_file.go @@ -2,6 +2,7 @@ package html import ( _ "embed" + "fmt" . "maragu.dev/gomponents" . "maragu.dev/gomponents/html" @@ -11,19 +12,24 @@ type RepoFileContext struct { BaseContext RepoHeaderComponentContext RepoBreadcrumbComponentContext - Code string + Code string + Commit string + Path string } //go:embed repo_file.js var repoFileJS string func RepoFileTemplate(rfc RepoFileContext) Node { + permalink := fmt.Sprintf("/%s/tree/%s/%s", rfc.RepoBreadcrumbComponentContext.Repo, rfc.Commit, rfc.Path) return base(rfc.BaseContext, []Node{ repoHeaderComponent(rfc.RepoHeaderComponentContext), Div(Class("mt-2 text-text"), repoBreadcrumbComponent(rfc.RepoBreadcrumbComponentContext), Text(" - "), A(Class("text-text underline decoration-text/50 decoration-dashed hover:decoration-solid"), Href("?raw"), Text("raw")), + Text(" - "), + A(Class("text-text underline decoration-text/50 decoration-dashed hover:decoration-solid"), ID("permalink"), Data("permalink", permalink), Href(permalink), Text("permalink")), Div(Class("code relative"), Raw(rfc.Code), Button(ID("copy"), Class("absolute top-0 right-0 rounded bg-base hover:bg-surface0")), diff --git a/internal/html/repo_file.js b/internal/html/repo_file.js index 42d5a24..9dd234f 100644 --- a/internal/html/repo_file.js +++ b/internal/html/repo_file.js @@ -2,6 +2,7 @@ const lineRe = /#L(\d+)(?:-L(\d+))?/g const $lineLines = document.querySelectorAll(".chroma .lntable .lnt"); const $codeLines = document.querySelectorAll(".chroma .lntable .line"); const $copyButton = document.getElementById('copy'); +const $permalink = document.getElementById('permalink'); const $copyIcon = "📋"; const $copiedIcon = "✅"; let $code = "" @@ -13,9 +14,12 @@ if (0 in results) { start = results[0][1] !== undefined ? parseInt(results[0][1]) : 0; end = results[0][2] !== undefined ? parseInt(results[0][2]) : 0; } -if (start != 0) { +if (start !== 0) { deactivateLines(); activateLines(start, end); + let anchor = `#${start}`; + if (end !== 0) anchor += `-${end}`; + if (anchor !== "") $permalink.href = $permalink.dataset.permalink + anchor; $lineLines[start - 1].scrollIntoView(true); } for (let line of $lineLines) { @@ -27,13 +31,17 @@ for (let line of $lineLines) { if (event.shiftKey) { end = n; anchor = `#L${start}-L${end}`; + } else if (start === n) { + start = 0; + end = 0; } else { start = n; end = 0; anchor = `#L${start}`; } - history.replaceState(null, null, anchor); - activateLines(start, end); + history.replaceState(null, null, window.location.pathname + anchor); + $permalink.href = $permalink.dataset.permalink + anchor; + if (start !== 0) activateLines(start, end); }); } if (navigator.clipboard && navigator.clipboard.writeText) { diff --git a/internal/http/repo.go b/internal/http/repo.go index 5382b7d..c47811a 100644 --- a/internal/http/repo.go +++ b/internal/http/repo.go @@ -92,11 +92,21 @@ func (rh repoHandler) repoFile(w http.ResponseWriter, r *http.Request, repo *git return httperr.Error(err) } + commit := ref + if len(ref) < 40 { + commitObj, err := repo.GetCommitFromRef(ref) + if err == nil { + commit = commitObj.Hash.String() + } + } + if err := html.RepoFileTemplate(html.RepoFileContext{ BaseContext: rh.baseContext(), RepoHeaderComponentContext: rh.repoHeaderContext(repo, r), RepoBreadcrumbComponentContext: rh.repoBreadcrumbContext(repo, r, path), Code: buf.String(), + Commit: commit, + Path: path, }).Render(w); err != nil { return httperr.Error(err) }