1/*
2Package color is Command line color library.
3Support rich color rendering output, universal API method, compatible with Windows system
4
5Source code and other details for the project are available at GitHub:
6
7	https://github.com/gookit/color
8
9More usage please see README and tests.
10*/
11package color
12
13import (
14	"fmt"
15	"io"
16	"os"
17	"regexp"
18
19	"github.com/xo/terminfo"
20)
21
22// terminal color available level alias of the terminfo.ColorLevel*
23const (
24	LevelNo  = terminfo.ColorLevelNone     // not support color.
25	Level16  = terminfo.ColorLevelBasic    // 3/4 bit color supported
26	Level256 = terminfo.ColorLevelHundreds // 8 bit color supported
27	LevelRgb = terminfo.ColorLevelMillions // (24 bit)true color supported
28)
29
30// color render templates
31// ESC 操作的表示:
32// 	"\033"(Octal 8进制) = "\x1b"(Hexadecimal 16进制) = 27 (10进制)
33const (
34	SettingTpl   = "\x1b[%sm"
35	FullColorTpl = "\x1b[%sm%s\x1b[0m"
36)
37
38// ResetSet Close all properties.
39const ResetSet = "\x1b[0m"
40
41// CodeExpr regex to clear color codes eg "\033[1;36mText\x1b[0m"
42const CodeExpr = `\033\[[\d;?]+m`
43
44var (
45	// Enable switch color render and display
46	//
47	// NOTICE:
48	// if ENV: NO_COLOR is not empty, will disable color render.
49	Enable = os.Getenv("NO_COLOR") == ""
50	// RenderTag render HTML tag on call color.Xprint, color.PrintX
51	RenderTag = true
52	// debug mode for development.
53	//
54	// set env:
55	// 	COLOR_DEBUG_MODE=on
56	// or:
57	// 	COLOR_DEBUG_MODE=on go run ./_examples/envcheck.go
58	debugMode = os.Getenv("COLOR_DEBUG_MODE") == "on"
59	// inner errors record on detect color level
60	innerErrs []error
61	// output the default io.Writer message print
62	output io.Writer = os.Stdout
63	// mark current env, It's like in `cmd.exe`
64	// if not in windows, it's always is False.
65	isLikeInCmd bool
66	// the color support level for current terminal
67	// needVTP - need enable VTP, only for windows OS
68	colorLevel, needVTP = detectTermColorLevel()
69	// match color codes
70	codeRegex = regexp.MustCompile(CodeExpr)
71	// mark current env is support color.
72	// Always: isLikeInCmd != supportColor
73	// supportColor = IsSupportColor()
74)
75
76// TermColorLevel value on current ENV
77func TermColorLevel() terminfo.ColorLevel {
78	return colorLevel
79}
80
81// SupportColor on the current ENV
82func SupportColor() bool {
83	return colorLevel > terminfo.ColorLevelNone
84}
85
86// Support16Color on the current ENV
87// func Support16Color() bool {
88// 	return colorLevel > terminfo.ColorLevelNone
89// }
90
91// Support256Color on the current ENV
92func Support256Color() bool {
93	return colorLevel > terminfo.ColorLevelBasic
94}
95
96// SupportTrueColor on the current ENV
97func SupportTrueColor() bool {
98	return colorLevel > terminfo.ColorLevelHundreds
99}
100
101/*************************************************************
102 * global settings
103 *************************************************************/
104
105// Set set console color attributes
106func Set(colors ...Color) (int, error) {
107	code := Colors2code(colors...)
108	err := SetTerminal(code)
109	return 0, err
110}
111
112// Reset reset console color attributes
113func Reset() (int, error) {
114	err := ResetTerminal()
115	return 0, err
116}
117
118// Disable disable color output
119func Disable() bool {
120	oldVal := Enable
121	Enable = false
122	return oldVal
123}
124
125// NotRenderTag on call color.Xprint, color.PrintX
126func NotRenderTag() {
127	RenderTag = false
128}
129
130// SetOutput set default colored text output
131func SetOutput(w io.Writer) {
132	output = w
133}
134
135// ResetOutput reset output
136func ResetOutput() {
137	output = os.Stdout
138}
139
140// ResetOptions reset all package option setting
141func ResetOptions() {
142	RenderTag = true
143	Enable = true
144	output = os.Stdout
145}
146
147// ForceColor force open color render
148func ForceSetColorLevel(level terminfo.ColorLevel) terminfo.ColorLevel {
149	oldLevelVal := colorLevel
150	colorLevel = level
151	return oldLevelVal
152}
153
154// ForceColor force open color render
155func ForceColor() terminfo.ColorLevel {
156	return ForceOpenColor()
157}
158
159// ForceOpenColor force open color render
160func ForceOpenColor() terminfo.ColorLevel {
161	// TODO should set level to ?
162	return ForceSetColorLevel(terminfo.ColorLevelMillions)
163}
164
165// IsLikeInCmd check result
166// Deprecated
167func IsLikeInCmd() bool {
168	return isLikeInCmd
169}
170
171// InnerErrs info
172func InnerErrs() []error {
173	return innerErrs
174}
175
176/*************************************************************
177 * render color code
178 *************************************************************/
179
180// RenderCode render message by color code.
181// Usage:
182// 	msg := RenderCode("3;32;45", "some", "message")
183func RenderCode(code string, args ...interface{}) string {
184	var message string
185	if ln := len(args); ln == 0 {
186		return ""
187	}
188
189	message = fmt.Sprint(args...)
190	if len(code) == 0 {
191		return message
192	}
193
194	// disabled OR not support color
195	if !Enable || !SupportColor() {
196		return ClearCode(message)
197	}
198
199	return fmt.Sprintf(FullColorTpl, code, message)
200}
201
202// RenderWithSpaces Render code with spaces.
203// If the number of args is > 1, a space will be added between the args
204func RenderWithSpaces(code string, args ...interface{}) string {
205	message := formatArgsForPrintln(args)
206	if len(code) == 0 {
207		return message
208	}
209
210	// disabled OR not support color
211	if !Enable || !SupportColor() {
212		return ClearCode(message)
213	}
214
215	return fmt.Sprintf(FullColorTpl, code, message)
216}
217
218// RenderString render a string with color code.
219// Usage:
220// 	msg := RenderString("3;32;45", "a message")
221func RenderString(code string, str string) string {
222	if len(code) == 0 || str == "" {
223		return str
224	}
225
226	// disabled OR not support color
227	if !Enable || !SupportColor() {
228		return ClearCode(str)
229	}
230
231	return fmt.Sprintf(FullColorTpl, code, str)
232}
233
234// ClearCode clear color codes.
235// eg: "\033[36;1mText\x1b[0m" -> "Text"
236func ClearCode(str string) string {
237	return codeRegex.ReplaceAllString(str, "")
238}
239