mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-09 09:57:34 +02:00
Use Unicode Control Pictures for control character display
Render ASCII control characters (0x00-0x1F, 0x7F) as Unicode Control Pictures (U+2400-U+2421) instead of text abbreviations like [DEL] or [U+001E]. This applies to both the file view and diff view paths. Also style control char badges in red without background, matching the style of other escaped code points. Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
This commit is contained in:
parent
30c07c20e9
commit
1e96a85f1b
@ -199,12 +199,20 @@ func (e *escapeStreamer) invisibleRune(r rune) error {
|
||||
e.escaped.Escaped = true
|
||||
e.escaped.HasInvisible = true
|
||||
|
||||
// Use Unicode Control Pictures for ASCII control chars
|
||||
escaped := fmt.Sprintf("[U+%04X]", r)
|
||||
if r >= 0 && r <= 0x1f {
|
||||
escaped = string(0x2400 + r)
|
||||
} else if r == 0x7f {
|
||||
escaped = string(rune(0x2421))
|
||||
}
|
||||
|
||||
if err := e.PassthroughHTMLStreamer.StartTag("span", html.Attribute{
|
||||
Key: "class",
|
||||
Val: "escaped-code-point",
|
||||
}, html.Attribute{
|
||||
Key: "data-escaped",
|
||||
Val: fmt.Sprintf("[U+%04X]", r),
|
||||
Val: escaped,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -151,7 +151,7 @@ func TestEscapeControlReader(t *testing.T) {
|
||||
for _, test := range escapeControlTests {
|
||||
test.name += " (+Control)"
|
||||
test.text = addPrefix("\u001E", test.text)
|
||||
test.result = addPrefix(`<span class="escaped-code-point" data-escaped="[U+001E]"><span class="char">`+"\u001e"+`</span></span>`, test.result)
|
||||
test.result = addPrefix(`<span class="escaped-code-point" data-escaped="`+string(rune(0x241e))+`"><span class="char">`+"\u001e"+`</span></span>`, test.result)
|
||||
test.status.Escaped = true
|
||||
test.status.HasInvisible = true
|
||||
tests = append(tests, test)
|
||||
|
||||
@ -43,20 +43,12 @@ func globalVars() *globalVarsType {
|
||||
globalVarsPtr.githubStyles = styles.Get("github")
|
||||
globalVarsPtr.highlightMapping = setting.GetHighlightMapping()
|
||||
globalVarsPtr.escCtrlCharsMap = make([]template.HTML, 256)
|
||||
// ASCII Table 0x00 - 0x1F
|
||||
controlCharNames := []string{
|
||||
"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
|
||||
"BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
|
||||
"DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
|
||||
"CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US",
|
||||
// ASCII control characters 0x00-0x1F map to Unicode Control Pictures U+2400-U+241F
|
||||
for i := range 0x20 {
|
||||
globalVarsPtr.escCtrlCharsMap[i] = template.HTML(`<span class="broken-code-point" data-escaped="` + string(rune(0x2400+i)) + `"><span class="char">` + string(byte(i)) + `</span></span>`)
|
||||
}
|
||||
// Uncomment this line if you'd debug the layout without creating a special file, then Space (0x20) will also be escaped.
|
||||
// Don't worry, even if you forget to comment it out and push it to git repo, the CI tests will catch it and fail.
|
||||
// controlCharNames = append(controlCharNames, "SP")
|
||||
for i, s := range controlCharNames {
|
||||
globalVarsPtr.escCtrlCharsMap[i] = template.HTML(`<span class="broken-code-point" data-escaped="` + s + `"><span class="char">` + string(byte(i)) + `</span></span>`)
|
||||
}
|
||||
globalVarsPtr.escCtrlCharsMap[0x7f] = template.HTML(`<span class="broken-code-point" data-escaped="DEL"><span class="char">` + string(byte(0x7f)) + `</span></span>`)
|
||||
// DEL (0x7F) maps to U+2421
|
||||
globalVarsPtr.escCtrlCharsMap[0x7f] = template.HTML(`<span class="broken-code-point" data-escaped="` + string(rune(0x2421)) + `"><span class="char">` + string(byte(0x7f)) + `</span></span>`)
|
||||
globalVarsPtr.escCtrlCharsMap['\t'] = ""
|
||||
globalVarsPtr.escCtrlCharsMap['\n'] = ""
|
||||
globalVarsPtr.escCtrlCharsMap['\r'] = ""
|
||||
|
||||
@ -206,12 +206,12 @@ func TestUnsafeSplitHighlightedLines(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEscape(t *testing.T) {
|
||||
assert.Equal(t, template.HTML("\t\r\n<span class=\"broken-code-point\" data-escaped=\"NUL\"><span class=\"char\">\x00</span></span><span class=\"broken-code-point\" data-escaped=\"US\"><span class=\"char\">\x1f</span></span>&'\"<>"), escapeControlChars([]byte("\t\r\n\x00\x1f&'\"<>")))
|
||||
assert.Equal(t, template.HTML("<span class=\"broken-code-point\" data-escaped=\"NUL\"><span class=\"char\">\x00</span></span><span class=\"broken-code-point\" data-escaped=\"US\"><span class=\"char\">\x1f</span></span>&'"<>\t\r\n"), escapeFullString("\x00\x1f&'\"<>\t\r\n"))
|
||||
assert.Equal(t, template.HTML("\t\r\n<span class=\"broken-code-point\" data-escaped=\"\u2400\"><span class=\"char\">\x00</span></span><span class=\"broken-code-point\" data-escaped=\"\u241f\"><span class=\"char\">\x1f</span></span>&'\"<>"), escapeControlChars([]byte("\t\r\n\x00\x1f&'\"<>")))
|
||||
assert.Equal(t, template.HTML("<span class=\"broken-code-point\" data-escaped=\"\u2400\"><span class=\"char\">\x00</span></span><span class=\"broken-code-point\" data-escaped=\"\u241f\"><span class=\"char\">\x1f</span></span>&'"<>\t\r\n"), escapeFullString("\x00\x1f&'\"<>\t\r\n"))
|
||||
|
||||
out, _ := RenderFullFile("a.py", "", []byte("# \x7f<>"))
|
||||
assert.Equal(t, template.HTML(`<span class="c1"># <span class="broken-code-point" data-escaped="DEL"><span class="char">`+string(byte(0x7f))+`</span></span><></span>`), out[0])
|
||||
assert.Equal(t, template.HTML(`<span class="c1"># <span class="broken-code-point" data-escaped="`+string(rune(0x2421))+`"><span class="char">`+string(byte(0x7f))+`</span></span><></span>`), out[0])
|
||||
|
||||
out = renderPlainText([]byte("# \x7f<>"))
|
||||
assert.Equal(t, template.HTML(`# <span class="broken-code-point" data-escaped="DEL"><span class="char">`+string(byte(0x7f))+`</span></span><>`), out[0])
|
||||
assert.Equal(t, template.HTML(`# <span class="broken-code-point" data-escaped="`+string(rune(0x2421))+`"><span class="char">`+string(byte(0x7f))+`</span></span><>`), out[0])
|
||||
}
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
/*
|
||||
Show the escaped and hide the real char:
|
||||
<span class="broken-code-point" data-escaped="DEL"><span class="char">{real-char}</span></span>
|
||||
<span class="broken-code-point" data-escaped="␡"><span class="char">{real-char}</span></span>
|
||||
Only show the real-char:
|
||||
<span class="broken-code-point">{real-char}</span>
|
||||
*/
|
||||
.broken-code-point:not([data-escaped]),
|
||||
.broken-code-point[data-escaped]::before {
|
||||
.broken-code-point:not([data-escaped]) {
|
||||
border-radius: 4px;
|
||||
padding: 0 2px;
|
||||
color: var(--color-body);
|
||||
@ -15,6 +14,7 @@ Only show the real-char:
|
||||
.broken-code-point[data-escaped]::before {
|
||||
visibility: visible;
|
||||
content: attr(data-escaped);
|
||||
color: var(--color-red);
|
||||
}
|
||||
.broken-code-point[data-escaped] .char {
|
||||
/* make it copyable by selecting the text (AI suggestion, no other solution) */
|
||||
@ -26,11 +26,11 @@ Only show the real-char:
|
||||
/*
|
||||
Show the escaped and hide the real-char:
|
||||
<span class="unicode-escaped">
|
||||
<span class="escaped-code-point" data-escaped="U+1F600"><span class="char">{real-char}</span></span>
|
||||
<span class="escaped-code-point" data-escaped="[U+1F600]"><span class="char">{real-char}</span></span>
|
||||
</span>
|
||||
Hide the escaped and show the real-char:
|
||||
<span>
|
||||
<span class="escaped-code-point" data-escaped="U+1F600"><span class="char">{real-char}</span></span>
|
||||
<span class="escaped-code-point" data-escaped="[U+1F600]"><span class="char">{real-char}</span></span>
|
||||
</span>
|
||||
*/
|
||||
.unicode-escaped .escaped-code-point[data-escaped]::before {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user