Skip to content

Commit 14c5462

Browse files
mahlzahnGusted
authored andcommitted
fix(ui): reworked file preview placement towards better HTML validity (go-gitea#9181)
### What? - fixes HTML nodes placement for inline previews - (breaking?) disallows file previews in headings (`<h1>`, …), striked out (`<del>`), `<summary>` and other environments - allows them in `<span>`, `<em>` and `<strong>` environments, but without extra formatting - allows them in `<div>`, `<li>`, `<th>`, `<td>` and `<details>` (not in `<summary>`) environments following the parent’s formatting - improves overall HTML validity, but only to the extend of the direct parent nodes (but not a big issue, as modern browsers tend to ignore invalid HTML and still parse it) - fix go-gitea#9136 See examples of strangely formatted file previews at https://v13.next.forgejo.org/mahlzahn/test/issues/4. ### How is it implemented? For links in `<p>`, `<span>`, `<em>` and `<strong>` parent nodes it inserts the file preview and following text as siblings to the **parent** node. `<em>before LINK after</em>` → `<em>before </em>PREVIEW<em> after</em>` For links in `<div>`, `<li>`, `<th>`, `<td>` and `<details>` parent nodes it inserts the file preview and following text as siblings to the **text** node. `<div>before LINK after</div>` → `<div>before PREVIEW after</div>` Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9181 Reviewed-by: Gusted <[email protected]> Co-authored-by: Robert Wolff <[email protected]> Co-committed-by: Robert Wolff <[email protected]>
1 parent 389b32f commit 14c5462

File tree

2 files changed

+102
-38
lines changed

2 files changed

+102
-38
lines changed

modules/markup/html.go

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,36 +1078,57 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) {
10781078

10791079
next := node.NextSibling
10801080
for node != nil && node != next {
1081+
if node.Parent == nil || node.Parent.Type != html.ElementNode {
1082+
node = node.NextSibling
1083+
continue
1084+
}
10811085
previews := NewFilePreviews(ctx, node, locale)
10821086
if previews == nil {
10831087
node = node.NextSibling
10841088
continue
10851089
}
1086-
10871090
offset := 0
10881091
for _, preview := range previews {
10891092
previewNode := preview.CreateHTML(locale)
10901093

10911094
// Specialized version of replaceContent, so the parent paragraph element is not destroyed from our div
10921095
before := node.Data[:(preview.start - offset)]
10931096
after := node.Data[(preview.end - offset):]
1094-
afterPrefix := "<p>"
1095-
offset = preview.end - len(afterPrefix)
1096-
node.Data = before
1097-
nextSibling := node.NextSibling
1098-
node.Parent.InsertBefore(&html.Node{
1099-
Type: html.RawNode,
1100-
Data: "</p>",
1101-
}, nextSibling)
1102-
node.Parent.InsertBefore(previewNode, nextSibling)
11031097
afterNode := &html.Node{
1104-
Type: html.RawNode,
1105-
Data: afterPrefix + after,
1098+
Type: html.TextNode,
1099+
Data: after,
1100+
}
1101+
matched := true
1102+
switch node.Parent.Data {
1103+
case "div", "li", "td", "th", "details":
1104+
nextSibling := node.NextSibling
1105+
node.Parent.InsertBefore(previewNode, nextSibling)
1106+
node.Parent.InsertBefore(afterNode, nextSibling)
1107+
case "p", "span", "em", "strong":
1108+
nextSibling := node.Parent.NextSibling
1109+
node.Parent.Parent.InsertBefore(previewNode, nextSibling)
1110+
afterPNode := &html.Node{
1111+
Type: html.ElementNode,
1112+
Data: node.Parent.Data,
1113+
Attr: node.Parent.Attr,
1114+
}
1115+
afterPNode.AppendChild(afterNode)
1116+
node.Parent.Parent.InsertBefore(afterPNode, nextSibling)
1117+
siblingNode := node.NextSibling
1118+
if siblingNode != nil {
1119+
node.NextSibling = nil
1120+
siblingNode.PrevSibling = nil
1121+
afterPNode.AppendChild(siblingNode)
1122+
}
1123+
default:
1124+
matched = false
1125+
}
1126+
if matched {
1127+
offset = preview.end
1128+
node.Data = before
1129+
node = afterNode
11061130
}
1107-
node.Parent.InsertBefore(afterNode, nextSibling)
1108-
node = afterNode
11091131
}
1110-
11111132
node = node.NextSibling
11121133
}
11131134
}

modules/markup/html_test.go

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,32 +1181,75 @@ func TestRender_FilePreview(t *testing.T) {
11811181
})
11821182

