mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-15 21:37:09 +09:00
* Fix default colors for unconfigured syntax groups
When GetColor is called for a syntax group not specified in the
colorscheme, it should fallback not to the terminal's default colors
(tcell.DefaultColor) but to the colorscheme's defaults (DefStyle)
which may be different from tcell.DefaultColor.
For example, if we are using micro's default colorscheme in a terminal
which uses a black-on-white theme, then dots and commas in Go files
("symbol" syntax group in go.yaml) are displayed black on a dark
background, i.e. barely visible.
* Avoid using terminal's default colors directly
If a syntax group color is set to "default" (which we have for some
syntax groups in some colorschemes), it defaults to the terminal's
default colors (tcell.DefaultColor), which is fine for 16-color
colorschemes but not quite fine for truecolor and 256-color
colorschemes which should not depend on the terminal colors.
It should default to the colorscheme's default (DefStyle) instead.
For example, if we are using micro's default colorscheme in a terminal
which uses a black-on-white theme, then "bool" type in C files
("type.extended" syntax group in c.yaml) is displayed black on a dark
background, i.e. barely visible.
214 lines
5.3 KiB
Go
214 lines
5.3 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/zyedidia/tcell/v2"
|
|
)
|
|
|
|
// DefStyle is Micro's default style
|
|
var DefStyle tcell.Style = tcell.StyleDefault
|
|
|
|
// Colorscheme is the current colorscheme
|
|
var Colorscheme map[string]tcell.Style
|
|
|
|
// GetColor takes in a syntax group and returns the colorscheme's style for that group
|
|
func GetColor(color string) tcell.Style {
|
|
st := DefStyle
|
|
if color == "" {
|
|
return st
|
|
}
|
|
groups := strings.Split(color, ".")
|
|
if len(groups) > 1 {
|
|
curGroup := ""
|
|
for i, g := range groups {
|
|
if i != 0 {
|
|
curGroup += "."
|
|
}
|
|
curGroup += g
|
|
if style, ok := Colorscheme[curGroup]; ok {
|
|
st = style
|
|
}
|
|
}
|
|
} else if style, ok := Colorscheme[color]; ok {
|
|
st = style
|
|
}
|
|
|
|
return st
|
|
}
|
|
|
|
// ColorschemeExists checks if a given colorscheme exists
|
|
func ColorschemeExists(colorschemeName string) bool {
|
|
return FindRuntimeFile(RTColorscheme, colorschemeName) != nil
|
|
}
|
|
|
|
// InitColorscheme picks and initializes the colorscheme when micro starts
|
|
func InitColorscheme() error {
|
|
Colorscheme = make(map[string]tcell.Style)
|
|
DefStyle = tcell.StyleDefault
|
|
|
|
return LoadDefaultColorscheme()
|
|
}
|
|
|
|
// LoadDefaultColorscheme loads the default colorscheme from $(ConfigDir)/colorschemes
|
|
func LoadDefaultColorscheme() error {
|
|
return LoadColorscheme(GlobalSettings["colorscheme"].(string))
|
|
}
|
|
|
|
// LoadColorscheme loads the given colorscheme from a directory
|
|
func LoadColorscheme(colorschemeName string) error {
|
|
file := FindRuntimeFile(RTColorscheme, colorschemeName)
|
|
if file == nil {
|
|
return errors.New(colorschemeName + " is not a valid colorscheme")
|
|
}
|
|
if data, err := file.Data(); err != nil {
|
|
return errors.New("Error loading colorscheme: " + err.Error())
|
|
} else {
|
|
Colorscheme, err = ParseColorscheme(string(data))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ParseColorscheme parses the text definition for a colorscheme and returns the corresponding object
|
|
// Colorschemes are made up of color-link statements linking a color group to a list of colors
|
|
// For example, color-link keyword (blue,red) makes all keywords have a blue foreground and
|
|
// red background
|
|
func ParseColorscheme(text string) (map[string]tcell.Style, error) {
|
|
var err error
|
|
parser := regexp.MustCompile(`color-link\s+(\S*)\s+"(.*)"`)
|
|
|
|
lines := strings.Split(text, "\n")
|
|
|
|
c := make(map[string]tcell.Style)
|
|
|
|
for _, line := range lines {
|
|
if strings.TrimSpace(line) == "" ||
|
|
strings.TrimSpace(line)[0] == '#' {
|
|
// Ignore this line
|
|
continue
|
|
}
|
|
|
|
matches := parser.FindSubmatch([]byte(line))
|
|
if len(matches) == 3 {
|
|
link := string(matches[1])
|
|
colors := string(matches[2])
|
|
|
|
style := StringToStyle(colors)
|
|
c[link] = style
|
|
|
|
if link == "default" {
|
|
DefStyle = style
|
|
}
|
|
} else {
|
|
err = errors.New("Color-link statement is not valid: " + line)
|
|
}
|
|
}
|
|
|
|
return c, err
|
|
}
|
|
|
|
// StringToStyle returns a style from a string
|
|
// The strings must be in the format "extra foregroundcolor,backgroundcolor"
|
|
// The 'extra' can be bold, reverse, italic or underline
|
|
func StringToStyle(str string) tcell.Style {
|
|
var fg, bg string
|
|
spaceSplit := strings.Split(str, " ")
|
|
split := strings.Split(spaceSplit[len(spaceSplit)-1], ",")
|
|
if len(split) > 1 {
|
|
fg, bg = split[0], split[1]
|
|
} else {
|
|
fg = split[0]
|
|
}
|
|
fg = strings.TrimSpace(fg)
|
|
bg = strings.TrimSpace(bg)
|
|
|
|
var fgColor, bgColor tcell.Color
|
|
if fg == "" || fg == "default" {
|
|
fgColor, _, _ = DefStyle.Decompose()
|
|
} else {
|
|
fgColor = StringToColor(fg)
|
|
}
|
|
if bg == "" || bg == "default" {
|
|
_, bgColor, _ = DefStyle.Decompose()
|
|
} else {
|
|
bgColor = StringToColor(bg)
|
|
}
|
|
|
|
style := DefStyle.Foreground(fgColor).Background(bgColor)
|
|
if strings.Contains(str, "bold") {
|
|
style = style.Bold(true)
|
|
}
|
|
if strings.Contains(str, "italic") {
|
|
style = style.Italic(true)
|
|
}
|
|
if strings.Contains(str, "reverse") {
|
|
style = style.Reverse(true)
|
|
}
|
|
if strings.Contains(str, "underline") {
|
|
style = style.Underline(true)
|
|
}
|
|
return style
|
|
}
|
|
|
|
// StringToColor returns a tcell color from a string representation of a color
|
|
// We accept either bright... or light... to mean the brighter version of a color
|
|
func StringToColor(str string) tcell.Color {
|
|
switch str {
|
|
case "black":
|
|
return tcell.ColorBlack
|
|
case "red":
|
|
return tcell.ColorMaroon
|
|
case "green":
|
|
return tcell.ColorGreen
|
|
case "yellow":
|
|
return tcell.ColorOlive
|
|
case "blue":
|
|
return tcell.ColorNavy
|
|
case "magenta":
|
|
return tcell.ColorPurple
|
|
case "cyan":
|
|
return tcell.ColorTeal
|
|
case "white":
|
|
return tcell.ColorSilver
|
|
case "brightblack", "lightblack":
|
|
return tcell.ColorGray
|
|
case "brightred", "lightred":
|
|
return tcell.ColorRed
|
|
case "brightgreen", "lightgreen":
|
|
return tcell.ColorLime
|
|
case "brightyellow", "lightyellow":
|
|
return tcell.ColorYellow
|
|
case "brightblue", "lightblue":
|
|
return tcell.ColorBlue
|
|
case "brightmagenta", "lightmagenta":
|
|
return tcell.ColorFuchsia
|
|
case "brightcyan", "lightcyan":
|
|
return tcell.ColorAqua
|
|
case "brightwhite", "lightwhite":
|
|
return tcell.ColorWhite
|
|
case "default":
|
|
return tcell.ColorDefault
|
|
default:
|
|
// Check if this is a 256 color
|
|
if num, err := strconv.Atoi(str); err == nil {
|
|
return GetColor256(num)
|
|
}
|
|
// Probably a truecolor hex value
|
|
return tcell.GetColor(str)
|
|
}
|
|
}
|
|
|
|
// GetColor256 returns the tcell color for a number between 0 and 255
|
|
func GetColor256(color int) tcell.Color {
|
|
if color == 0 {
|
|
return tcell.ColorDefault
|
|
}
|
|
return tcell.PaletteColor(color)
|
|
}
|