feat: use Termenv.Output to write to tty

This commit is contained in:
Christian Muehlhaeuser
2022-06-04 15:14:03 +02:00
parent 5c4218e5f6
commit 6c449e55bf
13 changed files with 111 additions and 219 deletions

45
tea.go
View File

@@ -23,7 +23,7 @@ import (
"github.com/containerd/console"
isatty "github.com/mattn/go-isatty"
"github.com/muesli/cancelreader"
te "github.com/muesli/termenv"
"github.com/muesli/termenv"
"golang.org/x/term"
)
@@ -89,9 +89,10 @@ type Program struct {
errs chan error
readLoopDone chan struct{}
output io.Writer // where to send output. this will usually be os.Stdout.
input io.Reader // this will usually be os.Stdin.
cancelReader cancelreader.CancelReader
output *termenv.Output // where to send output. this will usually be os.Stdout.
restoreOutput func() error
input io.Reader // this will usually be os.Stdin.
cancelReader cancelreader.CancelReader
renderer renderer
altScreenActive bool
@@ -253,7 +254,6 @@ func NewProgram(model Model, opts ...ProgramOption) *Program {
p := &Program{
mtx: &sync.Mutex{},
initialModel: model,
output: os.Stdout,
input: os.Stdin,
msgs: make(chan Msg),
CatchPanics: true,
@@ -265,6 +265,16 @@ func NewProgram(model Model, opts ...ProgramOption) *Program {
opt(p)
}
// if no output was set, set it to stdout
if p.output == nil {
p.output = termenv.DefaultOutput()
// cache detected color values
termenv.WithColorCache(true)(p.output)
}
p.restoreOutput, _ = termenv.EnableVirtualTerminalProcessing(p.output)
return p
}
@@ -429,7 +439,7 @@ func (p *Program) StartReturningModel() (Model, error) {
}
defer p.cancelReader.Close() // nolint:errcheck
if f, ok := p.output.(*os.File); ok && isatty.IsTerminal(f.Fd()) {
if f, ok := p.output.TTY().(*os.File); ok && isatty.IsTerminal(f.Fd()) {
// Get the initial terminal size and send it to the program.
go func() {
w, h, err := term.GetSize(int(f.Fd()))
@@ -527,7 +537,7 @@ func (p *Program) StartReturningModel() (Model, error) {
p.DisableMouseAllMotion()
case hideCursorMsg:
hideCursor(p.output)
p.output.HideCursor()
case execMsg:
// NB: this blocks.
@@ -606,6 +616,10 @@ func (p *Program) shutdown(kill bool) {
p.DisableMouseCellMotion()
p.DisableMouseAllMotion()
_ = p.restoreTerminalState()
if p.restoreOutput != nil {
_ = p.restoreOutput()
}
}
// EnterAltScreen enters the alternate screen buffer, which consumes the entire
@@ -620,7 +634,8 @@ func (p *Program) EnterAltScreen() {
return
}
enterAltScreen(p.output)
p.output.AltScreen()
p.output.MoveCursor(0, 0)
p.altScreenActive = true
if p.renderer != nil {
@@ -639,7 +654,7 @@ func (p *Program) ExitAltScreen() {
return
}
exitAltScreen(p.output)
p.output.ExitAltScreen()
p.altScreenActive = false
if p.renderer != nil {
@@ -654,7 +669,8 @@ func (p *Program) ExitAltScreen() {
func (p *Program) EnableMouseCellMotion() {
p.mtx.Lock()
defer p.mtx.Unlock()
fmt.Fprintf(p.output, te.CSI+te.EnableMouseCellMotionSeq)
p.output.EnableMouseCellMotion()
}
// DisableMouseCellMotion disables Mouse Cell Motion tracking. This will be
@@ -664,7 +680,8 @@ func (p *Program) EnableMouseCellMotion() {
func (p *Program) DisableMouseCellMotion() {
p.mtx.Lock()
defer p.mtx.Unlock()
fmt.Fprintf(p.output, te.CSI+te.DisableMouseCellMotionSeq)
p.output.DisableMouseCellMotion()
}
// EnableMouseAllMotion enables mouse click, release, wheel and motion events,
@@ -675,7 +692,8 @@ func (p *Program) DisableMouseCellMotion() {
func (p *Program) EnableMouseAllMotion() {
p.mtx.Lock()
defer p.mtx.Unlock()
fmt.Fprintf(p.output, te.CSI+te.EnableMouseAllMotionSeq)
p.output.EnableMouseAllMotion()
}
// DisableMouseAllMotion disables All Motion mouse tracking. This will be
@@ -685,7 +703,8 @@ func (p *Program) EnableMouseAllMotion() {
func (p *Program) DisableMouseAllMotion() {
p.mtx.Lock()
defer p.mtx.Unlock()
fmt.Fprintf(p.output, te.CSI+te.DisableMouseAllMotionSeq)
p.output.DisableMouseAllMotion()
}
// ReleaseTerminal restores the original terminal state and cancels the input