...

Source file src/github.com/muesli/termenv/output.go

Documentation: github.com/muesli/termenv

     1  package termenv
     2  
     3  import (
     4  	"io"
     5  	"os"
     6  	"sync"
     7  )
     8  
     9  var (
    10  	// output is the default global output.
    11  	output = NewOutput(os.Stdout)
    12  )
    13  
    14  // File represents a file descriptor.
    15  type File interface {
    16  	io.ReadWriter
    17  	Fd() uintptr
    18  }
    19  
    20  // OutputOption sets an option on Output.
    21  type OutputOption = func(*Output)
    22  
    23  // Output is a terminal output.
    24  type Output struct {
    25  	Profile
    26  	tty     io.Writer
    27  	environ Environ
    28  
    29  	assumeTTY bool
    30  	unsafe    bool
    31  	cache     bool
    32  	fgSync    *sync.Once
    33  	fgColor   Color
    34  	bgSync    *sync.Once
    35  	bgColor   Color
    36  }
    37  
    38  // Environ is an interface for getting environment variables.
    39  type Environ interface {
    40  	Environ() []string
    41  	Getenv(string) string
    42  }
    43  
    44  type osEnviron struct{}
    45  
    46  func (oe *osEnviron) Environ() []string {
    47  	return os.Environ()
    48  }
    49  
    50  func (oe *osEnviron) Getenv(key string) string {
    51  	return os.Getenv(key)
    52  }
    53  
    54  // DefaultOutput returns the default global output.
    55  func DefaultOutput() *Output {
    56  	return output
    57  }
    58  
    59  // SetDefaultOutput sets the default global output.
    60  func SetDefaultOutput(o *Output) {
    61  	output = o
    62  }
    63  
    64  // NewOutput returns a new Output for the given file descriptor.
    65  func NewOutput(tty io.Writer, opts ...OutputOption) *Output {
    66  	o := &Output{
    67  		tty:     tty,
    68  		environ: &osEnviron{},
    69  		Profile: -1,
    70  		fgSync:  &sync.Once{},
    71  		fgColor: NoColor{},
    72  		bgSync:  &sync.Once{},
    73  		bgColor: NoColor{},
    74  	}
    75  
    76  	if o.tty == nil {
    77  		o.tty = os.Stdout
    78  	}
    79  	for _, opt := range opts {
    80  		opt(o)
    81  	}
    82  	if o.Profile < 0 {
    83  		o.Profile = o.EnvColorProfile()
    84  	}
    85  
    86  	return o
    87  }
    88  
    89  // WithEnvironment returns a new OutputOption for the given environment.
    90  func WithEnvironment(environ Environ) OutputOption {
    91  	return func(o *Output) {
    92  		o.environ = environ
    93  	}
    94  }
    95  
    96  // WithProfile returns a new OutputOption for the given profile.
    97  func WithProfile(profile Profile) OutputOption {
    98  	return func(o *Output) {
    99  		o.Profile = profile
   100  	}
   101  }
   102  
   103  // WithColorCache returns a new OutputOption with fore- and background color values
   104  // pre-fetched and cached.
   105  func WithColorCache(v bool) OutputOption {
   106  	return func(o *Output) {
   107  		o.cache = v
   108  
   109  		// cache the values now
   110  		_ = o.ForegroundColor()
   111  		_ = o.BackgroundColor()
   112  	}
   113  }
   114  
   115  // WithTTY returns a new OutputOption to assume whether or not the output is a TTY.
   116  // This is useful when mocking console output.
   117  func WithTTY(v bool) OutputOption {
   118  	return func(o *Output) {
   119  		o.assumeTTY = v
   120  	}
   121  }
   122  
   123  // WithUnsafe returns a new OutputOption with unsafe mode enabled. Unsafe mode doesn't
   124  // check whether or not the terminal is a TTY.
   125  //
   126  // This option supersedes WithTTY.
   127  //
   128  // This is useful when mocking console output and enforcing ANSI escape output
   129  // e.g. on SSH sessions.
   130  func WithUnsafe() OutputOption {
   131  	return func(o *Output) {
   132  		o.unsafe = true
   133  	}
   134  }
   135  
   136  // ForegroundColor returns the terminal's default foreground color.
   137  func (o *Output) ForegroundColor() Color {
   138  	f := func() {
   139  		if !o.isTTY() {
   140  			return
   141  		}
   142  
   143  		o.fgColor = o.foregroundColor()
   144  	}
   145  
   146  	if o.cache {
   147  		o.fgSync.Do(f)
   148  	} else {
   149  		f()
   150  	}
   151  
   152  	return o.fgColor
   153  }
   154  
   155  // BackgroundColor returns the terminal's default background color.
   156  func (o *Output) BackgroundColor() Color {
   157  	f := func() {
   158  		if !o.isTTY() {
   159  			return
   160  		}
   161  
   162  		o.bgColor = o.backgroundColor()
   163  	}
   164  
   165  	if o.cache {
   166  		o.bgSync.Do(f)
   167  	} else {
   168  		f()
   169  	}
   170  
   171  	return o.bgColor
   172  }
   173  
   174  // HasDarkBackground returns whether terminal uses a dark-ish background.
   175  func (o *Output) HasDarkBackground() bool {
   176  	c := ConvertToRGB(o.BackgroundColor())
   177  	_, _, l := c.Hsl()
   178  	return l < 0.5
   179  }
   180  
   181  // TTY returns the terminal's file descriptor. This may be nil if the output is
   182  // not a terminal.
   183  func (o Output) TTY() File {
   184  	if f, ok := o.tty.(File); ok {
   185  		return f
   186  	}
   187  	return nil
   188  }
   189  
   190  func (o Output) Write(p []byte) (int, error) {
   191  	return o.tty.Write(p)
   192  }
   193  
   194  // WriteString writes the given string to the output.
   195  func (o Output) WriteString(s string) (int, error) {
   196  	return o.Write([]byte(s))
   197  }
   198  

View as plain text