diff --git a/modules/charset/escape_stream.go b/modules/charset/escape_stream.go
index 98a25e9e55..5aa15a6fa9 100644
--- a/modules/charset/escape_stream.go
+++ b/modules/charset/escape_stream.go
@@ -18,6 +18,19 @@ import (
// VScode defaultWordRegexp
var defaultWordRegexp = regexp.MustCompile(`(-?\d*\.\d\w*)|([^\` + "`" + `\~\!\@\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s\x00-\x1f]+)`)
+// ControlCharPicture returns the Unicode Control Picture for ASCII control
+// characters (0x00-0x1F → U+2400-U+241F, 0x7F → U+2421). For other runes it
+// returns 0, false.
+func ControlCharPicture(r rune) (rune, bool) {
+ if r >= 0 && r <= 0x1f {
+ return 0x2400 + r, true
+ }
+ if r == 0x7f {
+ return 0x2421, true
+ }
+ return 0, false
+}
+
func NewEscapeStreamer(locale translation.Locale, next HTMLStreamer, allowed ...rune) HTMLStreamer {
allowedM := make(map[rune]bool, len(allowed))
for _, v := range allowed {
@@ -199,12 +212,11 @@ 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))
+ var escaped string
+ if pic, ok := ControlCharPicture(r); ok {
+ escaped = string(pic)
+ } else {
+ escaped = fmt.Sprintf("[U+%04X]", r)
}
if err := e.PassthroughHTMLStreamer.StartTag("span", html.Attribute{
diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go
index 35d041527a..ec56a28851 100644
--- a/modules/highlight/highlight.go
+++ b/modules/highlight/highlight.go
@@ -10,6 +10,7 @@ import (
"slices"
"sync"
+ "code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
@@ -43,12 +44,12 @@ func globalVars() *globalVarsType {
globalVarsPtr.githubStyles = styles.Get("github")
globalVarsPtr.highlightMapping = setting.GetHighlightMapping()
globalVarsPtr.escCtrlCharsMap = make([]template.HTML, 256)
- // ASCII control characters 0x00-0x1F map to Unicode Control Pictures U+2400-U+241F
for i := range 0x20 {
- globalVarsPtr.escCtrlCharsMap[i] = template.HTML(`` + string(byte(i)) + ``)
+ pic, _ := charset.ControlCharPicture(rune(i))
+ globalVarsPtr.escCtrlCharsMap[i] = controlCharHTML(pic, byte(i))
}
- // DEL (0x7F) maps to U+2421
- globalVarsPtr.escCtrlCharsMap[0x7f] = template.HTML(`` + string(byte(0x7f)) + ``)
+ pic, _ := charset.ControlCharPicture(0x7f)
+ globalVarsPtr.escCtrlCharsMap[0x7f] = controlCharHTML(pic, 0x7f)
globalVarsPtr.escCtrlCharsMap['\t'] = ""
globalVarsPtr.escCtrlCharsMap['\n'] = ""
globalVarsPtr.escCtrlCharsMap['\r'] = ""
@@ -64,6 +65,10 @@ func globalVars() *globalVarsType {
return globalVarsPtr
}
+func controlCharHTML(pic rune, char byte) template.HTML {
+ return template.HTML(`` + string(char) + ``)
+}
+
func escapeByMap(code []byte, escapeMap []template.HTML) template.HTML {
firstEscapePos := -1
for i, c := range code {
diff --git a/web_src/css/modules/charescape.css b/web_src/css/modules/charescape.css
index b72d7bdf45..6f3dc99028 100644
--- a/web_src/css/modules/charescape.css
+++ b/web_src/css/modules/charescape.css
@@ -18,7 +18,10 @@ Only show the real-char:
.broken-code-point[data-escaped]::before {
visibility: visible;
content: attr(data-escaped);
- color: var(--color-red);
+ border-radius: 2px;
+ padding: 0 1px;
+ color: var(--color-body);
+ background: var(--color-text-light-1);
}
.broken-code-point[data-escaped] .char {