Skip redundant SGR sequences in light renderer

Track the last emitted SGR sequence and skip no-op transitions.
Reduces rendering output by 10-23% depending on the scenario.
This commit is contained in:
Junegunn Choi
2026-07-17 10:34:42 +09:00
parent 374b01242d
commit 3c0de84dab
2 changed files with 103 additions and 21 deletions
+45 -21
View File
@@ -39,6 +39,7 @@ func (r *LightRenderer) Bell() {
func (r *LightRenderer) PassThrough(str string) {
r.queued.WriteString("\x1b7" + str + "\x1b8")
r.invalidateSGR()
}
func (r *LightRenderer) stderr(str string) {
@@ -90,8 +91,28 @@ func (r *LightRenderer) csi(code string) string {
return fullcode
}
// setSGR emits the given SGR sequence only when the terminal is not already
// in that state. Sequences built by csiColor start with a reset parameter,
// so each of them fully determines the state on its own. The zero value of
// r.sgr never matches a sequence, so the first update after Init, Resume, or
// invalidateSGR is always emitted.
func (r *LightRenderer) setSGR(code string) {
if code != r.sgr {
r.stderr(code)
r.sgr = code
}
}
// invalidateSGR marks the terminal SGR state as unknown, e.g. after raw
// output that may have changed it behind the renderer's back.
func (r *LightRenderer) invalidateSGR() {
r.sgr = ""
}
func (r *LightRenderer) flush() {
if r.queued.Len() > 0 {
// Leave the terminal in the default SGR state between frames
r.setSGR("\x1b[0m")
// Wrap the frame in synchronized update mode (2026) so that the
// terminal applies it atomically. Terminals without support ignore
// the unknown private mode and behave as before.
@@ -130,6 +151,7 @@ type LightRenderer struct {
fullscreen bool
upOneLine bool
queued strings.Builder
sgr string
y int
x int
maxHeightFunc func(int) int
@@ -1002,6 +1024,9 @@ func (r *LightRenderer) disableModes() {
func (r *LightRenderer) Resume(clear bool, sigcont bool) {
r.setupTerminal()
// The programs that ran in the meantime may have left the terminal in an
// arbitrary SGR state
r.invalidateSGR()
if clear {
if r.fullscreen {
r.smcup()
@@ -1273,10 +1298,6 @@ func (w *LightWindow) drawBorder(onlyHorizontal bool) {
}
}
func (w *LightWindow) csi(code string) string {
return w.renderer.csi(code)
}
func (w *LightWindow) stderrInternal(str string, allowNLCR bool, resetCode string) {
w.renderer.stderrInternal(str, allowNLCR, resetCode)
}
@@ -1415,13 +1436,18 @@ func ulColorCode(c Color) string {
return ""
}
// csiColor builds the SGR sequence for the given colors and attributes
// without emitting it. The sequence starts with a reset parameter, so it
// fully determines the SGR state on its own.
func (w *LightWindow) csiColor(fg Color, bg Color, ul Color, attr Attr) (bool, string) {
codes := append(attrCodes(attr), colorCodes(fg, bg)...)
if ulCode := ulColorCode(ul); ulCode != "" {
codes = append(codes, ulCode)
}
code := w.csi(";" + strings.Join(codes, ";") + "m")
return len(codes) > 0, code
if len(codes) == 0 {
return false, "\x1b[0m"
}
return true, "\x1b[;" + strings.Join(codes, ";") + "m"
}
func (w *LightWindow) Print(text string) {
@@ -1434,15 +1460,13 @@ func cleanse(str string) string {
func (w *LightWindow) CPrint(pair ColorPair, text string) {
_, code := w.csiColor(pair.Fg(), pair.Bg(), pair.Ul(), pair.Attr())
w.renderer.setSGR(code)
w.stderrInternal(cleanse(text), false, code)
w.csi("0m")
}
func (w *LightWindow) cprint2(fg Color, bg Color, attr Attr, text string) {
hasColors, code := w.csiColor(fg, bg, colDefault, attr)
if hasColors {
defer w.csi("0m")
}
_, code := w.csiColor(fg, bg, colDefault, attr)
w.renderer.setSGR(code)
w.stderrInternal(cleanse(text), false, code)
}
@@ -1463,7 +1487,7 @@ func (w *LightWindow) fill(str string, resetCode string) FillReturn {
}
w.MoveAndClear(w.posy, w.posx)
w.Move(w.posy+1, 0)
w.renderer.stderr(resetCode)
w.renderer.setSGR(resetCode)
if len(lines) > 1 {
sign := w.wrapSign
width := w.wrapSignWidth
@@ -1473,7 +1497,8 @@ func (w *LightWindow) fill(str string, resetCode string) FillReturn {
width = truncatedWidth
}
w.stderrInternal(DIM+sign, false, resetCode)
w.renderer.stderr(resetCode)
w.renderer.invalidateSGR()
w.renderer.setSGR(resetCode)
w.Move(w.posy, width)
}
}
@@ -1484,20 +1509,19 @@ func (w *LightWindow) fill(str string, resetCode string) FillReturn {
return FillSuspend
}
w.Move(w.posy+1, 0)
w.renderer.stderr(resetCode)
w.renderer.setSGR(resetCode)
return FillNextLine
}
return FillContinue
}
func (w *LightWindow) setBg() string {
if w.bg != colDefault {
_, code := w.csiColor(colDefault, w.bg, colDefault, AttrRegular)
return code
}
// Should clear dim attribute after ␍ in the preview window
// The plain reset code for the default background is still required to
// clear the dim attribute after ␍ in the preview window
// e.g. printf "foo\rbar" | fzf --ansi --preview 'printf "foo\rbar"'
return "\x1b[m"
_, code := w.csiColor(colDefault, w.bg, colDefault, AttrRegular)
w.renderer.setSGR(code)
return code
}
func (w *LightWindow) LinkBegin(uri string, params string) {
@@ -1523,7 +1547,7 @@ func (w *LightWindow) CFill(fg Color, bg Color, ul Color, attr Attr, text string
bg = w.bg
}
if hasColors, resetCode := w.csiColor(fg, bg, ul, attr); hasColors {
defer w.csi("0m")
w.renderer.setSGR(resetCode)
return w.fill(text, resetCode)
}
return w.fill(text, w.setBg())
+58
View File
@@ -419,3 +419,61 @@ func TestLightRendererScrollWheel(t *testing.T) {
assertScroll("\x1b[<94;1;1M", 0, "ctrl-alt-shift") // ctrl+alt+shift + left (ignored)
assertScroll("\x1b[<95;1;1M", 0, "ctrl-alt-shift") // ctrl+alt+shift + right (ignored)
}
// Assert the exact byte stream emitted for a scripted rendering sequence,
// locking down the SGR deduplication behavior of the light renderer.
func TestLightRendererSGRDeduplication(t *testing.T) {
renderer, _ := NewLightRenderer(
"", nil, &ColorTheme{}, true, false, 8, false, true,
func(h int) int { return h })
r := renderer.(*LightRenderer)
r.width = 80
r.height = 24
out, err := os.CreateTemp(t.TempDir(), "fzf-ttyout")
if err != nil {
t.Fatal(err)
}
r.ttyout = out
w := r.NewWindow(0, 0, 40, 4, WindowList, MakeBorderStyle(BorderNone, true), false).(*LightWindow)
w.fg = colDefault
w.bg = colDefault
r.queued.Reset()
red := NewColorPair(196, colDefault, AttrRegular)
blue := NewColorPair(21, colDefault, AttrRegular)
w.Move(0, 0)
w.CPrint(red, "ab")
w.CPrint(red, "cd") // same pair; no SGR expected
w.CPrint(blue, "ef")
w.Print("gh") // default colors; one reset expected
w.Print("ij") // still default; no SGR expected
w.Move(1, 2)
w.CPrint(red, "kl")
r.invalidateSGR()
w.CPrint(red, "mn") // re-emitted after invalidation
r.flush() // adds a trailing reset to leave the default state
expected := "\x1b[?2026h\x1b[?7l\x1b[?25l" +
"\r" +
"\x1b[;38;5;196mab" +
"cd" +
"\x1b[;38;5;21mef" +
"\x1b[0mgh" +
"ij" +
"\x1b[1B\x1b[3G" +
"\x1b[;38;5;196mkl" +
"\x1b[;38;5;196mmn" +
"\x1b[0m" +
"\x1b[?25h\x1b[?7h\x1b[?2026l"
bytes, err := os.ReadFile(out.Name())
if err != nil {
t.Fatal(err)
}
if string(bytes) != expected {
t.Errorf("expected: %q\n got: %q", expected, string(bytes))
}
}