]> git.lizzy.rs Git - micro.git/blob - internal/config/colorscheme.go
4a2ef2f43127daa37ba4c71d833c5073e945c6a1
[micro.git] / internal / config / colorscheme.go
1 package config
2
3 import (
4         "errors"
5         "regexp"
6         "strconv"
7         "strings"
8
9         "github.com/zyedidia/tcell/v2"
10 )
11
12 // DefStyle is Micro's default style
13 var DefStyle tcell.Style = tcell.StyleDefault
14
15 // Colorscheme is the current colorscheme
16 var Colorscheme map[string]tcell.Style
17
18 // GetColor takes in a syntax group and returns the colorscheme's style for that group
19 func GetColor(color string) tcell.Style {
20         st := DefStyle
21         if color == "" {
22                 return st
23         }
24         groups := strings.Split(color, ".")
25         if len(groups) > 1 {
26                 curGroup := ""
27                 for i, g := range groups {
28                         if i != 0 {
29                                 curGroup += "."
30                         }
31                         curGroup += g
32                         if style, ok := Colorscheme[curGroup]; ok {
33                                 st = style
34                         }
35                 }
36         } else if style, ok := Colorscheme[color]; ok {
37                 st = style
38         }
39
40         return st
41 }
42
43 // ColorschemeExists checks if a given colorscheme exists
44 func ColorschemeExists(colorschemeName string) bool {
45         return FindRuntimeFile(RTColorscheme, colorschemeName) != nil
46 }
47
48 // InitColorscheme picks and initializes the colorscheme when micro starts
49 func InitColorscheme() error {
50         Colorscheme = make(map[string]tcell.Style)
51         DefStyle = tcell.StyleDefault
52
53         return LoadDefaultColorscheme()
54 }
55
56 // LoadDefaultColorscheme loads the default colorscheme from $(ConfigDir)/colorschemes
57 func LoadDefaultColorscheme() error {
58         return LoadColorscheme(GlobalSettings["colorscheme"].(string))
59 }
60
61 // LoadColorscheme loads the given colorscheme from a directory
62 func LoadColorscheme(colorschemeName string) error {
63         file := FindRuntimeFile(RTColorscheme, colorschemeName)
64         if file == nil {
65                 return errors.New(colorschemeName + " is not a valid colorscheme")
66         }
67         if data, err := file.Data(); err != nil {
68                 return errors.New("Error loading colorscheme: " + err.Error())
69         } else {
70                 Colorscheme, err = ParseColorscheme(string(data))
71                 if err != nil {
72                         return err
73                 }
74         }
75         return nil
76 }
77
78 // ParseColorscheme parses the text definition for a colorscheme and returns the corresponding object
79 // Colorschemes are made up of color-link statements linking a color group to a list of colors
80 // For example, color-link keyword (blue,red) makes all keywords have a blue foreground and
81 // red background
82 func ParseColorscheme(text string) (map[string]tcell.Style, error) {
83         var err error
84         parser := regexp.MustCompile(`color-link\s+(\S*)\s+"(.*)"`)
85
86         lines := strings.Split(text, "\n")
87
88         c := make(map[string]tcell.Style)
89
90         for _, line := range lines {
91                 if strings.TrimSpace(line) == "" ||
92                         strings.TrimSpace(line)[0] == '#' {
93                         // Ignore this line
94                         continue
95                 }
96
97                 matches := parser.FindSubmatch([]byte(line))
98                 if len(matches) == 3 {
99                         link := string(matches[1])
100                         colors := string(matches[2])
101
102                         style := StringToStyle(colors)
103                         c[link] = style
104
105                         if link == "default" {
106                                 DefStyle = style
107                         }
108                 } else {
109                         err = errors.New("Color-link statement is not valid: " + line)
110                 }
111         }
112
113         return c, err
114 }
115
116 // StringToStyle returns a style from a string
117 // The strings must be in the format "extra foregroundcolor,backgroundcolor"
118 // The 'extra' can be bold, reverse, italic or underline
119 func StringToStyle(str string) tcell.Style {
120         var fg, bg string
121         spaceSplit := strings.Split(str, " ")
122         split := strings.Split(spaceSplit[len(spaceSplit)-1], ",")
123         if len(split) > 1 {
124                 fg, bg = split[0], split[1]
125         } else {
126                 fg = split[0]
127         }
128         fg = strings.TrimSpace(fg)
129         bg = strings.TrimSpace(bg)
130
131         var fgColor, bgColor tcell.Color
132         if fg == "" || fg == "default" {
133                 fgColor, _, _ = DefStyle.Decompose()
134         } else {
135                 fgColor = StringToColor(fg)
136         }
137         if bg == "" || bg == "default" {
138                 _, bgColor, _ = DefStyle.Decompose()
139         } else {
140                 bgColor = StringToColor(bg)
141         }
142
143         style := DefStyle.Foreground(fgColor).Background(bgColor)
144         if strings.Contains(str, "bold") {
145                 style = style.Bold(true)
146         }
147         if strings.Contains(str, "italic") {
148                 style = style.Italic(true)
149         }
150         if strings.Contains(str, "reverse") {
151                 style = style.Reverse(true)
152         }
153         if strings.Contains(str, "underline") {
154                 style = style.Underline(true)
155         }
156         return style
157 }
158
159 // StringToColor returns a tcell color from a string representation of a color
160 // We accept either bright... or light... to mean the brighter version of a color
161 func StringToColor(str string) tcell.Color {
162         switch str {
163         case "black":
164                 return tcell.ColorBlack
165         case "red":
166                 return tcell.ColorMaroon
167         case "green":
168                 return tcell.ColorGreen
169         case "yellow":
170                 return tcell.ColorOlive
171         case "blue":
172                 return tcell.ColorNavy
173         case "magenta":
174                 return tcell.ColorPurple
175         case "cyan":
176                 return tcell.ColorTeal
177         case "white":
178                 return tcell.ColorSilver
179         case "brightblack", "lightblack":
180                 return tcell.ColorGray
181         case "brightred", "lightred":
182                 return tcell.ColorRed
183         case "brightgreen", "lightgreen":
184                 return tcell.ColorLime
185         case "brightyellow", "lightyellow":
186                 return tcell.ColorYellow
187         case "brightblue", "lightblue":
188                 return tcell.ColorBlue
189         case "brightmagenta", "lightmagenta":
190                 return tcell.ColorFuchsia
191         case "brightcyan", "lightcyan":
192                 return tcell.ColorAqua
193         case "brightwhite", "lightwhite":
194                 return tcell.ColorWhite
195         case "default":
196                 return tcell.ColorDefault
197         default:
198                 // Check if this is a 256 color
199                 if num, err := strconv.Atoi(str); err == nil {
200                         return GetColor256(num)
201                 }
202                 // Probably a truecolor hex value
203                 return tcell.GetColor(str)
204         }
205 }
206
207 // GetColor256 returns the tcell color for a number between 0 and 255
208 func GetColor256(color int) tcell.Color {
209         if color == 0 {
210                 return tcell.ColorDefault
211         }
212         return tcell.PaletteColor(color)
213 }