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