mirror of
https://github.com/restic/restic.git
synced 2025-12-11 18:47:50 +00:00
termstatus: centralize OutputIsTerminal checks
This commit is contained in:
@@ -10,18 +10,11 @@ func StdinIsTerminal() bool {
|
||||
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,
|
||||
// but which are not a terminal handle
|
||||
return term.IsTerminal(int(os.Stdout.Fd())) || StdoutCanUpdateStatus()
|
||||
}
|
||||
|
||||
func StdoutCanUpdateStatus() bool {
|
||||
return CanUpdateStatus(os.Stdout.Fd())
|
||||
}
|
||||
|
||||
func StdoutWidth() int {
|
||||
return Width(os.Stdout.Fd())
|
||||
// but which are not a terminal handle. Thus also check `CanUpdateStatus`,
|
||||
// which is able to detect such pipes.
|
||||
return term.IsTerminal(int(fd)) || CanUpdateStatus(fd)
|
||||
}
|
||||
|
||||
func Width(fd uintptr) int {
|
||||
|
||||
@@ -30,8 +30,17 @@ func (m *Message) S(msg string, args ...interface{}) {
|
||||
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.
|
||||
// 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.
|
||||
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{}) {
|
||||
if m.v >= 1 {
|
||||
m.term.Print(fmt.Sprintf(msg, args...))
|
||||
|
||||
@@ -28,3 +28,7 @@ func (m *MockTerminal) CanUpdateStatus() bool {
|
||||
func (m *MockTerminal) OutputRaw() io.Writer {
|
||||
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.
|
||||
// Appends a newline if not present.
|
||||
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),
|
||||
// this is used for normal messages which are not errors. Appends a newline if not present.
|
||||
P(msg string, args ...interface{})
|
||||
@@ -47,6 +51,8 @@ func (*NoopPrinter) E(_ string, _ ...interface{}) {}
|
||||
|
||||
func (*NoopPrinter) S(_ string, _ ...interface{}) {}
|
||||
|
||||
func (*NoopPrinter) PT(_ string, _ ...interface{}) {}
|
||||
|
||||
func (*NoopPrinter) P(_ 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...)
|
||||
}
|
||||
|
||||
func (p *TestPrinter) PT(msg string, args ...interface{}) {
|
||||
p.t.Logf("stdout(terminal): "+msg, args...)
|
||||
}
|
||||
|
||||
func (p *TestPrinter) P(msg string, args ...interface{}) {
|
||||
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
|
||||
// or any other method that writes to the terminal.
|
||||
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
|
||||
// printed.
|
||||
type Terminal struct {
|
||||
wr io.Writer
|
||||
fd uintptr
|
||||
errWriter io.Writer
|
||||
msg chan message
|
||||
status chan status
|
||||
canUpdateStatus bool
|
||||
lastStatusLen int
|
||||
wr io.Writer
|
||||
fd uintptr
|
||||
errWriter io.Writer
|
||||
msg chan message
|
||||
status chan status
|
||||
outputIsTerminal bool
|
||||
canUpdateStatus bool
|
||||
lastStatusLen int
|
||||
|
||||
// will be closed when the goroutine which runs Run() terminates, so it'll
|
||||
// yield a default value immediately
|
||||
@@ -65,12 +66,17 @@ func New(wr io.Writer, errWriter io.Writer, disableStatus bool) *Terminal {
|
||||
return t
|
||||
}
|
||||
|
||||
if d, ok := wr.(fder); ok && terminal.CanUpdateStatus(d.Fd()) {
|
||||
// only use the fancy status code when we're running on a real terminal.
|
||||
t.canUpdateStatus = true
|
||||
t.fd = d.Fd()
|
||||
t.clearCurrentLine = terminal.ClearCurrentLine(t.fd)
|
||||
t.moveCursorUp = terminal.MoveCursorUp(t.fd)
|
||||
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.
|
||||
t.canUpdateStatus = true
|
||||
t.fd = d.Fd()
|
||||
t.clearCurrentLine = terminal.ClearCurrentLine(t.fd)
|
||||
t.moveCursorUp = terminal.MoveCursorUp(t.fd)
|
||||
}
|
||||
if terminal.OutputIsTerminal(d.Fd()) {
|
||||
t.outputIsTerminal = true
|
||||
}
|
||||
}
|
||||
|
||||
return t
|
||||
@@ -88,6 +94,11 @@ func (t *Terminal) OutputRaw() io.Writer {
|
||||
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
|
||||
// ctx is cancelled, the status lines are cleanly removed.
|
||||
func (t *Terminal) Run(ctx context.Context) {
|
||||
|
||||
Reference in New Issue
Block a user