mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-20 20:07:03 +01:00
Merge pull request 'Fix some edge cases with permalink rendering' (#3286) from Mai-Lapyst/forgejo:fix-permalink-rendering-edgecases into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3286 Reviewed-by: oliverpool <oliverpool@noreply.codeberg.org> Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
This commit is contained in:
commit
d029796e1e
3 changed files with 351 additions and 108 deletions
|
@ -35,32 +35,44 @@ type FilePreview struct {
|
|||
isTruncated bool
|
||||
}
|
||||
|
||||
func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Locale) *FilePreview {
|
||||
func NewFilePreviews(ctx *RenderContext, node *html.Node, locale translation.Locale) []*FilePreview {
|
||||
if setting.FilePreviewMaxLines == 0 {
|
||||
// Feature is disabled
|
||||
return nil
|
||||
}
|
||||
|
||||
preview := &FilePreview{}
|
||||
|
||||
m := filePreviewPattern.FindStringSubmatchIndex(node.Data)
|
||||
if m == nil {
|
||||
mAll := filePreviewPattern.FindAllStringSubmatchIndex(node.Data, -1)
|
||||
if mAll == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensure that every group has a match
|
||||
result := make([]*FilePreview, 0)
|
||||
|
||||
for _, m := range mAll {
|
||||
if slices.Contains(m, -1) {
|
||||
return nil
|
||||
continue
|
||||
}
|
||||
|
||||
preview := newFilePreview(ctx, node, locale, m)
|
||||
if preview != nil {
|
||||
result = append(result, preview)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func newFilePreview(ctx *RenderContext, node *html.Node, locale translation.Locale, m []int) *FilePreview {
|
||||
preview := &FilePreview{}
|
||||
|
||||
urlFull := node.Data[m[0]:m[1]]
|
||||
|
||||
// Ensure that we only use links to local repositories
|
||||
if !strings.HasPrefix(urlFull, setting.AppURL+setting.AppSubURL) {
|
||||
if !strings.HasPrefix(urlFull, setting.AppURL) {
|
||||
return nil
|
||||
}
|
||||
|
||||
projPath := strings.TrimSuffix(node.Data[m[2]:m[3]], "/")
|
||||
projPath := strings.TrimPrefix(strings.TrimSuffix(node.Data[m[0]:m[3]], "/"), setting.AppURL)
|
||||
|
||||
commitSha := node.Data[m[4]:m[5]]
|
||||
filePath := node.Data[m[6]:m[7]]
|
||||
|
@ -70,6 +82,10 @@ func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca
|
|||
preview.end = m[1]
|
||||
|
||||
projPathSegments := strings.Split(projPath, "/")
|
||||
if len(projPathSegments) != 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ownerName := projPathSegments[len(projPathSegments)-2]
|
||||
repoName := projPathSegments[len(projPathSegments)-1]
|
||||
|
||||
|
|
|
@ -1063,8 +1063,6 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) {
|
|||
return
|
||||
}
|
||||
|
||||
next := node.NextSibling
|
||||
for node != nil && node != next {
|
||||
locale := translation.NewLocale("en-US")
|
||||
if ctx.Ctx != nil {
|
||||
ctxLocale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale)
|
||||
|
@ -1073,16 +1071,23 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) {
|
|||
}
|
||||
}
|
||||
|
||||
preview := NewFilePreview(ctx, node, locale)
|
||||
if preview == nil {
|
||||
return
|
||||
next := node.NextSibling
|
||||
for node != nil && node != next {
|
||||
previews := NewFilePreviews(ctx, node, locale)
|
||||
if previews == nil {
|
||||
node = node.NextSibling
|
||||
continue
|
||||
}
|
||||
|
||||
offset := 0
|
||||
for _, preview := range previews {
|
||||
previewNode := preview.CreateHTML(locale)
|
||||
|
||||
// Specialized version of replaceContent, so the parent paragraph element is not destroyed from our div
|
||||
before := node.Data[:preview.start]
|
||||
after := node.Data[preview.end:]
|
||||
before := node.Data[:(preview.start - offset)]
|
||||
after := node.Data[(preview.end - offset):]
|
||||
afterPrefix := "<p>"
|
||||
offset = preview.end - len(afterPrefix)
|
||||
node.Data = before
|
||||
nextSibling := node.NextSibling
|
||||
node.Parent.InsertBefore(&html.Node{
|
||||
|
@ -1090,10 +1095,13 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) {
|
|||
Data: "</p>",
|
||||
}, nextSibling)
|
||||
node.Parent.InsertBefore(previewNode, nextSibling)
|
||||
node.Parent.InsertBefore(&html.Node{
|
||||
afterNode := &html.Node{
|
||||
Type: html.RawNode,
|
||||
Data: "<p>" + after,
|
||||
}, nextSibling)
|
||||
Data: afterPrefix + after,
|
||||
}
|
||||
node.Parent.InsertBefore(afterNode, nextSibling)
|
||||
node = afterNode
|
||||
}
|
||||
|
||||
node = node.NextSibling
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
|
@ -680,9 +681,9 @@ func TestIssue18471(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRender_FilePreview(t *testing.T) {
|
||||
setting.StaticRootPath = "../../"
|
||||
setting.Names = []string{"english"}
|
||||
setting.Langs = []string{"en-US"}
|
||||
defer test.MockVariableValue(&setting.StaticRootPath, "../../")()
|
||||
defer test.MockVariableValue(&setting.Names, []string{"english"})()
|
||||
defer test.MockVariableValue(&setting.Langs, []string{"en-US"})()
|
||||
translation.InitLocales(context.Background())
|
||||
|
||||
setting.AppURL = markup.TestAppURL
|
||||
|
@ -705,7 +706,7 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
sha := "190d9492934af498c3f669d6a2431dc5459e5b20"
|
||||
commitFilePreview := util.URLJoin(markup.TestRepoURL, "src", "commit", sha, "path", "to", "file.go") + "#L2-L3"
|
||||
|
||||
test := func(input, expected string, metas map[string]string) {
|
||||
testRender := func(input, expected string, metas map[string]string) {
|
||||
buffer, err := markup.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
RelativePath: ".md",
|
||||
|
@ -715,7 +716,8 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
}
|
||||
|
||||
test(
|
||||
t.Run("single", func(t *testing.T) {
|
||||
testRender(
|
||||
commitFilePreview,
|
||||
`<p></p>`+
|
||||
`<div class="file-preview-box">`+
|
||||
|
@ -745,8 +747,10 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
`<p></p>`,
|
||||
localMetas,
|
||||
)
|
||||
})
|
||||
|
||||
test(
|
||||
t.Run("cross-repo", func(t *testing.T) {
|
||||
testRender(
|
||||
commitFilePreview,
|
||||
`<p></p>`+
|
||||
`<div class="file-preview-box">`+
|
||||
|
@ -780,4 +784,219 @@ func TestRender_FilePreview(t *testing.T) {
|
|||
"repo": "gogs2",
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("AppSubURL", func(t *testing.T) {
|
||||
urlWithSub := util.URLJoin(markup.TestAppURL, "sub", markup.TestOrgRepo, "src", "commit", sha, "path", "to", "file.go") + "#L2-L3"
|
||||
|
||||
testRender(
|
||||
urlWithSub,
|
||||
`<p><a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" rel="nofollow"><code>190d949293/path/to/file.go (L2-L3)</code></a></p>`,
|
||||
localMetas,
|
||||
)
|
||||
|
||||
defer test.MockVariableValue(&setting.AppURL, markup.TestAppURL+"sub/")()
|
||||
defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
|
||||
|
||||
testRender(
|
||||
urlWithSub,
|
||||
`<p></p>`+
|
||||
`<div class="file-preview-box">`+
|
||||
`<div class="header">`+
|
||||
`<div>`+
|
||||
`<a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
`<div class="ui table">`+
|
||||
`<table class="file-preview">`+
|
||||
`<tbody>`+
|
||||
`<tr>`+
|
||||
`<td class="lines-num"><span data-line-number="2"></span></td>`+
|
||||
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
|
||||
`</tr>`+
|
||||
`<tr>`+
|
||||
`<td class="lines-num"><span data-line-number="3"></span></td>`+
|
||||
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
|
||||
`</tr>`+
|
||||
`</tbody>`+
|
||||
`</table>`+
|
||||
`</div>`+
|
||||
`</div>`+
|
||||
`<p></p>`,
|
||||
localMetas,
|
||||
)
|
||||
|
||||
testRender(
|
||||
"first without sub "+commitFilePreview+" second "+urlWithSub,
|
||||
`<p>first without sub <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" rel="nofollow"><code>190d949293/path/to/file.go (L2-L3)</code></a> second </p>`+
|
||||
`<div class="file-preview-box">`+
|
||||
`<div class="header">`+
|
||||
`<div>`+
|
||||
`<a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/sub/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
`<div class="ui table">`+
|
||||
`<table class="file-preview">`+
|
||||
`<tbody>`+
|
||||
`<tr>`+
|
||||
`<td class="lines-num"><span data-line-number="2"></span></td>`+
|
||||
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
|
||||
`</tr>`+
|
||||
`<tr>`+
|
||||
`<td class="lines-num"><span data-line-number="3"></span></td>`+
|
||||
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
|
||||
`</tr>`+
|
||||
`</tbody>`+
|
||||
`</table>`+
|
||||
`</div>`+
|
||||
`</div>`+
|
||||
`<p></p>`,
|
||||
localMetas,
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("multiples", func(t *testing.T) {
|
||||
testRender(
|
||||
"first "+commitFilePreview+" second "+commitFilePreview,
|
||||
`<p>first </p>`+
|
||||
`<div class="file-preview-box">`+
|
||||
`<div class="header">`+
|
||||
`<div>`+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
`<div class="ui table">`+
|
||||
`<table class="file-preview">`+
|
||||
`<tbody>`+
|
||||
`<tr>`+
|
||||
`<td class="lines-num"><span data-line-number="2"></span></td>`+
|
||||
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
|
||||
`</tr>`+
|
||||
`<tr>`+
|
||||
`<td class="lines-num"><span data-line-number="3"></span></td>`+
|
||||
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
|
||||
`</tr>`+
|
||||
`</tbody>`+
|
||||
`</table>`+
|
||||
`</div>`+
|
||||
`</div>`+
|
||||
`<p> second </p>`+
|
||||
`<div class="file-preview-box">`+
|
||||
`<div class="header">`+
|
||||
`<div>`+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
`<div class="ui table">`+
|
||||
`<table class="file-preview">`+
|
||||
`<tbody>`+
|
||||
`<tr>`+
|
||||
`<td class="lines-num"><span data-line-number="2"></span></td>`+
|
||||
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
|
||||
`</tr>`+
|
||||
`<tr>`+
|
||||
`<td class="lines-num"><span data-line-number="3"></span></td>`+
|
||||
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
|
||||
`</tr>`+
|
||||
`</tbody>`+
|
||||
`</table>`+
|
||||
`</div>`+
|
||||
`</div>`+
|
||||
`<p></p>`,
|
||||
localMetas,
|
||||
)
|
||||
|
||||
testRender(
|
||||
"first "+commitFilePreview+" second "+commitFilePreview+" third "+commitFilePreview,
|
||||
`<p>first </p>`+
|
||||
`<div class="file-preview-box">`+
|
||||
`<div class="header">`+
|
||||
`<div>`+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
`<div class="ui table">`+
|
||||
`<table class="file-preview">`+
|
||||
`<tbody>`+
|
||||
`<tr>`+
|
||||
`<td class="lines-num"><span data-line-number="2"></span></td>`+
|
||||
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
|
||||
`</tr>`+
|
||||
`<tr>`+
|
||||
`<td class="lines-num"><span data-line-number="3"></span></td>`+
|
||||
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
|
||||
`</tr>`+
|
||||
`</tbody>`+
|
||||
`</table>`+
|
||||
`</div>`+
|
||||
`</div>`+
|
||||
`<p> second </p>`+
|
||||
`<div class="file-preview-box">`+
|
||||
`<div class="header">`+
|
||||
`<div>`+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
`<div class="ui table">`+
|
||||
`<table class="file-preview">`+
|
||||
`<tbody>`+
|
||||
`<tr>`+
|
||||
`<td class="lines-num"><span data-line-number="2"></span></td>`+
|
||||
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
|
||||
`</tr>`+
|
||||
`<tr>`+
|
||||
`<td class="lines-num"><span data-line-number="3"></span></td>`+
|
||||
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
|
||||
`</tr>`+
|
||||
`</tbody>`+
|
||||
`</table>`+
|
||||
`</div>`+
|
||||
`</div>`+
|
||||
`<p> third </p>`+
|
||||
`<div class="file-preview-box">`+
|
||||
`<div class="header">`+
|
||||
`<div>`+
|
||||
`<a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20/path/to/file.go#L2-L3" class="muted" rel="nofollow">path/to/file.go</a>`+
|
||||
`</div>`+
|
||||
`<span class="text small grey">`+
|
||||
`Lines 2 to 3 in <a href="http://localhost:3000/gogits/gogs/src/commit/190d9492934af498c3f669d6a2431dc5459e5b20" class="text black" rel="nofollow">190d949</a>`+
|
||||
`</span>`+
|
||||
`</div>`+
|
||||
`<div class="ui table">`+
|
||||
`<table class="file-preview">`+
|
||||
`<tbody>`+
|
||||
`<tr>`+
|
||||
`<td class="lines-num"><span data-line-number="2"></span></td>`+
|
||||
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">B</span>`+"\n"+`</code></td>`+
|
||||
`</tr>`+
|
||||
`<tr>`+
|
||||
`<td class="lines-num"><span data-line-number="3"></span></td>`+
|
||||
`<td class="lines-code chroma"><code class="code-inner"><span class="nx">C</span>`+"\n"+`</code></td>`+
|
||||
`</tr>`+
|
||||
`</tbody>`+
|
||||
`</table>`+
|
||||
`</div>`+
|
||||
`</div>`+
|
||||
`<p></p>`,
|
||||
localMetas,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue