diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go index b9d7b21db0..dd92ca90d0 100644 --- a/modules/markup/orgmode/orgmode.go +++ b/modules/markup/orgmode/orgmode.go @@ -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) diff --git a/modules/markup/orgmode/orgmode_test.go b/modules/markup/orgmode/orgmode_test.go index 723d78e745..2cd4eada22 100644 --- a/modules/markup/orgmode/orgmode_test.go +++ b/modules/markup/orgmode/orgmode_test.go @@ -89,6 +89,43 @@ func TestRender_BaseLinks(t *testing.T) { `
`) } +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