Skip to content

Commit

Permalink
chore: merge branch 'main' into v2-exp
Browse files Browse the repository at this point in the history
  • Loading branch information
aymanbagabas committed Nov 12, 2024
2 parents 1ebb164 + 97fc4fa commit 29a3c0d
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 29 deletions.
4 changes: 2 additions & 2 deletions examples/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ require (
github.com/yuin/goldmark v1.7.4 // indirect
github.com/yuin/goldmark-emoji v1.0.3 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/term v0.22.0 // indirect
golang.org/x/text v0.19.0 // indirect
)
Expand Down
8 changes: 4 additions & 4 deletions examples/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,12 @@ golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
Expand Down
11 changes: 6 additions & 5 deletions screen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,27 @@ func TestClearMsg(t *testing.T) {
{
name: "clear_screen",
cmds: []Cmd{ClearScreen},
expected: "\x1b[?25l\x1b[?2004h\x1b[2J\x1b[1;1H\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
expected: "\x1b[?25l\x1b[?2004h\x1b[2J\x1b[H\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
},
{
name: "altscreen",
cmds: []Cmd{EnterAltScreen, ExitAltScreen},
expected: "\x1b[?25l\x1b[?2004h\x1b[?1049h\x1b[2J\x1b[1;1H\x1b[?25l\x1b[?1049l\x1b[?25l\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
expected: "\x1b[?25l\x1b[?2004h\x1b[?1049h\x1b[2J\x1b[H\x1b[?25l\x1b[?1049l\x1b[?25l\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
},
{
name: "altscreen_autoexit",
cmds: []Cmd{EnterAltScreen},
expected: "\x1b[?25l\x1b[?2004h\x1b[?1049h\x1b[2J\x1b[1;1H\x1b[?25l\rsuccess\x1b[0K\r\n\x1b[0K\x1b[2;0H\x1b[2K\r\x1b[?2004l\x1b[?25h\x1b[?1049l\x1b[?25h",
expected: "\x1b[?25l\x1b[?2004h\x1b[?1049h\x1b[2J\x1b[H\x1b[?25l\rsuccess\x1b[0K\r\n\x1b[0K\x1b[2;0H\x1b[2K\r\x1b[?2004l\x1b[?25h\x1b[?1049l\x1b[?25h",
},
{
name: "mouse_cellmotion",
cmds: []Cmd{EnableMouseCellMotion},
expected: "\x1b[?25l\x1b[?2004h\x1b[?1002h\x1b[?1006h\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h\x1b[?1002l\x1b[?1003l\x1b[?1006l",
expected: "\x1b[?25l\x1b[?2004h\x1b[?1002h\x1b[?1006h\rsuccess\x1b[0K\r\n\x1b[0K\x1b[80D\x1b[2K\r\x1b[?2004l\x1b[?25h\x1b[?1002l\x1b[?1003l\x1b[?1006l",
},
{
name: "mouse_allmotion",
cmds: []Cmd{EnableMouseAllMotion},
expected: "\x1b[?25l\x1b[?2004h\x1b[?1003h\x1b[?1006h\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h\x1b[?1002l\x1b[?1003l\x1b[?1006l",
expected: "\x1b[?25l\x1b[?2004h\x1b[?1003h\x1b[?1006h\rsuccess\x1b[0K\r\n\x1b[0K\x1b[80D\x1b[2K\r\x1b[?2004l\x1b[?25h\x1b[?1002l\x1b[?1003l\x1b[?1006l",
},
{
name: "mouse_disable",
Expand Down Expand Up @@ -112,6 +112,7 @@ func TestClearMsg(t *testing.T) {
// to update the tests to use the Ferocious Renderer.
withStandardRenderer())

test.cmds = append([]Cmd{func() Msg { return WindowSizeMsg{80, 24} }}, test.cmds...)
test.cmds = append(test.cmds, Quit)
go p.Send(test.cmds)

Expand Down
47 changes: 35 additions & 12 deletions standard_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type standardRenderer struct {
buf bytes.Buffer
queuedMessageLines []string
lastRender string
lastRenderedLines []string
linesRendered int

// cursor visibility state
Expand Down Expand Up @@ -105,7 +106,6 @@ func (r *standardRenderer) flush() (err error) {
}

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

// If we know the output's height, we can use it to determine how many
// lines we can render. We drop lines from the top of the render buffer if
Expand All @@ -115,14 +115,20 @@ func (r *standardRenderer) flush() (err error) {
newLines = newLines[len(newLines)-r.height:]
}

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

if flushQueuedMessages {
// Dump the lines we've queued up for printing.
for _, line := range r.queuedMessageLines {
// Removing previousy rendered content at the end of line.
line = line + ansi.EraseLineRight
if ansi.StringWidth(line) < r.width {
// We only erase the rest of the line when the line is shorter than
// the width of the terminal. When the cursor reaches the end of
// the line, any escape sequences that follow will only affect the
// last cell of the line.

// Removing previously rendered content at the end of line.
line = line + ansi.EraseLineRight
}

_, _ = buf.WriteString(line)
_, _ = buf.WriteString("\r\n")
Expand All @@ -134,7 +140,7 @@ func (r *standardRenderer) flush() (err error) {
// Paint new lines.
for i := 0; i < len(newLines); i++ {
canSkip := !flushQueuedMessages && // Queuing messages triggers repaint -> we don't have access to previous frame content.
len(oldLines) > i && oldLines[i] == newLines[i] // Previously rendered line is the same.
len(r.lastRenderedLines) > i && r.lastRenderedLines[i] == newLines[i] // Previously rendered line is the same.

if _, ignore := r.ignoreLines[i]; ignore || canSkip {
// Unless this is the last line, move the cursor down.
Expand All @@ -152,9 +158,6 @@ func (r *standardRenderer) flush() (err error) {

line := newLines[i]

// Removing previousy rendered content at the end of line.
line = line + ansi.EraseLineRight

// Truncate lines wider than the width of the window to avoid
// wrapping, which will mess up rendering. If we don't have the
// width of the window this will be ignored.
Expand All @@ -166,6 +169,16 @@ func (r *standardRenderer) flush() (err error) {
line = ansi.Truncate(line, r.width, "")
}

if ansi.StringWidth(line) < r.width {
// We only erase the rest of the line when the line is shorter than
// the width of the terminal. When the cursor reaches the end of
// the line, any escape sequences that follow will only affect the
// last cell of the line.

// Removing previously rendered content at the end of line.
line = line + ansi.EraseLineRight
}

_, _ = buf.WriteString(line)

if i < len(newLines)-1 {
Expand All @@ -174,11 +187,11 @@ func (r *standardRenderer) flush() (err error) {
}

// Clearing left over content from last render.
if r.linesRendered > numLinesThisFlush {
if r.linesRendered > len(newLines) {
buf.WriteString(ansi.EraseScreenBelow)
}

r.linesRendered = numLinesThisFlush
r.linesRendered = len(newLines)

// Make sure the cursor is at the start of the last line to keep rendering
// behavior consistent.
Expand All @@ -193,6 +206,11 @@ func (r *standardRenderer) flush() (err error) {

_, err = r.out.Write(buf.Bytes())
r.lastRender = r.buf.String()

// Save previously rendered lines for comparison in the next render. If we
// don't do this, we can't skip rendering lines that haven't changed.
// See https://github.com/charmbracelet/bubbletea/pull/1233
r.lastRenderedLines = newLines
r.buf.Reset()
return
}
Expand All @@ -218,6 +236,7 @@ func (r *standardRenderer) render(s string) {
// repaint forces a full repaint.
func (r *standardRenderer) repaint() {
r.lastRender = ""
r.lastRenderedLines = nil
}

// reset resets the standardRenderer to its initial state.
Expand All @@ -226,7 +245,11 @@ func (r *standardRenderer) reset() {
}

func (r *standardRenderer) clearScreen() {
r.execute(ansi.EraseEntireScreen + ansi.CursorOrigin)
r.mtx.Lock()
defer r.mtx.Unlock()

r.execute(ansi.EraseEntireScreen)
r.execute(ansi.HomeCursorPosition)

r.repaint()
}
Expand All @@ -238,7 +261,7 @@ func (r *standardRenderer) setAltScreenBuffer(on bool) {
// alt screen (or alt screen support is disabled, like GNU screen by
// default).
r.execute(ansi.EraseEntireScreen)
r.execute(ansi.CursorOrigin)
r.execute(ansi.HomeCursorPosition)
}

// cmd.exe and other terminals keep separate cursor states for the AltScreen
Expand Down
13 changes: 7 additions & 6 deletions tea.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"fmt"
"image/color"
"io"
"log"
"os"
"os/signal"
"runtime"
Expand Down Expand Up @@ -425,6 +426,8 @@ func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) {
continue
}

log.Printf("msg: %T", msg)

// Handle special internal messages.
switch msg := msg.(type) {
case QuitMsg:
Expand Down Expand Up @@ -701,13 +704,11 @@ func (p *Program) Run() (Model, error) {
if !p.startupOptions.has(withColorProfile) {
p.profile = colorprofile.Detect(p.output.Writer(), p.environ)
}
go p.Send(ColorProfileMsg{p.profile})

// If no renderer is set use the ferocious one.
if p.startupOptions&avecStandardRenderer != 0 {
p.renderer = newStandardRenderer(p.profile)
} else if p.renderer == nil {
if p.exp.has(experimentalUnferocious) {
go p.Send(ColorProfileMsg{p.profile})
if p.renderer == nil {
// If no renderer is set use the ferocious one.
if p.startupOptions&avecStandardRenderer != 0 || p.exp.has(experimentalUnferocious) {
p.renderer = newStandardRenderer(p.profile)
} else {
p.renderer = newFerociousRenderer(p.profile)
Expand Down

0 comments on commit 29a3c0d

Please # to comment.