mirror of
https://github.com/restic/restic.git
synced 2025-11-04 15:21:00 +00:00
termstatus: centralize OutputIsTerminal checks
This commit is contained in:
parent
c745e4221e
commit
1ae2d08d1b
14 changed files with 85 additions and 68 deletions
|
|
@ -523,7 +523,7 @@ func runBackup(ctx context.Context, opts BackupOptions, gopts GlobalOptions, ter
|
||||||
progressPrinter = backup.NewTextProgress(term, gopts.verbosity)
|
progressPrinter = backup.NewTextProgress(term, gopts.verbosity)
|
||||||
}
|
}
|
||||||
progressReporter := backup.NewProgress(progressPrinter,
|
progressReporter := backup.NewProgress(progressPrinter,
|
||||||
calculateProgressInterval(!gopts.Quiet, gopts.JSON))
|
calculateProgressInterval(!gopts.Quiet, gopts.JSON, term.CanUpdateStatus()))
|
||||||
defer progressReporter.Done()
|
defer progressReporter.Done()
|
||||||
|
|
||||||
// rejectByNameFuncs collect functions that can reject items from the backup based on path only
|
// rejectByNameFuncs collect functions that can reject items from the backup based on path only
|
||||||
|
|
|
||||||
|
|
@ -540,5 +540,6 @@ func (p *jsonErrorPrinter) E(msg string, args ...interface{}) {
|
||||||
}
|
}
|
||||||
func (*jsonErrorPrinter) S(_ string, _ ...interface{}) {}
|
func (*jsonErrorPrinter) S(_ string, _ ...interface{}) {}
|
||||||
func (*jsonErrorPrinter) P(_ string, _ ...interface{}) {}
|
func (*jsonErrorPrinter) P(_ string, _ ...interface{}) {}
|
||||||
|
func (*jsonErrorPrinter) PT(_ string, _ ...interface{}) {}
|
||||||
func (*jsonErrorPrinter) V(_ string, _ ...interface{}) {}
|
func (*jsonErrorPrinter) V(_ string, _ ...interface{}) {}
|
||||||
func (*jsonErrorPrinter) VV(_ string, _ ...interface{}) {}
|
func (*jsonErrorPrinter) VV(_ string, _ ...interface{}) {}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"github.com/restic/restic/internal/dump"
|
"github.com/restic/restic/internal/dump"
|
||||||
"github.com/restic/restic/internal/errors"
|
"github.com/restic/restic/internal/errors"
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
"github.com/restic/restic/internal/terminal"
|
|
||||||
"github.com/restic/restic/internal/ui"
|
"github.com/restic/restic/internal/ui"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
@ -180,7 +179,7 @@ func runDump(ctx context.Context, opts DumpOptions, gopts GlobalOptions, args []
|
||||||
}
|
}
|
||||||
|
|
||||||
outputFileWriter := term.OutputRaw()
|
outputFileWriter := term.OutputRaw()
|
||||||
canWriteArchiveFunc := checkStdoutArchive
|
canWriteArchiveFunc := checkStdoutArchive(term)
|
||||||
|
|
||||||
if opts.Target != "" {
|
if opts.Target != "" {
|
||||||
file, err := os.Create(opts.Target)
|
file, err := os.Create(opts.Target)
|
||||||
|
|
@ -204,9 +203,9 @@ func runDump(ctx context.Context, opts DumpOptions, gopts GlobalOptions, args []
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkStdoutArchive() error {
|
func checkStdoutArchive(term ui.Terminal) func() error {
|
||||||
if terminal.StdoutIsTerminal() {
|
if term.OutputIsTerminal() {
|
||||||
return fmt.Errorf("stdout is the terminal, please redirect output")
|
return func() error { return fmt.Errorf("stdout is the terminal, please redirect output") }
|
||||||
}
|
}
|
||||||
return nil
|
return func() error { return nil }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/restic/restic/internal/errors"
|
"github.com/restic/restic/internal/errors"
|
||||||
"github.com/restic/restic/internal/terminal"
|
|
||||||
"github.com/restic/restic/internal/ui"
|
"github.com/restic/restic/internal/ui"
|
||||||
"github.com/restic/restic/internal/ui/progress"
|
"github.com/restic/restic/internal/ui/progress"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
@ -76,9 +75,7 @@ func writeManpages(root *cobra.Command, dir string, printer progress.Printer) er
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeCompletion(filename string, shell string, generate func(w io.Writer) error, printer progress.Printer, gopts GlobalOptions) (err error) {
|
func writeCompletion(filename string, shell string, generate func(w io.Writer) error, printer progress.Printer, gopts GlobalOptions) (err error) {
|
||||||
if terminal.StdoutIsTerminal() {
|
printer.PT("writing %s completion file to %v", shell, filename)
|
||||||
printer.P("writing %s completion file to %v", shell, filename)
|
|
||||||
}
|
|
||||||
var outWriter io.Writer
|
var outWriter io.Writer
|
||||||
if filename != "-" {
|
if filename != "-" {
|
||||||
var outFile *os.File
|
var outFile *os.File
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions,
|
||||||
printer = restoreui.NewTextProgress(term, gopts.verbosity)
|
printer = restoreui.NewTextProgress(term, gopts.verbosity)
|
||||||
}
|
}
|
||||||
|
|
||||||
progress := restoreui.NewProgress(printer, calculateProgressInterval(!gopts.Quiet, gopts.JSON))
|
progress := restoreui.NewProgress(printer, calculateProgressInterval(!gopts.Quiet, gopts.JSON, term.CanUpdateStatus()))
|
||||||
res := restorer.NewRestorer(repo, sn, restorer.Options{
|
res := restorer.NewRestorer(repo, sn, restorer.Options{
|
||||||
DryRun: opts.DryRun,
|
DryRun: opts.DryRun,
|
||||||
Sparse: opts.Sparse,
|
Sparse: opts.Sparse,
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,13 @@ import (
|
||||||
|
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
rtest "github.com/restic/restic/internal/test"
|
rtest "github.com/restic/restic/internal/test"
|
||||||
|
"github.com/restic/restic/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testRunTag(t testing.TB, opts TagOptions, gopts GlobalOptions) {
|
func testRunTag(t testing.TB, opts TagOptions, gopts GlobalOptions) {
|
||||||
rtest.OK(t, runTag(context.TODO(), opts, gopts, nil, []string{}))
|
rtest.OK(t, withTermStatus(gopts, func(ctx context.Context, term ui.Terminal) error {
|
||||||
|
return runTag(context.TODO(), opts, gopts, term, []string{})
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTag(t *testing.T) {
|
func TestTag(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -267,9 +267,7 @@ func ReadPassword(ctx context.Context, opts GlobalOptions, prompt string, printe
|
||||||
if terminal.StdinIsTerminal() {
|
if terminal.StdinIsTerminal() {
|
||||||
password, err = terminal.ReadPassword(ctx, os.Stdin, os.Stderr, prompt)
|
password, err = terminal.ReadPassword(ctx, os.Stdin, os.Stderr, prompt)
|
||||||
} else {
|
} else {
|
||||||
if terminal.StdoutIsTerminal() {
|
printer.PT("reading repository password from stdin")
|
||||||
printer.P("reading repository password from stdin")
|
|
||||||
}
|
|
||||||
password, err = readPassword(os.Stdin)
|
password, err = readPassword(os.Stdin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -385,19 +383,15 @@ func OpenRepository(ctx context.Context, opts GlobalOptions, printer progress.Pr
|
||||||
return nil, errors.Fatalf("%s", err)
|
return nil, errors.Fatalf("%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if terminal.StdoutIsTerminal() && !opts.JSON {
|
id := s.Config().ID
|
||||||
id := s.Config().ID
|
if len(id) > 8 {
|
||||||
if len(id) > 8 {
|
id = id[:8]
|
||||||
id = id[:8]
|
|
||||||
}
|
|
||||||
if !opts.JSON {
|
|
||||||
extra := ""
|
|
||||||
if s.Config().Version >= 2 {
|
|
||||||
extra = ", compression level " + opts.Compression.String()
|
|
||||||
}
|
|
||||||
printer.P("repository %v opened (version %v%s)", id, s.Config().Version, extra)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
extra := ""
|
||||||
|
if s.Config().Version >= 2 {
|
||||||
|
extra = ", compression level " + opts.Compression.String()
|
||||||
|
}
|
||||||
|
printer.PT("repository %v opened (version %v%s)", id, s.Config().Version, extra)
|
||||||
|
|
||||||
if opts.NoCache {
|
if opts.NoCache {
|
||||||
return s, nil
|
return s, nil
|
||||||
|
|
@ -409,8 +403,8 @@ func OpenRepository(ctx context.Context, opts GlobalOptions, printer progress.Pr
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Created && !opts.JSON && terminal.StdoutIsTerminal() {
|
if c.Created {
|
||||||
printer.P("created new cache in %v", c.Base)
|
printer.PT("created new cache in %v", c.Base)
|
||||||
}
|
}
|
||||||
|
|
||||||
// start using the cache
|
// start using the cache
|
||||||
|
|
@ -428,9 +422,7 @@ func OpenRepository(ctx context.Context, opts GlobalOptions, printer progress.Pr
|
||||||
|
|
||||||
// cleanup old cache dirs if instructed to do so
|
// cleanup old cache dirs if instructed to do so
|
||||||
if opts.CleanupCache {
|
if opts.CleanupCache {
|
||||||
if terminal.StdoutIsTerminal() && !opts.JSON {
|
printer.PT("removing %d old cache dirs from %v", len(oldCacheDirs), c.Base)
|
||||||
printer.P("removing %d old cache dirs from %v", len(oldCacheDirs), c.Base)
|
|
||||||
}
|
|
||||||
for _, item := range oldCacheDirs {
|
for _, item := range oldCacheDirs {
|
||||||
dir := filepath.Join(c.Base, item.Name())
|
dir := filepath.Join(c.Base, item.Name())
|
||||||
err = os.RemoveAll(dir)
|
err = os.RemoveAll(dir)
|
||||||
|
|
@ -439,10 +431,8 @@ func OpenRepository(ctx context.Context, opts GlobalOptions, printer progress.Pr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if terminal.StdoutIsTerminal() {
|
printer.PT("found %d old cache directories in %v, run `restic cache --cleanup` to remove them",
|
||||||
printer.P("found %d old cache directories in %v, run `restic cache --cleanup` to remove them",
|
len(oldCacheDirs), c.Base)
|
||||||
len(oldCacheDirs), c.Base)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/restic/restic/internal/terminal"
|
|
||||||
"github.com/restic/restic/internal/ui"
|
"github.com/restic/restic/internal/ui"
|
||||||
"github.com/restic/restic/internal/ui/progress"
|
"github.com/restic/restic/internal/ui/progress"
|
||||||
)
|
)
|
||||||
|
|
@ -14,7 +13,7 @@ import (
|
||||||
// calculateProgressInterval returns the interval configured via RESTIC_PROGRESS_FPS
|
// calculateProgressInterval returns the interval configured via RESTIC_PROGRESS_FPS
|
||||||
// or if unset returns an interval for 60fps on interactive terminals and 0 (=disabled)
|
// or if unset returns an interval for 60fps on interactive terminals and 0 (=disabled)
|
||||||
// for non-interactive terminals or when run using the --quiet flag
|
// for non-interactive terminals or when run using the --quiet flag
|
||||||
func calculateProgressInterval(show bool, json bool) time.Duration {
|
func calculateProgressInterval(show bool, json bool, canUpdateStatus bool) time.Duration {
|
||||||
interval := time.Second / 60
|
interval := time.Second / 60
|
||||||
fps, err := strconv.ParseFloat(os.Getenv("RESTIC_PROGRESS_FPS"), 64)
|
fps, err := strconv.ParseFloat(os.Getenv("RESTIC_PROGRESS_FPS"), 64)
|
||||||
if err == nil && fps > 0 {
|
if err == nil && fps > 0 {
|
||||||
|
|
@ -22,7 +21,7 @@ func calculateProgressInterval(show bool, json bool) time.Duration {
|
||||||
fps = 60
|
fps = 60
|
||||||
}
|
}
|
||||||
interval = time.Duration(float64(time.Second) / fps)
|
interval = time.Duration(float64(time.Second) / fps)
|
||||||
} else if !json && !terminal.StdoutCanUpdateStatus() || !show {
|
} else if !json && !canUpdateStatus || !show {
|
||||||
interval = 0
|
interval = 0
|
||||||
}
|
}
|
||||||
return interval
|
return interval
|
||||||
|
|
@ -33,7 +32,7 @@ func newTerminalProgressMax(show bool, max uint64, description string, term ui.T
|
||||||
if !show {
|
if !show {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
interval := calculateProgressInterval(show, false)
|
interval := calculateProgressInterval(show, false, term.CanUpdateStatus())
|
||||||
|
|
||||||
return progress.NewCounter(interval, max, func(v uint64, max uint64, d time.Duration, final bool) {
|
return progress.NewCounter(interval, max, func(v uint64, max uint64, d time.Duration, final bool) {
|
||||||
var status string
|
var status string
|
||||||
|
|
@ -65,7 +64,7 @@ func (t *terminalProgressPrinter) NewCounter(description string) *progress.Count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *terminalProgressPrinter) NewCounterTerminalOnly(description string) *progress.Counter {
|
func (t *terminalProgressPrinter) NewCounterTerminalOnly(description string) *progress.Counter {
|
||||||
return newTerminalProgressMax(t.show && terminal.StdoutIsTerminal(), 0, description, t.term)
|
return newTerminalProgressMax(t.show && t.term.OutputIsTerminal(), 0, description, t.term)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTerminalProgressPrinter(json bool, verbosity uint, term ui.Terminal) progress.Printer {
|
func newTerminalProgressPrinter(json bool, verbosity uint, term ui.Terminal) progress.Printer {
|
||||||
|
|
|
||||||
|
|
@ -10,18 +10,11 @@ func StdinIsTerminal() bool {
|
||||||
return term.IsTerminal(int(os.Stdin.Fd()))
|
return term.IsTerminal(int(os.Stdin.Fd()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func StdoutIsTerminal() bool {
|
func OutputIsTerminal(fd uintptr) bool {
|
||||||
// mintty on windows can use pipes which behave like a posix terminal,
|
// mintty on windows can use pipes which behave like a posix terminal,
|
||||||
// but which are not a terminal handle
|
// but which are not a terminal handle. Thus also check `CanUpdateStatus`,
|
||||||
return term.IsTerminal(int(os.Stdout.Fd())) || StdoutCanUpdateStatus()
|
// which is able to detect such pipes.
|
||||||
}
|
return term.IsTerminal(int(fd)) || CanUpdateStatus(fd)
|
||||||
|
|
||||||
func StdoutCanUpdateStatus() bool {
|
|
||||||
return CanUpdateStatus(os.Stdout.Fd())
|
|
||||||
}
|
|
||||||
|
|
||||||
func StdoutWidth() int {
|
|
||||||
return Width(os.Stdout.Fd())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Width(fd uintptr) int {
|
func Width(fd uintptr) int {
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,17 @@ func (m *Message) S(msg string, args ...interface{}) {
|
||||||
m.term.Print(fmt.Sprintf(msg, args...))
|
m.term.Print(fmt.Sprintf(msg, args...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// P prints a message if verbosity >= 1 (neither --quiet nor --verbose is specified),
|
// PT prints a message if verbosity >= 1 (neither --quiet nor --verbose is specified)
|
||||||
// this is used for normal messages which are not errors.
|
// and stdout points to a terminal.
|
||||||
|
// This is used for informational messages.
|
||||||
|
func (m *Message) PT(msg string, args ...interface{}) {
|
||||||
|
if m.term.OutputIsTerminal() && m.v >= 1 {
|
||||||
|
m.term.Print(fmt.Sprintf(msg, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// P prints a message if verbosity >= 1 (neither --quiet nor --verbose is specified).
|
||||||
|
// This is used for normal messages which are not errors.
|
||||||
func (m *Message) P(msg string, args ...interface{}) {
|
func (m *Message) P(msg string, args ...interface{}) {
|
||||||
if m.v >= 1 {
|
if m.v >= 1 {
|
||||||
m.term.Print(fmt.Sprintf(msg, args...))
|
m.term.Print(fmt.Sprintf(msg, args...))
|
||||||
|
|
|
||||||
|
|
@ -28,3 +28,7 @@ func (m *MockTerminal) CanUpdateStatus() bool {
|
||||||
func (m *MockTerminal) OutputRaw() io.Writer {
|
func (m *MockTerminal) OutputRaw() io.Writer {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MockTerminal) OutputIsTerminal() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,10 @@ type Printer interface {
|
||||||
// that are not errors. The message is even printed if --quiet is specified.
|
// that are not errors. The message is even printed if --quiet is specified.
|
||||||
// Appends a newline if not present.
|
// Appends a newline if not present.
|
||||||
S(msg string, args ...interface{})
|
S(msg string, args ...interface{})
|
||||||
|
// PT prints a message if verbosity >= 1 (neither --quiet nor --verbose is specified)
|
||||||
|
// and stdout points to a terminal.
|
||||||
|
// This is used for informational messages.
|
||||||
|
PT(msg string, args ...interface{})
|
||||||
// P prints a message if verbosity >= 1 (neither --quiet nor --verbose is specified),
|
// P prints a message if verbosity >= 1 (neither --quiet nor --verbose is specified),
|
||||||
// this is used for normal messages which are not errors. Appends a newline if not present.
|
// this is used for normal messages which are not errors. Appends a newline if not present.
|
||||||
P(msg string, args ...interface{})
|
P(msg string, args ...interface{})
|
||||||
|
|
@ -47,6 +51,8 @@ func (*NoopPrinter) E(_ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func (*NoopPrinter) S(_ string, _ ...interface{}) {}
|
func (*NoopPrinter) S(_ string, _ ...interface{}) {}
|
||||||
|
|
||||||
|
func (*NoopPrinter) PT(_ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func (*NoopPrinter) P(_ string, _ ...interface{}) {}
|
func (*NoopPrinter) P(_ string, _ ...interface{}) {}
|
||||||
|
|
||||||
func (*NoopPrinter) V(_ string, _ ...interface{}) {}
|
func (*NoopPrinter) V(_ string, _ ...interface{}) {}
|
||||||
|
|
@ -82,6 +88,10 @@ func (p *TestPrinter) S(msg string, args ...interface{}) {
|
||||||
p.t.Logf("stdout: "+msg, args...)
|
p.t.Logf("stdout: "+msg, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *TestPrinter) PT(msg string, args ...interface{}) {
|
||||||
|
p.t.Logf("stdout(terminal): "+msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *TestPrinter) P(msg string, args ...interface{}) {
|
func (p *TestPrinter) P(msg string, args ...interface{}) {
|
||||||
p.t.Logf("print: "+msg, args...)
|
p.t.Logf("print: "+msg, args...)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,4 +17,5 @@ type Terminal interface {
|
||||||
// other option. Must not be used in combination with Print, Error, SetStatus
|
// other option. Must not be used in combination with Print, Error, SetStatus
|
||||||
// or any other method that writes to the terminal.
|
// or any other method that writes to the terminal.
|
||||||
OutputRaw() io.Writer
|
OutputRaw() io.Writer
|
||||||
|
OutputIsTerminal() bool
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,14 @@ var _ ui.Terminal = &Terminal{}
|
||||||
// updated. When the output is redirected to a file, the status lines are not
|
// updated. When the output is redirected to a file, the status lines are not
|
||||||
// printed.
|
// printed.
|
||||||
type Terminal struct {
|
type Terminal struct {
|
||||||
wr io.Writer
|
wr io.Writer
|
||||||
fd uintptr
|
fd uintptr
|
||||||
errWriter io.Writer
|
errWriter io.Writer
|
||||||
msg chan message
|
msg chan message
|
||||||
status chan status
|
status chan status
|
||||||
canUpdateStatus bool
|
outputIsTerminal bool
|
||||||
lastStatusLen int
|
canUpdateStatus bool
|
||||||
|
lastStatusLen int
|
||||||
|
|
||||||
// will be closed when the goroutine which runs Run() terminates, so it'll
|
// will be closed when the goroutine which runs Run() terminates, so it'll
|
||||||
// yield a default value immediately
|
// yield a default value immediately
|
||||||
|
|
@ -65,12 +66,17 @@ func New(wr io.Writer, errWriter io.Writer, disableStatus bool) *Terminal {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
if d, ok := wr.(fder); ok && terminal.CanUpdateStatus(d.Fd()) {
|
if d, ok := wr.(fder); ok {
|
||||||
// only use the fancy status code when we're running on a real terminal.
|
if terminal.CanUpdateStatus(d.Fd()) {
|
||||||
t.canUpdateStatus = true
|
// only use the fancy status code when we're running on a real terminal.
|
||||||
t.fd = d.Fd()
|
t.canUpdateStatus = true
|
||||||
t.clearCurrentLine = terminal.ClearCurrentLine(t.fd)
|
t.fd = d.Fd()
|
||||||
t.moveCursorUp = terminal.MoveCursorUp(t.fd)
|
t.clearCurrentLine = terminal.ClearCurrentLine(t.fd)
|
||||||
|
t.moveCursorUp = terminal.MoveCursorUp(t.fd)
|
||||||
|
}
|
||||||
|
if terminal.OutputIsTerminal(d.Fd()) {
|
||||||
|
t.outputIsTerminal = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return t
|
return t
|
||||||
|
|
@ -88,6 +94,11 @@ func (t *Terminal) OutputRaw() io.Writer {
|
||||||
return t.wr
|
return t.wr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OutputIsTerminal returns whether the output is a terminal.
|
||||||
|
func (t *Terminal) OutputIsTerminal() bool {
|
||||||
|
return t.outputIsTerminal
|
||||||
|
}
|
||||||
|
|
||||||
// Run updates the screen. It should be run in a separate goroutine. When
|
// Run updates the screen. It should be run in a separate goroutine. When
|
||||||
// ctx is cancelled, the status lines are cleanly removed.
|
// ctx is cancelled, the status lines are cleanly removed.
|
||||||
func (t *Terminal) Run(ctx context.Context) {
|
func (t *Terminal) Run(ctx context.Context) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue