src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/ui/styling.go (about) 1 package ui 2 3 import ( 4 "strings" 5 ) 6 7 // Styling specifies how to change a Style. It can also be applied to a Segment 8 // or Text. 9 type Styling interface{ transform(*Style) } 10 11 // StyleText returns a new Text with the given Styling's applied. It does not 12 // modify the given Text. 13 func StyleText(t Text, ts ...Styling) Text { 14 newt := make(Text, len(t)) 15 for i, seg := range t { 16 newt[i] = StyleSegment(seg, ts...) 17 } 18 return newt 19 } 20 21 // StyleSegment returns a new Segment with the given Styling's applied. It does 22 // not modify the given Segment. 23 func StyleSegment(seg *Segment, ts ...Styling) *Segment { 24 return &Segment{Text: seg.Text, Style: ApplyStyling(seg.Style, ts...)} 25 } 26 27 // ApplyStyling returns a new Style with the given Styling's applied. 28 func ApplyStyling(s Style, ts ...Styling) Style { 29 for _, t := range ts { 30 if t != nil { 31 t.transform(&s) 32 } 33 } 34 return s 35 } 36 37 // Stylings joins several transformers into one. 38 func Stylings(ts ...Styling) Styling { return jointStyling(ts) } 39 40 // Common stylings. 41 var ( 42 Reset Styling = reset{} 43 44 FgDefault Styling = setForeground{nil} 45 46 FgBlack Styling = setForeground{Black} 47 FgRed Styling = setForeground{Red} 48 FgGreen Styling = setForeground{Green} 49 FgYellow Styling = setForeground{Yellow} 50 FgBlue Styling = setForeground{Blue} 51 FgMagenta Styling = setForeground{Magenta} 52 FgCyan Styling = setForeground{Cyan} 53 FgWhite Styling = setForeground{White} 54 55 FgBrightBlack Styling = setForeground{BrightBlack} 56 FgBrightRed Styling = setForeground{BrightRed} 57 FgBrightGreen Styling = setForeground{BrightGreen} 58 FgBrightYellow Styling = setForeground{BrightYellow} 59 FgBrightBlue Styling = setForeground{BrightBlue} 60 FgBrightMagenta Styling = setForeground{BrightMagenta} 61 FgBrightCyan Styling = setForeground{BrightCyan} 62 FgBrightWhite Styling = setForeground{BrightWhite} 63 64 BgDefault Styling = setBackground{nil} 65 66 BgBlack Styling = setBackground{Black} 67 BgRed Styling = setBackground{Red} 68 BgGreen Styling = setBackground{Green} 69 BgYellow Styling = setBackground{Yellow} 70 BgBlue Styling = setBackground{Blue} 71 BgMagenta Styling = setBackground{Magenta} 72 BgCyan Styling = setBackground{Cyan} 73 BgWhite Styling = setBackground{White} 74 75 BgBrightBlack Styling = setBackground{BrightBlack} 76 BgBrightRed Styling = setBackground{BrightRed} 77 BgBrightGreen Styling = setBackground{BrightGreen} 78 BgBrightYellow Styling = setBackground{BrightYellow} 79 BgBrightBlue Styling = setBackground{BrightBlue} 80 BgBrightMagenta Styling = setBackground{BrightMagenta} 81 BgBrightCyan Styling = setBackground{BrightCyan} 82 BgBrightWhite Styling = setBackground{BrightWhite} 83 84 Bold Styling = boolOn{boldField{}} 85 Dim Styling = boolOn{dimField{}} 86 Italic Styling = boolOn{italicField{}} 87 Underlined Styling = boolOn{underlinedField{}} 88 Blink Styling = boolOn{blinkField{}} 89 Inverse Styling = boolOn{inverseField{}} 90 91 NoBold Styling = boolOff{boldField{}} 92 NoDim Styling = boolOff{dimField{}} 93 NoItalic Styling = boolOff{italicField{}} 94 NoUnderlined Styling = boolOff{underlinedField{}} 95 NoBlink Styling = boolOff{blinkField{}} 96 NoInverse Styling = boolOff{inverseField{}} 97 98 ToggleBold Styling = boolToggle{boldField{}} 99 ToggleDim Styling = boolToggle{dimField{}} 100 ToggleItalic Styling = boolToggle{italicField{}} 101 ToggleUnderlined Styling = boolToggle{underlinedField{}} 102 ToggleBlink Styling = boolToggle{blinkField{}} 103 ToggleInverse Styling = boolToggle{inverseField{}} 104 ) 105 106 // Fg returns a Styling that sets the foreground color. 107 func Fg(c Color) Styling { return setForeground{c} } 108 109 // Bg returns a Styling that sets the background color. 110 func Bg(c Color) Styling { return setBackground{c} } 111 112 type reset struct{} 113 type setForeground struct{ c Color } 114 type setBackground struct{ c Color } 115 type boolOn struct{ f boolField } 116 type boolOff struct{ f boolField } 117 type boolToggle struct{ f boolField } 118 119 func (reset) transform(s *Style) { *s = Style{} } 120 func (t setForeground) transform(s *Style) { s.Fg = t.c } 121 func (t setBackground) transform(s *Style) { s.Bg = t.c } 122 func (t boolOn) transform(s *Style) { *t.f.get(s) = true } 123 func (t boolOff) transform(s *Style) { *t.f.get(s) = false } 124 func (t boolToggle) transform(s *Style) { p := t.f.get(s); *p = !*p } 125 126 type boolField interface{ get(*Style) *bool } 127 128 type boldField struct{} 129 type dimField struct{} 130 type italicField struct{} 131 type underlinedField struct{} 132 type blinkField struct{} 133 type inverseField struct{} 134 135 func (boldField) get(s *Style) *bool { return &s.Bold } 136 func (dimField) get(s *Style) *bool { return &s.Dim } 137 func (italicField) get(s *Style) *bool { return &s.Italic } 138 func (underlinedField) get(s *Style) *bool { return &s.Underlined } 139 func (blinkField) get(s *Style) *bool { return &s.Blink } 140 func (inverseField) get(s *Style) *bool { return &s.Inverse } 141 142 type jointStyling []Styling 143 144 func (t jointStyling) transform(s *Style) { 145 for _, t := range t { 146 t.transform(s) 147 } 148 } 149 150 // ParseStyling parses a text representation of Styling, which are kebab 151 // case counterparts to the names of the builtin Styling's. For example, 152 // ToggleInverse is expressed as "toggle-inverse". 153 // 154 // Multiple stylings can be joined by spaces, which is equivalent to calling 155 // Stylings. 156 // 157 // If the given string is invalid, ParseStyling returns nil. 158 func ParseStyling(s string) Styling { 159 if !strings.ContainsRune(s, ' ') { 160 return parseOneStyling(s) 161 } 162 var joint jointStyling 163 for _, subs := range strings.Split(s, " ") { 164 parsed := parseOneStyling(subs) 165 if parsed == nil { 166 return nil 167 } 168 joint = append(joint, parseOneStyling(subs)) 169 } 170 return joint 171 } 172 173 var boolFields = map[string]boolField{ 174 "bold": boldField{}, 175 "dim": dimField{}, 176 "italic": italicField{}, 177 "underlined": underlinedField{}, 178 "blink": blinkField{}, 179 "inverse": inverseField{}, 180 } 181 182 func parseOneStyling(name string) Styling { 183 switch { 184 case name == "default" || name == "fg-default": 185 return FgDefault 186 case strings.HasPrefix(name, "fg-"): 187 if color := parseColor(name[len("fg-"):]); color != nil { 188 return setForeground{color} 189 } 190 case name == "bg-default": 191 return BgDefault 192 case strings.HasPrefix(name, "bg-"): 193 if color := parseColor(name[len("bg-"):]); color != nil { 194 return setBackground{color} 195 } 196 case strings.HasPrefix(name, "no-"): 197 if f, ok := boolFields[name[len("no-"):]]; ok { 198 return boolOff{f} 199 } 200 case strings.HasPrefix(name, "toggle-"): 201 if f, ok := boolFields[name[len("toggle-"):]]; ok { 202 return boolToggle{f} 203 } 204 default: 205 if f, ok := boolFields[name]; ok { 206 return boolOn{f} 207 } 208 if color := parseColor(name); color != nil { 209 return setForeground{color} 210 } 211 } 212 return nil 213 }