11831183
commitFileURL = util.URLJoin(markup.TestRepoURL, "src", "commit", "eeb243c3395e1921c5d90e73bd739827251fc99d", "path", "to", "file%20%23.txt")
1184+
commitFileURLFirstLine := commitFileURL + "#L1"
1185+
filePreviewBox := `<div class="file-preview-box">` +
1186+
`<div class="header">` +
1187+
`<div>` +
1188+
`<a href="http://localhost:3000/gogits/gogs/src/commit/eeb243c3395e1921c5d90e73bd739827251fc99d/path/to/file%20%23.txt#L1" class="muted" rel="nofollow">path/to/file #.txt</a>` +
1189+
`</div>` +
1190+
`<span class="text grey">` +
1191+
`Line 1 in <a href="http://localhost:3000/gogits/gogs/src/commit/eeb243c3395e1921c5d90e73bd739827251fc99d" class="text black" rel="nofollow">eeb243c</a>` +
1192+
`</span>` +
1193+
`</div>` +
1194+
`<div class="ui table">` +
1195+
`<table class="file-preview">` +
1196+
`<tbody>` +
1197+
`<tr>` +
1198+
`<td class="lines-num"><span data-line-number="1"></span></td>` +
1199+
`<td class="lines-code chroma"><code class="code-inner">A` + "\n" + `</code></td>` +
1200+
`</tr>` +
1201+
`</tbody>` +
1202+
`</table>` +
1203+
`</div>` +
1204+
`</div>`
1205+
linkRendered := `<a href="` + commitFileURLFirstLine + `" rel="nofollow"><code>eeb243c339/path/to/file%20%23.txt (L1)</code></a>`
11841206

11851207
t.Run("file with strange characters in name", func(t *testing.T) {
11861208
testRender(
1187-
commitFileURL+"#L1",
1188-
`<p></p>`+
1189-
`<div class="file-preview-box">`+
1190-
`<div class="header">`+
1191-
`<div>`+
1192-
`<a href="http://localhost:3000/gogits/gogs/src/commit/eeb243c3395e1921c5d90e73bd739827251fc99d/path/to/file%20%23.txt#L1" class="muted" rel="nofollow">path/to/file #.txt</a>`+
1193-
`</div>`+
1194-
`<span class="text grey">`+
1195-
`Line 1 in <a href="http://localhost:3000/gogits/gogs/src/commit/eeb243c3395e1921c5d90e73bd739827251fc99d" class="text black" rel="nofollow">eeb243c</a>`+
1196-
`</span>`+
1197-
`</div>`+
1198-
`<div class="ui table">`+
1199-
`<table class="file-preview">`+
1200-
`<tbody>`+
1201-
`<tr>`+
1202-
`<td class="lines-num"><span data-line-number="1"></span></td>`+
1203-
`<td class="lines-code chroma"><code class="code-inner">A`+"\n"+`</code></td>`+
1204-
`</tr>`+
1205-
`</tbody>`+
1206-
`</table>`+
1207-
`</div>`+
1208-
`</div>`+
1209-
`<p></p>`,
1209+
commitFileURLFirstLine,
1210+
`<p></p>`+filePreviewBox+`<p></p>`,
1211+
localMetas,
1212+
)
1213+
})
1214+
1215+
t.Run("file preview with stuff before and after", func(t *testing.T) {
1216+
testRender(
1217+
":frog: before"+commitFileURLFirstLine+" :frog: after",
1218+
`<p><span class="emoji" aria-label="frog" data-alias="frog">🐸</span> before</p>`+
1219+
filePreviewBox+
1220+
`<p> <span class="emoji" aria-label="frog" data-alias="frog">🐸</span> after</p>`,
1221+
localMetas,
1222+
)
1223+
})
1224+
1225+
t.Run("file preview in <div>, <li>, <details> (not in <summary>) environments", func(t *testing.T) {
1226+
testRender(
1227+
"<div>"+commitFileURLFirstLine+"</div>\n"+
1228+
"<ul><li>"+commitFileURLFirstLine+"</li></ul>\n"+
1229+
"<details><summary>"+commitFileURLFirstLine+"</summary>"+commitFileURLFirstLine+"</details>",
1230+
`<div>`+filePreviewBox+`</div>`+"\n"+
1231+
`<ul><li>`+filePreviewBox+`</li></ul>`+"\n"+
1232+
`<details><summary>`+linkRendered+`</summary>`+filePreviewBox+`</details>`,
1233+
localMetas,
1234+
)
1235+
})
1236+
1237+
t.Run("file preview in <span>, <em> and <strong> environments", func(t *testing.T) {
1238+
testRender(
1239+
"<div><span>"+commitFileURLFirstLine+"</span> <em>"+commitFileURLFirstLine+"</em> <strong>"+commitFileURLFirstLine+"</strong></div>",
1240+
`<div><span></span>`+filePreviewBox+`<span></span> `+
1241+
`<em></em>`+filePreviewBox+`<em></em> `+
1242+
`<strong></strong>`+filePreviewBox+`<strong></strong></div>`,
1243+
localMetas,
1244+
)
1245+
})
1246+
1247+
t.Run("no file preview in heading, striked out, code environments", func(t *testing.T) {
1248+
testRender(
1249+
"<h1>"+commitFileURLFirstLine+"</h1>\n<del>"+commitFileURLFirstLine+"</del>\n<code>"+commitFileURLFirstLine+"</code>",
1250+
`<h1>`+linkRendered+`</h1>`+"\n"+
1251+
`<del>`+linkRendered+`</del>`+"\n"+
1252+
`<code>`+commitFileURLFirstLine+`</code>`,
12101253
localMetas,
12111254
)
12121255
})

0 commit comments

Comments
 (0)