Move smart and dumb terminals into separate implementations

Support for smart and dumb terminals are implemented in writer.go,
which makes dumb terminals much more complicated than necessary.
Move smart and dumb terminals into two separate implementations
of StatusOutput, with common code moved into a shared formatter
class.

Test: not yet
Change-Id: I59bbdae479f138b46cd0f03092720a3303e8f0fe
This commit is contained in:
Colin Cross
2019-06-08 18:58:00 -07:00
parent dde49cb687
commit ce525350f4
7 changed files with 355 additions and 233 deletions

View File

@@ -15,21 +15,9 @@
package terminal
import (
"fmt"
"strings"
"time"
"android/soong/ui/status"
)
type statusOutput struct {
writer Writer
format string
start time.Time
quiet bool
}
// NewStatusOutput returns a StatusOutput that represents the
// current build status similarly to Ninja's built-in terminal
// output.
@@ -37,109 +25,11 @@ type statusOutput struct {
// statusFormat takes nearly all the same options as NINJA_STATUS.
// %c is currently unsupported.
func NewStatusOutput(w Writer, statusFormat string, quietBuild bool) status.StatusOutput {
return &statusOutput{
writer: w,
format: statusFormat,
formatter := newFormatter(statusFormat, quietBuild)
start: time.Now(),
quiet: quietBuild,
}
}
func (s *statusOutput) Message(level status.MsgLevel, message string) {
if level >= status.ErrorLvl {
s.writer.Print(fmt.Sprintf("FAILED: %s", message))
} else if level > status.StatusLvl {
s.writer.Print(fmt.Sprintf("%s%s", level.Prefix(), message))
} else if level == status.StatusLvl {
s.writer.StatusLine(message)
}
}
func (s *statusOutput) StartAction(action *status.Action, counts status.Counts) {
if !s.writer.isSmartTerminal() {
return
}
str := action.Description
if str == "" {
str = action.Command
}
s.writer.StatusLine(s.progress(counts) + str)
}
func (s *statusOutput) FinishAction(result status.ActionResult, counts status.Counts) {
str := result.Description
if str == "" {
str = result.Command
}
progress := s.progress(counts) + str
if result.Error != nil {
targets := strings.Join(result.Outputs, " ")
if s.quiet || result.Command == "" {
s.writer.StatusAndMessage(progress, fmt.Sprintf("FAILED: %s\n%s", targets, result.Output))
} else {
s.writer.StatusAndMessage(progress, fmt.Sprintf("FAILED: %s\n%s\n%s", targets, result.Command, result.Output))
}
} else if result.Output != "" {
s.writer.StatusAndMessage(progress, result.Output)
if w.isSmartTerminal() {
return NewSmartStatusOutput(w, formatter)
} else {
s.writer.StatusLine(progress)
return NewDumbStatusOutput(w, formatter)
}
}
func (s *statusOutput) Flush() {}
func (s *statusOutput) progress(counts status.Counts) string {
if s.format == "" {
return fmt.Sprintf("[%3d%% %d/%d] ", 100*counts.FinishedActions/counts.TotalActions, counts.FinishedActions, counts.TotalActions)
}
buf := &strings.Builder{}
for i := 0; i < len(s.format); i++ {
c := s.format[i]
if c != '%' {
buf.WriteByte(c)
continue
}
i = i + 1
if i == len(s.format) {
buf.WriteByte(c)
break
}
c = s.format[i]
switch c {
case '%':
buf.WriteByte(c)
case 's':
fmt.Fprintf(buf, "%d", counts.StartedActions)
case 't':
fmt.Fprintf(buf, "%d", counts.TotalActions)
case 'r':
fmt.Fprintf(buf, "%d", counts.RunningActions)
case 'u':
fmt.Fprintf(buf, "%d", counts.TotalActions-counts.StartedActions)
case 'f':
fmt.Fprintf(buf, "%d", counts.FinishedActions)
case 'o':
fmt.Fprintf(buf, "%.1f", float64(counts.FinishedActions)/time.Since(s.start).Seconds())
case 'c':
// TODO: implement?
buf.WriteRune('?')
case 'p':
fmt.Fprintf(buf, "%3d%%", 100*counts.FinishedActions/counts.TotalActions)
case 'e':
fmt.Fprintf(buf, "%.3f", time.Since(s.start).Seconds())
default:
buf.WriteString("unknown placeholder '")
buf.WriteByte(c)
buf.WriteString("'")
}
}
return buf.String()
}