From f7e4c92f385ff7253c47502c8711109dc308005b Mon Sep 17 00:00:00 2001 From: Benjamin Elder Date: Sun, 10 Nov 2019 22:36:37 -0800 Subject: [PATCH] handle windows terminals better --- go.mod | 1 - go.sum | 4 ---- pkg/cmd/logger.go | 2 +- pkg/internal/util/cli/spinner.go | 25 +++++++++++++++---------- pkg/internal/util/env/term.go | 19 +++++++++++++++++++ 5 files changed, 35 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 18373e1f..9ae67fe7 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.13 require ( github.com/alessio/shellescape v0.0.0-20190409004728-b115ca0f9053 github.com/evanphx/json-patch v4.5.0+incompatible - github.com/mattn/go-colorable v0.1.4 github.com/mattn/go-isatty v0.0.10 github.com/pkg/errors v0.8.1 github.com/spf13/cobra v0.0.5 diff --git a/go.sum b/go.sum index f6beb343..3ce58e47 100644 --- a/go.sum +++ b/go.sum @@ -52,9 +52,6 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -101,7 +98,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd h1:3x5uuvBgE6oaXJjCOvpCC1IpgJogqQ+PqGGU3ZxAgII= diff --git a/pkg/cmd/logger.go b/pkg/cmd/logger.go index c0d4dd37..0440f3fe 100644 --- a/pkg/cmd/logger.go +++ b/pkg/cmd/logger.go @@ -30,7 +30,7 @@ import ( // This logger writes to os.Stderr func NewLogger() log.Logger { var writer io.Writer = os.Stderr - if env.IsTerminal(writer) { + if env.IsSmartTerminal(writer) { writer = cli.NewSpinner(writer) } return cli.NewLogger(writer, 0) diff --git a/pkg/internal/util/cli/spinner.go b/pkg/internal/util/cli/spinner.go index 9c9fadac..9d71af3a 100644 --- a/pkg/internal/util/cli/spinner.go +++ b/pkg/internal/util/cli/spinner.go @@ -19,11 +19,9 @@ package cli import ( "fmt" "io" - "os" + "runtime" "sync" "time" - - colorable "github.com/mattn/go-colorable" ) // custom CLI loading spinner for kind @@ -57,6 +55,8 @@ type Spinner struct { ticker *time.Ticker // signals that it is time to write a frame prefix string suffix string + // format string used to write a frame, depends on the host OS / terminal + frameFormat string } // spinner implements writer @@ -65,14 +65,19 @@ var _ io.Writer = &Spinner{} // NewSpinner initializes and returns a new Spinner that will write to w // NOTE: w should be os.Stderr or similar, and it should be a Terminal func NewSpinner(w io.Writer) *Spinner { - if v, ok := w.(*os.File); ok { - w = colorable.NewColorable(v) + frameFormat := "\x1b[?7l\x1b[2K\r%s%s%s\x1b[?7h" + // toggling wrapping seems to behave poorly on windows + // in general only the simplest escape codes behave well at the moment, + // and only in newer shells + if runtime.GOOS == "windows" { + frameFormat = "\x1b[2K\r%s%s%s" } return &Spinner{ - stop: make(chan struct{}, 1), - stopped: make(chan struct{}), - mu: &sync.Mutex{}, - writer: w, + stop: make(chan struct{}, 1), + stopped: make(chan struct{}), + mu: &sync.Mutex{}, + writer: w, + frameFormat: frameFormat, } } @@ -123,7 +128,7 @@ func (s *Spinner) Start() { func() { s.mu.Lock() defer s.mu.Unlock() - fmt.Fprintf(s.writer, "\x1b[?7l\x1b[2K\r%s%s%s\x1b[?7h", s.prefix, frame, s.suffix) + fmt.Fprintf(s.writer, s.frameFormat, s.prefix, frame, s.suffix) }() } } diff --git a/pkg/internal/util/env/term.go b/pkg/internal/util/env/term.go index fec35704..c40fb1b5 100644 --- a/pkg/internal/util/env/term.go +++ b/pkg/internal/util/env/term.go @@ -19,6 +19,7 @@ package env import ( "io" "os" + "runtime" isatty "github.com/mattn/go-isatty" ) @@ -30,3 +31,21 @@ func IsTerminal(w io.Writer) bool { } return false } + +// IsSmartTerminal returns true if the writer w is a terminal AND +// we think that the terminal is smart enough to use VT escape codes etc. +func IsSmartTerminal(w io.Writer) bool { + if !IsTerminal(w) { + return false + } + // explicitly dumb terminals are not smart + if os.Getenv("TERM") == "dumb" { + return false + } + // On Windows WT_SESSION is set by the modern terminal component. + // Older terminals have poor support for UTF-8, VT escape codes, etc. + if runtime.GOOS == "windows" && os.Getenv("WT_SESSION") == "" { + return false + } + return true +}