gopkg.in/essentialkaos/ek.v3@v3.5.1/fmtc/fmtc.go (about) 1 // Package fmtc provides methods similar to fmt for colored output 2 package fmtc 3 4 // ////////////////////////////////////////////////////////////////////////////////// // 5 // // 6 // Copyright (c) 2009-2016 Essential Kaos // 7 // Essential Kaos Open Source License <http://essentialkaos.com/ekol?en> // 8 // // 9 // ////////////////////////////////////////////////////////////////////////////////// // 10 11 import ( 12 "bytes" 13 "errors" 14 "fmt" 15 "io" 16 ) 17 18 // ////////////////////////////////////////////////////////////////////////////////// // 19 20 const ( 21 _CODE_RESET = "\033[0m" 22 _CODE_BACKSPACE = "\b" 23 _CODE_BELL = "\a" 24 ) 25 26 // ////////////////////////////////////////////////////////////////////////////////// // 27 28 // T is struct can be used for printing temporary messages 29 type T struct { 30 size int 31 } 32 33 // ////////////////////////////////////////////////////////////////////////////////// // 34 35 var codes = map[int]int{ 36 // Special 37 '-': -1, // Light colors 38 '!': 0, // Default 39 '*': 1, // Bold 40 '^': 2, // Dim 41 '_': 4, // Underline 42 '~': 5, // Blink 43 '@': 7, // Reverse 44 45 // Text 46 'd': 30, // Black (Dark) 47 'r': 31, // Red 48 'g': 32, // Green 49 'y': 33, // Yellow 50 'b': 34, // Blue 51 'm': 35, // Magenta 52 'c': 36, // Cyan 53 's': 37, // Grey (Smokey) 54 'w': 97, // White 55 56 // Background 57 'D': 40, // Black (Dark) 58 'R': 41, // Red 59 'G': 42, // Green 60 'Y': 43, // Yellow 61 'B': 44, // Blue 62 'M': 45, // Magenta 63 'C': 46, // Cyan 64 'S': 47, // Grey (Smokey) 65 'W': 107, // White 66 } 67 68 // ////////////////////////////////////////////////////////////////////////////////// // 69 70 // DisableColors disable all colors and modificators in output 71 var DisableColors = false 72 73 // ////////////////////////////////////////////////////////////////////////////////// // 74 75 // Println formats using the default formats for its operands and writes to standard 76 // output. Spaces are always added between operands and a newline is appended. It 77 // returns the number of bytes written and any write error encountered. 78 func Println(a ...interface{}) (int, error) { 79 applyColors(&a, DisableColors) 80 return fmt.Println(a...) 81 } 82 83 // Printf formats according to a format specifier and writes to standard output. It 84 // returns the number of bytes written and any write error encountered. 85 func Printf(f string, a ...interface{}) (int, error) { 86 return fmt.Printf(searchColors(f, DisableColors), a...) 87 } 88 89 // Fprint formats using the default formats for its operands and writes to w. 90 // Spaces are added between operands when neither is a string. It returns the 91 // number of bytes written and any write error encountered. 92 func Fprint(w io.Writer, a ...interface{}) (int, error) { 93 applyColors(&a, DisableColors) 94 return fmt.Fprint(w, a...) 95 } 96 97 // Fprintln formats using the default formats for its operands and writes to w. 98 // Spaces are always added between operands and a newline is appended. It returns 99 // the number of bytes written and any write error encountered. 100 func Fprintln(w io.Writer, a ...interface{}) (int, error) { 101 applyColors(&a, DisableColors) 102 return fmt.Fprintln(w, a...) 103 } 104 105 // Fprintf formats according to a format specifier and writes to w. It returns 106 // the number of bytes written and any write error encountered. 107 func Fprintf(w io.Writer, f string, a ...interface{}) (int, error) { 108 return fmt.Fprintf(w, searchColors(f, DisableColors), a...) 109 } 110 111 // Sprint formats using the default formats for its operands and returns the 112 // resulting string. Spaces are added between operands when neither is a string. 113 func Sprint(a ...interface{}) string { 114 applyColors(&a, DisableColors) 115 return fmt.Sprint(a...) 116 } 117 118 // Sprintf formats according to a format specifier and returns the resulting 119 // string. 120 func Sprintf(f string, a ...interface{}) string { 121 return fmt.Sprintf(searchColors(f, DisableColors), a...) 122 } 123 124 // Errorf formats according to a format specifier and returns the string as a 125 // value that satisfies error. 126 func Errorf(f string, a ...interface{}) error { 127 return errors.New(Sprintf(f, a...)) 128 } 129 130 // NewLine prints a newline to standart output 131 func NewLine() (int, error) { 132 return fmt.Println("") 133 } 134 135 // Clean return string without color tags 136 func Clean(s string) string { 137 return searchColors(s, true) 138 } 139 140 // Bell print alert symbol 141 func Bell() { 142 fmt.Printf(_CODE_BELL) 143 } 144 145 // ////////////////////////////////////////////////////////////////////////////////// // 146 147 // Printf remove previous message (if printed) and print new message 148 func (t *T) Printf(f string, a ...interface{}) (int, error) { 149 if t.size != 0 { 150 fmt.Printf(getSymbols(_CODE_BACKSPACE, t.size) + "\033[0K") 151 } 152 153 t.size = len(fmt.Sprintf(searchColors(f, true), a...)) 154 155 return fmt.Printf(searchColors(f, DisableColors), a...) 156 } 157 158 // Println remove previous message (if printed) and print new message 159 func (t *T) Println(a ...interface{}) (int, error) { 160 if t.size != 0 { 161 fmt.Printf(getSymbols(_CODE_BACKSPACE, t.size) + "\033[0K") 162 } 163 164 t.size = 0 165 166 return Println(a...) 167 } 168 169 // ////////////////////////////////////////////////////////////////////////////////// // 170 171 func tag2ANSI(tag string, clean bool) string { 172 if clean { 173 return "" 174 } 175 176 var ( 177 modificator = 0 178 charColor = 39 179 bgColor = 49 180 light = false 181 ) 182 183 for _, key := range tag { 184 code, ok := codes[int(key)] 185 186 if !ok { 187 return fmt.Sprint(tag) 188 } 189 190 switch key { 191 case '-': 192 light = true 193 case '!', '*', '^', '_', '~', '@': 194 modificator = code 195 case 'd', 'r', 'g', 'y', 'b', 'm', 'c', 's', 'w': 196 charColor = code 197 case 'D', 'R', 'G', 'Y', 'B', 'M', 'C', 'S', 'W': 198 bgColor = code 199 } 200 } 201 202 if light { 203 switch charColor { 204 case 97: 205 break 206 case 37: 207 charColor = 90 208 default: 209 charColor += 60 210 } 211 } 212 213 return fmt.Sprintf("\033[%d;%d;%dm", modificator, charColor, bgColor) 214 } 215 216 func replaceColorTags(input, output *bytes.Buffer, clean bool) bool { 217 tag := bytes.NewBufferString("") 218 219 LOOP: 220 for { 221 i, _, err := input.ReadRune() 222 223 if err != nil { 224 output.WriteString("{") 225 output.WriteString(tag.String()) 226 return true 227 } 228 229 switch i { 230 default: 231 tag.WriteRune(i) 232 case '{': 233 output.WriteString("{") 234 output.WriteString(tag.String()) 235 tag = bytes.NewBufferString("") 236 case '}': 237 break LOOP 238 } 239 } 240 241 tagStr := tag.String() 242 243 if tagStr == "!" { 244 if !clean { 245 output.WriteString(_CODE_RESET) 246 } 247 248 return true 249 } 250 251 colorCode := tag2ANSI(tagStr, clean) 252 253 if colorCode == tagStr { 254 output.WriteString("{") 255 output.WriteString(colorCode) 256 output.WriteString("}") 257 258 return true 259 } 260 261 output.WriteString(colorCode) 262 263 return false 264 } 265 266 func searchColors(text string, clean bool) string { 267 if text == "" { 268 return "" 269 } 270 271 closed := true 272 input := bytes.NewBufferString(text) 273 output := bytes.NewBufferString("") 274 275 for { 276 i, _, err := input.ReadRune() 277 278 if err != nil { 279 break 280 } 281 282 switch i { 283 case '{': 284 closed = replaceColorTags(input, output, clean) 285 case rune(65533): 286 continue 287 default: 288 output.WriteRune(i) 289 } 290 } 291 292 if !closed { 293 output.WriteString(_CODE_RESET) 294 } 295 296 return output.String() 297 } 298 299 func applyColors(a *[]interface{}, clean bool) { 300 for i, x := range *a { 301 if s, ok := x.(string); ok { 302 (*a)[i] = searchColors(s, clean) 303 } 304 } 305 } 306 307 func getSymbols(symbol string, count int) string { 308 result := "" 309 310 for i := 0; i < count; i++ { 311 result += symbol 312 } 313 314 return result 315 } 316 317 // ////////////////////////////////////////////////////////////////////////////////// //