Cache Decolorise's result for a string that decolorises to nothing

Decolorise stores its result in a cache keyed by the string it was given,
but the lookup treats an empty result as a miss, so a string that
decolorises to nothing is recomputed on every call. Recomputing it means
compiling two regexes, and that costs 4534ns against 24.7ns for a string
the cache does answer for.

Empty cells are everywhere in the list panels, and RenderDisplayStrings
measures every cell twice, once to work out the column widths and once to
pad it. A commits panel showing 50 lines with one empty column spends
about 0.45ms of every render on this.

Read the cache with the two-value form, so that an empty result counts as
an answer, and compile the two regexes once at package level.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller
2026-09-19 09:54:36 +02:00
co-authored by Claude Opus 5
parent 71d3e7dfa5
commit 939bf831fb
+6 -5
View File
@@ -12,21 +12,22 @@ import (
var (
decoloriseCache = make(map[string]string)
decoloriseMutex sync.RWMutex
colorCodeRe = regexp.MustCompile(`\x1B\[([0-9]{1,3}(;[0-9]{1,3})*)?[mGK]`)
linkRe = regexp.MustCompile(`\x1B]8;[^;]*;(.*?)(\x1B.|\x07)`)
)
// Decolorise strips a string of color
func Decolorise(str string) string {
decoloriseMutex.RLock()
val := decoloriseCache[str]
val, ok := decoloriseCache[str]
decoloriseMutex.RUnlock()
if val != "" {
if ok {
return val
}
re := regexp.MustCompile(`\x1B\[([0-9]{1,3}(;[0-9]{1,3})*)?[mGK]`)
linkRe := regexp.MustCompile(`\x1B]8;[^;]*;(.*?)(\x1B.|\x07)`)
ret := re.ReplaceAllString(str, "")
ret := colorCodeRe.ReplaceAllString(str, "")
ret = linkRe.ReplaceAllString(ret, "")
decoloriseMutex.Lock()