muun-recovery/vendor/github.com/logrusorgru/aurora/color.go

399 lines
9.6 KiB
Go

//
// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved.
// This program is free software. It comes without any warranty,
// to the extent permitted by applicable law. You can redistribute
// it and/or modify it under the terms of the Unlicense. See LICENSE
// file for more details or see below.
//
//
// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
//
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// For more information, please refer to <http://unlicense.org/>
//
package aurora
// A Color type is a color. It can contain
// one background color, one foreground color
// and a format, including ideogram related
// formats.
type Color uint
/*
Developer note.
The int type is architecture depended and can be
represented as int32 or int64.
Thus, we can use 32-bits only to be fast and
cross-platform.
All supported formats requires 14 bits. It is
first 14 bits.
A foreground color requires 8 bit + 1 bit (presence flag).
And the same for background color.
The Color representations
[ bg 8 bit ] [fg 8 bit ] [ fg/bg 2 bits ] [ fm 14 bits ]
https://play.golang.org/p/fq2zcNstFoF
*/
// Special formats
const (
BoldFm Color = 1 << iota // 1
FaintFm // 2
ItalicFm // 3
UnderlineFm // 4
SlowBlinkFm // 5
RapidBlinkFm // 6
ReverseFm // 7
ConcealFm // 8
CrossedOutFm // 9
FrakturFm // 20
DoublyUnderlineFm // 21 or bold off for some systems
FramedFm // 51
EncircledFm // 52
OverlinedFm // 53
InverseFm = ReverseFm // alias to ReverseFm
BlinkFm = SlowBlinkFm // alias to SlowBlinkFm
HiddenFm = ConcealFm // alias to ConcealFm
StrikeThroughFm = CrossedOutFm // alias to CrossedOutFm
maskFm = BoldFm | FaintFm |
ItalicFm | UnderlineFm |
SlowBlinkFm | RapidBlinkFm |
ReverseFm |
ConcealFm | CrossedOutFm |
FrakturFm | DoublyUnderlineFm |
FramedFm | EncircledFm | OverlinedFm
flagFg Color = 1 << 14 // presence flag (14th bit)
flagBg Color = 1 << 15 // presence flag (15th bit)
shiftFg = 16 // shift for foreground (starting from 16th bit)
shiftBg = 24 // shift for background (starting from 24th bit)
)
// Foreground colors and related formats
const (
// 8 bits
// [ 0; 7] - 30-37
// [ 8; 15] - 90-97
// [ 16; 231] - RGB
// [232; 255] - grayscale
BlackFg Color = (iota << shiftFg) | flagFg // 30, 90
RedFg // 31, 91
GreenFg // 32, 92
YellowFg // 33, 93
BlueFg // 34, 94
MagentaFg // 35, 95
CyanFg // 36, 96
WhiteFg // 37, 97
BrightFg Color = ((1 << 3) << shiftFg) | flagFg // -> 90
// the BrightFg itself doesn't represent
// a color, thus it has not flagFg
// 5 bits
// BrownFg represents brown foreground color.
//
// Deprecated: use YellowFg instead, following specifications
BrownFg = YellowFg
//
maskFg = (0xff << shiftFg) | flagFg
)
// Background colors and related formats
const (
// 8 bits
// [ 0; 7] - 40-47
// [ 8; 15] - 100-107
// [ 16; 231] - RGB
// [232; 255] - grayscale
BlackBg Color = (iota << shiftBg) | flagBg // 40, 100
RedBg // 41, 101
GreenBg // 42, 102
YellowBg // 43, 103
BlueBg // 44, 104
MagentaBg // 45, 105
CyanBg // 46, 106
WhiteBg // 47, 107
BrightBg Color = ((1 << 3) << shiftBg) | flagBg // -> 100
// the BrightBg itself doesn't represent
// a color, thus it has not flagBg
// 5 bits
// BrownBg represents brown foreground color.
//
// Deprecated: use YellowBg instead, following specifications
BrownBg = YellowBg
//
maskBg = (0xff << shiftBg) | flagBg
)
const (
availFlags = "-+# 0"
esc = "\033["
clear = esc + "0m"
)
// IsValid returns true always
//
// Deprecated: don't use this method anymore
func (c Color) IsValid() bool {
return true
}
// Nos returns string like 1;7;31;45. It
// may be an empty string for empty color.
// If the zero is true, then the string
// is prepended with 0;
func (c Color) Nos(zero bool) string {
return string(c.appendNos(make([]byte, 0, 59), zero))
}
func appendCond(bs []byte, cond, semi bool, vals ...byte) []byte {
if !cond {
return bs
}
return appendSemi(bs, semi, vals...)
}
// if the semi is true, then prepend with semicolon
func appendSemi(bs []byte, semi bool, vals ...byte) []byte {
if semi {
bs = append(bs, ';')
}
return append(bs, vals...)
}
func itoa(t byte) string {
var (
a [3]byte
j = 2
)
for i := 0; i < 3; i, j = i+1, j-1 {
a[j] = '0' + t%10
if t = t / 10; t == 0 {
break
}
}
return string(a[j:])
}
func (c Color) appendFg(bs []byte, zero bool) []byte {
if zero || c&maskFm != 0 {
bs = append(bs, ';')
}
// 0- 7 : 30-37
// 8-15 : 90-97
// > 15 : 38;5;val
switch fg := (c & maskFg) >> shiftFg; {
case fg <= 7:
// '3' and the value itself
bs = append(bs, '3', '0'+byte(fg))
case fg <= 15:
// '9' and the value itself
bs = append(bs, '9', '0'+byte(fg&^0x08)) // clear bright flag
default:
bs = append(bs, '3', '8', ';', '5', ';')
bs = append(bs, itoa(byte(fg))...)
}
return bs
}
func (c Color) appendBg(bs []byte, zero bool) []byte {
if zero || c&(maskFm|maskFg) != 0 {
bs = append(bs, ';')
}
// 0- 7 : 40- 47
// 8-15 : 100-107
// > 15 : 48;5;val
switch fg := (c & maskBg) >> shiftBg; {
case fg <= 7:
// '3' and the value itself
bs = append(bs, '4', '0'+byte(fg))
case fg <= 15:
// '1', '0' and the value itself
bs = append(bs, '1', '0', '0'+byte(fg&^0x08)) // clear bright flag
default:
bs = append(bs, '4', '8', ';', '5', ';')
bs = append(bs, itoa(byte(fg))...)
}
return bs
}
func (c Color) appendFm9(bs []byte, zero bool) []byte {
bs = appendCond(bs, c&ItalicFm != 0,
zero || c&(BoldFm|FaintFm) != 0,
'3')
bs = appendCond(bs, c&UnderlineFm != 0,
zero || c&(BoldFm|FaintFm|ItalicFm) != 0,
'4')
// don't combine slow and rapid blink using only
// on of them, preferring slow blink
if c&SlowBlinkFm != 0 {
bs = appendSemi(bs,
zero || c&(BoldFm|FaintFm|ItalicFm|UnderlineFm) != 0,
'5')
} else if c&RapidBlinkFm != 0 {
bs = appendSemi(bs,
zero || c&(BoldFm|FaintFm|ItalicFm|UnderlineFm) != 0,
'6')
}
// including 1-2
const mask6i = BoldFm | FaintFm |
ItalicFm | UnderlineFm |
SlowBlinkFm | RapidBlinkFm
bs = appendCond(bs, c&ReverseFm != 0,
zero || c&(mask6i) != 0,
'7')
bs = appendCond(bs, c&ConcealFm != 0,
zero || c&(mask6i|ReverseFm) != 0,
'8')
bs = appendCond(bs, c&CrossedOutFm != 0,
zero || c&(mask6i|ReverseFm|ConcealFm) != 0,
'9')
return bs
}
// append 1;3;38;5;216 like string that represents ANSI
// color of the Color; the zero argument requires
// appending of '0' before to reset previous format
// and colors
func (c Color) appendNos(bs []byte, zero bool) []byte {
if zero {
bs = append(bs, '0') // reset previous
}
// formats
//
if c&maskFm != 0 {
// 1-2
// don't combine bold and faint using only on of them, preferring bold
if c&BoldFm != 0 {
bs = appendSemi(bs, zero, '1')
} else if c&FaintFm != 0 {
bs = appendSemi(bs, zero, '2')
}
// 3-9
const mask9 = ItalicFm | UnderlineFm |
SlowBlinkFm | RapidBlinkFm |
ReverseFm | ConcealFm | CrossedOutFm
if c&mask9 != 0 {
bs = c.appendFm9(bs, zero)
}
// 20-21
const (
mask21 = FrakturFm | DoublyUnderlineFm
mask9i = BoldFm | FaintFm | mask9
)
if c&mask21 != 0 {
bs = appendCond(bs, c&FrakturFm != 0,
zero || c&mask9i != 0,
'2', '0')
bs = appendCond(bs, c&DoublyUnderlineFm != 0,
zero || c&(mask9i|FrakturFm) != 0,
'2', '1')
}
// 50-53
const (
mask53 = FramedFm | EncircledFm | OverlinedFm
mask21i = mask9i | mask21
)
if c&mask53 != 0 {
bs = appendCond(bs, c&FramedFm != 0,
zero || c&mask21i != 0,
'5', '1')
bs = appendCond(bs, c&EncircledFm != 0,
zero || c&(mask21i|FramedFm) != 0,
'5', '2')
bs = appendCond(bs, c&OverlinedFm != 0,
zero || c&(mask21i|FramedFm|EncircledFm) != 0,
'5', '3')
}
}
// foreground
if c&maskFg != 0 {
bs = c.appendFg(bs, zero)
}
// background
if c&maskBg != 0 {
bs = c.appendBg(bs, zero)
}
return bs
}