...
1 package termenv
2
3 import (
4 "io"
5 "os"
6 "sync"
7 )
8
9 var (
10
11 output = NewOutput(os.Stdout)
12 )
13
14
15 type File interface {
16 io.ReadWriter
17 Fd() uintptr
18 }
19
20
21 type OutputOption = func(*Output)
22
23
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
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
55 func DefaultOutput() *Output {
56 return output
57 }
58
59
60 func SetDefaultOutput(o *Output) {
61 output = o
62 }
63
64
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
90 func WithEnvironment(environ Environ) OutputOption {
91 return func(o *Output) {
92 o.environ = environ
93 }
94 }
95
96
97 func WithProfile(profile Profile) OutputOption {
98 return func(o *Output) {
99 o.Profile = profile
100 }
101 }
102
103
104
105 func WithColorCache(v bool) OutputOption {
106 return func(o *Output) {
107 o.cache = v
108
109
110 _ = o.ForegroundColor()
111 _ = o.BackgroundColor()
112 }
113 }
114
115
116
117 func WithTTY(v bool) OutputOption {
118 return func(o *Output) {
119 o.assumeTTY = v
120 }
121 }
122
123
124
125
126
127
128
129
130 func WithUnsafe() OutputOption {
131 return func(o *Output) {
132 o.unsafe = true
133 }
134 }
135
136
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
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
175 func (o *Output) HasDarkBackground() bool {
176 c := ConvertToRGB(o.BackgroundColor())
177 _, _, l := c.Hsl()
178 return l < 0.5
179 }
180
181
182
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
195 func (o Output) WriteString(s string) (int, error) {
196 return o.Write([]byte(s))
197 }
198
View as plain text