Skip to content

Commit 97fc4fa

Browse files
fix: incorrect line skipping in renderer flush (#1233)
* fix line skip in flush * mod tidy in examples dir * reset lastRenderedLines on repaint * docs: add note * fix: simplify line skip logic --------- Co-authored-by: Ayman Bagabas <[email protected]>
1 parent 1feb60b commit 97fc4fa

File tree

3 files changed

+19
-14
lines changed

3 files changed

+19
-14
lines changed

examples/go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ require (
2424
github.com/aymerick/douceur v0.2.0 // indirect
2525
github.com/charmbracelet/x/ansi v0.4.5 // indirect
2626
github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b // indirect
27-
github.com/charmbracelet/x/term v0.2.0 // indirect
27+
github.com/charmbracelet/x/term v0.2.1 // indirect
2828
github.com/dlclark/regexp2 v1.11.0 // indirect
2929
github.com/dustin/go-humanize v1.0.1 // indirect
3030
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
@@ -41,8 +41,8 @@ require (
4141
github.com/yuin/goldmark v1.7.4 // indirect
4242
github.com/yuin/goldmark-emoji v1.0.3 // indirect
4343
golang.org/x/net v0.27.0 // indirect
44-
golang.org/x/sync v0.8.0 // indirect
45-
golang.org/x/sys v0.26.0 // indirect
44+
golang.org/x/sync v0.9.0 // indirect
45+
golang.org/x/sys v0.27.0 // indirect
4646
golang.org/x/term v0.22.0 // indirect
4747
golang.org/x/text v0.19.0 // indirect
4848
)

examples/go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAM
2828
github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
2929
github.com/charmbracelet/x/exp/teatest v0.0.0-20240521184646-23081fb03b28 h1:sOWKNRjt8uOEVgPiJVIJCse1+mUDM2F/vYY6W0Go640=
3030
github.com/charmbracelet/x/exp/teatest v0.0.0-20240521184646-23081fb03b28/go.mod h1:l1w+LTJZCCozeGzMEWGxRw6Mo2DfcZUvupz8HGubdes=
31-
github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0=
32-
github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0=
31+
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
32+
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
3333
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
3434
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
3535
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
@@ -76,12 +76,12 @@ github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhb
7676
github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
7777
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
7878
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
79-
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
80-
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
79+
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
80+
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
8181
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
8282
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
83-
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
84-
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
83+
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
84+
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
8585
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
8686
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
8787
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=

standard_renderer.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type standardRenderer struct {
3434
ticker *time.Ticker
3535
done chan struct{}
3636
lastRender string
37+
lastRenderedLines []string
3738
linesRendered int
3839
useANSICompressor bool
3940
once sync.Once
@@ -174,7 +175,6 @@ func (r *standardRenderer) flush() {
174175
}
175176

176177
newLines := strings.Split(r.buf.String(), "\n")
177-
oldLines := strings.Split(r.lastRender, "\n")
178178

179179
// If we know the output's height, we can use it to determine how many
180180
// lines we can render. We drop lines from the top of the render buffer if
@@ -184,7 +184,6 @@ func (r *standardRenderer) flush() {
184184
newLines = newLines[len(newLines)-r.height:]
185185
}
186186

187-
numLinesThisFlush := len(newLines)
188187
flushQueuedMessages := len(r.queuedMessageLines) > 0 && !r.altScreenActive
189188

190189
if flushQueuedMessages {
@@ -210,7 +209,7 @@ func (r *standardRenderer) flush() {
210209
// Paint new lines.
211210
for i := 0; i < len(newLines); i++ {
212211
canSkip := !flushQueuedMessages && // Queuing messages triggers repaint -> we don't have access to previous frame content.
213-
len(oldLines) > i && oldLines[i] == newLines[i] // Previously rendered line is the same.
212+
len(r.lastRenderedLines) > i && r.lastRenderedLines[i] == newLines[i] // Previously rendered line is the same.
214213

215214
if _, ignore := r.ignoreLines[i]; ignore || canSkip {
216215
// Unless this is the last line, move the cursor down.
@@ -257,11 +256,11 @@ func (r *standardRenderer) flush() {
257256
}
258257

259258
// Clearing left over content from last render.
260-
if r.linesRendered > numLinesThisFlush {
259+
if r.linesRendered > len(newLines) {
261260
buf.WriteString(ansi.EraseScreenBelow)
262261
}
263262

264-
r.linesRendered = numLinesThisFlush
263+
r.linesRendered = len(newLines)
265264

266265
// Make sure the cursor is at the start of the last line to keep rendering
267266
// behavior consistent.
@@ -276,6 +275,11 @@ func (r *standardRenderer) flush() {
276275

277276
_, _ = r.out.Write(buf.Bytes())
278277
r.lastRender = r.buf.String()
278+
279+
// Save previously rendered lines for comparison in the next render. If we
280+
// don't do this, we can't skip rendering lines that haven't changed.
281+
// See https://github.com/charmbracelet/bubbletea/pull/1233
282+
r.lastRenderedLines = newLines
279283
r.buf.Reset()
280284
}
281285

@@ -299,6 +303,7 @@ func (r *standardRenderer) write(s string) {
299303

300304
func (r *standardRenderer) repaint() {
301305
r.lastRender = ""
306+
r.lastRenderedLines = nil
302307
}
303308

304309
func (r *standardRenderer) clearScreen() {

0 commit comments

Comments
 (0)