feat: render org-mode file links with line numbers (#12496)
This change renders file links in org-mode like `./module.el::20` as a link to the 20th line, for example. It also strips off other search types that are not currently supported in forgejo like regex search to avoid generating invalid URLs. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/12496 Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"html"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"forgejo.org/modules/highlight"
|
||||
@@ -159,6 +160,16 @@ func (r *Writer) resolveLink(node org.Node) string {
|
||||
switch l.Kind() {
|
||||
case "image", "video":
|
||||
base = r.Ctx.Links.ResolveMediaLink(r.Ctx.IsWiki)
|
||||
case "regular":
|
||||
// Convert line search syntax to line links
|
||||
target, search, found := strings.Cut(link, "::")
|
||||
if found {
|
||||
if _, err := strconv.Atoi(search); err == nil {
|
||||
link = target + "#L" + search
|
||||
} else {
|
||||
link = target
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
link = util.URLJoin(base, link)
|
||||
|
||||
@@ -89,6 +89,43 @@ func TestRender_BaseLinks(t *testing.T) {
|
||||
`<p><a href="http://localhost:3000/gogits/gogs/src/branch/main/deep/nested/folder/src">./src/</a></p>`)
|
||||
}
|
||||
|
||||
func TestRender_SearchSuffix(t *testing.T) {
|
||||
setting.AppURL = AppURL
|
||||
setting.AppSubURL = AppSubURL
|
||||
|
||||
test := func(input, expected string) {
|
||||
buffer, err := RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
Links: markup.Links{
|
||||
Base: setting.AppSubURL,
|
||||
BranchPath: "branch/main",
|
||||
},
|
||||
}, input)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
}
|
||||
|
||||
// `::N` line search becomes an `#L<n>` anchor.
|
||||
test("[[./file.el::35][line 35]]",
|
||||
`<p><a href="http://localhost:3000/gogits/gogs/src/branch/main/file.el#L35">line 35</a></p>`)
|
||||
test("[[file:./file.el::35][line 35]]",
|
||||
`<p><a href="http://localhost:3000/gogits/gogs/src/branch/main/file.el#L35">line 35</a></p>`)
|
||||
|
||||
// Other search types are ignored.
|
||||
test("[[./file.org::*Heading][heading]]",
|
||||
`<p><a href="http://localhost:3000/gogits/gogs/src/branch/main/file.org">heading</a></p>`)
|
||||
test("[[./file.org::#custom-id][heading]]",
|
||||
`<p><a href="http://localhost:3000/gogits/gogs/src/branch/main/file.org">heading</a></p>`)
|
||||
test("[[./file.el::/regex/][regex]]",
|
||||
`<p><a href="http://localhost:3000/gogits/gogs/src/branch/main/file.el">regex</a></p>`)
|
||||
test("[[file:./file.el::][no search]]",
|
||||
`<p><a href="http://localhost:3000/gogits/gogs/src/branch/main/file.el">no search</a></p>`)
|
||||
|
||||
// Absolute URLs that happen to contain `::` are unchanged.
|
||||
test("[[https://example.com/foo::35][ext]]",
|
||||
`<p><a href="https://example.com/foo::35">ext</a></p>`)
|
||||
}
|
||||
|
||||
func TestRender_Media(t *testing.T) {
|
||||
setting.AppURL = AppURL
|
||||
setting.AppSubURL = AppSubURL
|
||||
|
||||
Reference in New Issue
Block a user