Repository: charmbracelet/bubbletea
Pull request #1236 (2024-11-12 20:45:11)

(v2) feat: make the ferocious renderer opt-in
The ferocious renderer is now opt-in. This commit adds a new option `WithFerociousRenderer` that enables the ferocious renderer.


Patch:
diff --git a/options.go b/options.go
index 7954e4d..c0ae254 100644
--- a/options.go
+++ b/options.go
@@ -286,8 +286,8 @@ func (e experimentalOptions) has(option string) bool {
 }
 
 const (
-	// Unferocious disables the "ferocious" renderer.
-	experimentalUnferocious = "unferocious"
+	// Ferocious enables the "ferocious" renderer.
+	experimentalFerocious = "ferocious"
 )
 
 // WithColorProfile sets the color profile that the program will use. This is
@@ -302,9 +302,14 @@ func WithColorProfile(profile colorprofile.Profile) ProgramOption {
 	}
 }
 
-// withStandardRenderer tells Bubble Tea to use the standrad renderer.
-func withStandardRenderer() ProgramOption { //nolint:unused
+// WithFerociousRenderer tells Bubble Tea to use the new shiny "ferocious"
+// renderer. This renderer is experimental and may change or be removed in
+// future versions.
+//
+// The ferocious renderer is a new renderer that is faster and more efficient
+// than the default renderer. It is also more ferocious ;)
+func WithFerociousRenderer() ProgramOption { //nolint:unused
 	return func(p *Program) {
-		p.startupOptions |= avecStandardRenderer
+		p.startupOptions |= withFerociousRenderer
 	}
 }
diff --git a/standard_renderer.go b/standard_renderer.go
index c206712..db5e20c 100644
--- a/standard_renderer.go
+++ b/standard_renderer.go
@@ -245,12 +245,7 @@ func (r *standardRenderer) reset() {
 }
 
 func (r *standardRenderer) clearScreen() {
-	r.mtx.Lock()
-	defer r.mtx.Unlock()
-
-	r.execute(ansi.EraseEntireScreen)
-	r.execute(ansi.HomeCursorPosition)
-
+	r.execute(ansi.EraseEntireScreen + ansi.HomeCursorPosition)
 	r.repaint()
 }
 
diff --git a/tea.go b/tea.go
index 1e2a818..0333218 100644
--- a/tea.go
+++ b/tea.go
@@ -15,7 +15,6 @@ import (
 	"fmt"
 	"image/color"
 	"io"
-	"log"
 	"os"
 	"os/signal"
 	"runtime"
@@ -112,7 +111,7 @@ const (
 	withKeyboardEnhancements
 	withGraphemeClustering
 
-	avecStandardRenderer
+	withFerociousRenderer
 )
 
 // channelHandlers manages the series of channels returned by various processes.
@@ -426,8 +425,6 @@ 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:
@@ -708,10 +705,10 @@ func (p *Program) Run() (Model, error) {
 	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 {
+		if p.startupOptions&withFerociousRenderer != 0 || p.exp.has(experimentalFerocious) {
 			p.renderer = newFerociousRenderer(p.profile)
+		} else {
+			p.renderer = newStandardRenderer(p.profile)
 		}
 	}
 


Test patch:
diff --git a/examples/simple/main_test.go b/examples/simple/main_test.go
index 87169e7..e489658 100644
--- a/examples/simple/main_test.go
+++ b/examples/simple/main_test.go
@@ -54,9 +54,6 @@ func TestApp(t *testing.T) {
 }
 
 func TestAppInteractive(t *testing.T) {
-	// TODO: Enable this test again
-	t.Skip("update this test to recognize the new renderer.")
-
 	m := model(10)
 	tm := teatest.NewTestModel(
 		t, m,
diff --git a/screen_test.go b/screen_test.go
index 2fc48cc..3b2333e 100644
--- a/screen_test.go
+++ b/screen_test.go
@@ -19,67 +19,67 @@ func TestClearMsg(t *testing.T) {
 		{
 			name:     "clear_screen",
 			cmds:     []Cmd{ClearScreen},
-			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",
+			expected: "\x1b[?25l\x1b[?2004h\x1b[2J\x1b[H\rsuccess\r\n\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[H\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\r\n\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[H\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\r\n\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[80D\x1b[2K\r\x1b[?2004l\x1b[?25h\x1b[?1002l\x1b[?1003l\x1b[?1006l",
+			expected: "\x1b[?25l\x1b[?2004h\x1b[?1002h\x1b[?1006h\rsuccess\r\n\x1b[D\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[80D\x1b[2K\r\x1b[?2004l\x1b[?25h\x1b[?1002l\x1b[?1003l\x1b[?1006l",
+			expected: "\x1b[?25l\x1b[?2004h\x1b[?1003h\x1b[?1006h\rsuccess\r\n\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h\x1b[?1002l\x1b[?1003l\x1b[?1006l",
 		},
 		{
 			name:     "mouse_disable",
 			cmds:     []Cmd{EnableMouseAllMotion, DisableMouse},
-			expected: "\x1b[?25l\x1b[?2004h\x1b[?1003h\x1b[?1006h\x1b[?1002l\x1b[?1003l\x1b[?1006l\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
+			expected: "\x1b[?25l\x1b[?2004h\x1b[?1003h\x1b[?1006h\x1b[?1002l\x1b[?1003l\x1b[?1006l\rsuccess\r\n\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
 		},
 		{
 			name:     "cursor_hide",
 			cmds:     []Cmd{HideCursor},
-			expected: "\x1b[?25l\x1b[?2004h\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
+			expected: "\x1b[?25l\x1b[?2004h\rsuccess\r\n\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
 		},
 		{
 			name:     "cursor_hideshow",
 			cmds:     []Cmd{HideCursor, ShowCursor},
-			expected: "\x1b[?25l\x1b[?2004h\x1b[?25h\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l",
+			expected: "\x1b[?25l\x1b[?2004h\x1b[?25h\rsuccess\r\n\x1b[D\x1b[2K\r\x1b[?2004l",
 		},
 		{
 			name:     "bp_stop_start",
 			cmds:     []Cmd{DisableBracketedPaste, EnableBracketedPaste},
-			expected: "\x1b[?25l\x1b[?2004h\x1b[?2004l\x1b[?2004h\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
+			expected: "\x1b[?25l\x1b[?2004h\x1b[?2004l\x1b[?2004h\rsuccess\r\n\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
 		},
 		{
 			name:     "read_set_clipboard",
 			cmds:     []Cmd{ReadClipboard, SetClipboard("success")},
-			expected: "\x1b[?25l\x1b[?2004h\x1b]52;c;?\a\x1b]52;c;c3VjY2Vzcw==\a\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
+			expected: "\x1b[?25l\x1b[?2004h\x1b]52;c;?\a\x1b]52;c;c3VjY2Vzcw==\a\rsuccess\r\n\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
 		},
 		{
 			name:     "bg_fg_cur_color",
 			cmds:     []Cmd{RequestForegroundColor, RequestBackgroundColor, RequestCursorColor},
-			expected: "\x1b[?25l\x1b[?2004h\x1b]10;?\a\x1b]11;?\a\x1b]12;?\a\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
+			expected: "\x1b[?25l\x1b[?2004h\x1b]10;?\a\x1b]11;?\a\x1b]12;?\a\rsuccess\r\n\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
 		},
 		{
 			name:     "bg_set_color",
 			cmds:     []Cmd{SetBackgroundColor(color.RGBA{255, 255, 255, 255})},
-			expected: "\x1b[?25l\x1b[?2004h\x1b]11;#ffffff\a\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h\x1b]111\a",
+			expected: "\x1b[?25l\x1b[?2004h\x1b]11;#ffffff\a\rsuccess\r\n\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h\x1b]111\a",
 		},
 		{
 			name:     "grapheme_clustering",
 			cmds:     []Cmd{EnableGraphemeClustering},
-			expected: "\x1b[?25l\x1b[?2004h\x1b[?2027h\x1b[?2027$p\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h\x1b[?2027l",
+			expected: "\x1b[?25l\x1b[?2004h\x1b[?2027h\x1b[?2027$p\rsuccess\r\n\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h\x1b[?2027l",
 		},
 	}
 
@@ -88,13 +88,13 @@ func TestClearMsg(t *testing.T) {
 		tests = append(tests, test{
 			name:     "kitty_start",
 			cmds:     []Cmd{DisableKeyboardEnhancements, EnableKeyboardEnhancements(WithKeyReleases)},
-			expected: "\x1b[?25l\x1b[?2004h\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
+			expected: "\x1b[?25l\x1b[?2004h\rsuccess\r\n\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h",
 		})
 	} else {
 		tests = append(tests, test{
 			name:     "kitty_start",
 			cmds:     []Cmd{DisableKeyboardEnhancements, EnableKeyboardEnhancements(WithKeyReleases)},
-			expected: "\x1b[?25l\x1b[?2004h\x1b[>4;1m\x1b[>3u\rsuccess\x1b[0K\r\n\x1b[0K\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h\x1b[>4;0m\x1b[>0u",
+			expected: "\x1b[?25l\x1b[?2004h\x1b[>4;1m\x1b[>3u\rsuccess\r\n\x1b[D\x1b[2K\r\x1b[?2004l\x1b[?25h\x1b[>4;0m\x1b[>0u",
 		})
 	}
 
@@ -106,13 +106,8 @@ func TestClearMsg(t *testing.T) {
 			m := &testModel{}
 			p := NewProgram(m, WithInput(&in), WithOutput(&buf),
 				// Use ANSI256 to increase test coverage.
-				WithColorProfile(colorprofile.ANSI256),
+				WithColorProfile(colorprofile.ANSI256))
 
-				// XXX: Use the standard renderer, for now. Ultiamtely we want
-				// 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)