termstatus: add stdin and inject into backup command

This commit is contained in:
Michael Eischer
2025-09-18 22:17:21 +02:00
parent ca5b0c0249
commit 96af35555a
14 changed files with 73 additions and 41 deletions

View File

@@ -1,13 +1,11 @@
package terminal
import (
"os"
"golang.org/x/term"
)
func StdinIsTerminal() bool {
return term.IsTerminal(int(os.Stdin.Fd()))
func InputIsTerminal(fd uintptr) bool {
return term.IsTerminal(int(fd))
}
func OutputIsTerminal(fd uintptr) bool {

View File

@@ -25,6 +25,14 @@ func (m *MockTerminal) CanUpdateStatus() bool {
return true
}
func (m *MockTerminal) InputRaw() io.ReadCloser {
return nil
}
func (m *MockTerminal) InputIsTerminal() bool {
return true
}
func (m *MockTerminal) OutputRaw() io.Writer {
return nil
}

View File

@@ -13,6 +13,8 @@ type Terminal interface {
SetStatus(lines []string)
// CanUpdateStatus returns true if the terminal can update the status lines.
CanUpdateStatus() bool
InputRaw() io.ReadCloser
InputIsTerminal() bool
// OutputRaw returns the output writer. Should only be used if there is no
// other option. Must not be used in combination with Print, Error, SetStatus
// or any other method that writes to the terminal.

View File

@@ -17,14 +17,16 @@ var _ ui.Terminal = &Terminal{}
// updated. When the output is redirected to a file, the status lines are not
// printed.
type Terminal struct {
rd io.ReadCloser
wr io.Writer
fd uintptr
errWriter io.Writer
msg chan message
status chan status
lastStatusLen int
inputIsTerminal bool
outputIsTerminal bool
canUpdateStatus bool
lastStatusLen int
// will be closed when the goroutine which runs Run() terminates, so it'll
// yield a default value immediately
@@ -56,12 +58,12 @@ type fder interface {
// defer cancel()
// // do stuff
// ```
func Setup(stdout, stderr io.Writer, quiet bool) (*Terminal, func()) {
func Setup(stdin io.ReadCloser, stdout, stderr io.Writer, quiet bool) (*Terminal, func()) {
var wg sync.WaitGroup
// only shutdown once cancel is called to ensure that no output is lost
cancelCtx, cancel := context.WithCancel(context.Background())
term := New(stdout, stderr, quiet)
term := New(stdin, stdout, stderr, quiet)
wg.Add(1)
go func() {
defer wg.Done()
@@ -82,8 +84,9 @@ func Setup(stdout, stderr io.Writer, quiet bool) (*Terminal, func()) {
// normal output (via Print/Printf) are written to wr, error messages are
// written to errWriter. If disableStatus is set to true, no status messages
// are printed even if the terminal supports it.
func New(wr io.Writer, errWriter io.Writer, disableStatus bool) *Terminal {
func New(rd io.ReadCloser, wr io.Writer, errWriter io.Writer, disableStatus bool) *Terminal {
t := &Terminal{
rd: rd,
wr: wr,
errWriter: errWriter,
msg: make(chan message),
@@ -95,6 +98,12 @@ func New(wr io.Writer, errWriter io.Writer, disableStatus bool) *Terminal {
return t
}
if d, ok := rd.(fder); ok {
if terminal.InputIsTerminal(d.Fd()) {
t.inputIsTerminal = true
}
}
if d, ok := wr.(fder); ok {
if terminal.CanUpdateStatus(d.Fd()) {
// only use the fancy status code when we're running on a real terminal.
@@ -111,6 +120,16 @@ func New(wr io.Writer, errWriter io.Writer, disableStatus bool) *Terminal {
return t
}
// InputIsTerminal returns whether the input is a terminal.
func (t *Terminal) InputIsTerminal() bool {
return t.inputIsTerminal
}
// InputRaw returns the input reader.
func (t *Terminal) InputRaw() io.ReadCloser {
return t.rd
}
// CanUpdateStatus return whether the status output is updated in place.
func (t *Terminal) CanUpdateStatus() bool {
return t.canUpdateStatus

View File

@@ -13,7 +13,7 @@ import (
func TestSetStatus(t *testing.T) {
var buf bytes.Buffer
term := New(&buf, io.Discard, false)
term := New(nil, &buf, io.Discard, false)
term.canUpdateStatus = true
term.fd = ^uintptr(0)