Repository: charmbracelet/bubbletea
Pull request #504 (2022-10-07 19:50:34)

chore: make CatchPanics an option flag
Handle it like the other options, no need to make this special.


Patch:
diff --git a/options.go b/options.go
index 3aad18c..8b4b4d4 100644
--- a/options.go
+++ b/options.go
@@ -52,7 +52,7 @@ func WithoutSignalHandler() ProgramOption {
 // cleanup on exit.
 func WithoutCatchPanics() ProgramOption {
 	return func(p *Program) {
-		p.CatchPanics = false
+		p.startupOptions |= withoutCatchPanics
 	}
 }
 
diff --git a/tea.go b/tea.go
index 82afda5..75fa756 100644
--- a/tea.go
+++ b/tea.go
@@ -72,6 +72,12 @@ const (
 	withCustomInput
 	withANSICompressor
 	withoutSignalHandler
+
+	// Catching panics is incredibly useful for restoring the terminal to a
+	// usable state after a panic occurs. When this is set, Bubble Tea will
+	// recover from panics, print the stack trace, and disable raw mode. This
+	// feature is on by default.
+	withoutCatchPanics
 )
 
 // Program is a terminal user interface.
@@ -88,26 +94,21 @@ type Program struct {
 	errs         chan error
 	readLoopDone chan struct{}
 
-	output        *termenv.Output // where to send output. this will usually be os.Stdout.
+	// where to send output, this will usually be os.Stdout.
+	output        *termenv.Output
 	restoreOutput func() error
-	input         io.Reader // this will usually be os.Stdin.
-	cancelReader  cancelreader.CancelReader
-
-	renderer           renderer
-	altScreenWasActive bool // was the altscreen active before releasing the terminal?
+	renderer      renderer
 
-	// CatchPanics is incredibly useful for restoring the terminal to a usable
-	// state after a panic occurs. When this is set, Bubble Tea will recover
-	// from panics, print the stack trace, and disable raw mode. This feature
-	// is on by default.
-	CatchPanics bool
+	// where to read inputs from, this will usually be os.Stdin.
+	input        io.Reader
+	cancelReader cancelreader.CancelReader
+	console      console.Console
 
-	ignoreSignals bool
+	altScreenWasActive bool // was the altscreen active before releasing the terminal?
+	ignoreSignals      bool
 
 	killc chan bool
 
-	console console.Console
-
 	// Stores the original reference to stdin for cases where input is not a
 	// TTY on windows and we've automatically opened CONIN$ to receive input.
 	// When the program exits this will be restored.
@@ -133,7 +134,6 @@ func NewProgram(model Model, opts ...ProgramOption) *Program {
 		initialModel: model,
 		input:        os.Stdin,
 		msgs:         make(chan Msg),
-		CatchPanics:  true,
 		killc:        make(chan bool, 1),
 	}
 
@@ -383,7 +383,8 @@ func (p *Program) StartReturningModel() (Model, error) {
 		close(sigintLoopDone)
 	}
 
-	if p.CatchPanics {
+	// Recover from panics.
+	if !p.startupOptions.has(withoutCatchPanics) {
 		defer func() {
 			if r := recover(); r != nil {
 				p.shutdown(true)


Test patch:
diff --git a/options_test.go b/options_test.go
index ba44410..7e08f58 100644
--- a/options_test.go
+++ b/options_test.go
@@ -25,13 +25,6 @@ func TestOptions(t *testing.T) {
 		}
 	})
 
-	t.Run("catch panics", func(t *testing.T) {
-		p := NewProgram(nil, WithoutCatchPanics())
-		if p.CatchPanics {
-			t.Errorf("catch panics should not have been set")
-		}
-	})
-
 	t.Run("renderer", func(t *testing.T) {
 		p := NewProgram(nil, WithoutRenderer())
 		switch p.renderer.(type) {
@@ -62,6 +55,10 @@ func TestOptions(t *testing.T) {
 			exercise(t, WithANSICompressor(), withANSICompressor)
 		})
 
+		t.Run("without catch panics", func(t *testing.T) {
+			exercise(t, WithoutCatchPanics(), withoutCatchPanics)
+		})
+
 		t.Run("without signal handler", func(t *testing.T) {
 			exercise(t, WithoutSignalHandler(), withoutSignalHandler)
 		})