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  // ////////////////////////////////////////////////////////////////////////////////// //