Now we can load them in via JSON files at launch time. They are embded in the final exe though...
155 lines
3.7 KiB
Go
155 lines
3.7 KiB
Go
package theme
|
|
|
|
import (
|
|
"embed"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/fs"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/charmbracelet/lipgloss"
|
|
)
|
|
|
|
//go:embed themes/*.json
|
|
var embeddedThemes embed.FS
|
|
|
|
// LoadEmbeddedThemesJSON reads all embedded theme JSON files and unmarshals
|
|
// them into ThemeJSON objects keyed by theme name.
|
|
func LoadEmbeddedThemesJSON() (map[string]ThemeJSON, error) {
|
|
paths, err := fs.Glob(embeddedThemes, "themes/*.json")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sort.Strings(paths)
|
|
|
|
out := make(map[string]ThemeJSON, len(paths))
|
|
for _, path := range paths {
|
|
b, readErr := embeddedThemes.ReadFile(path)
|
|
if readErr != nil {
|
|
return nil, fmt.Errorf("read embedded theme %q: %w", path, readErr)
|
|
}
|
|
|
|
var th ThemeJSON
|
|
if unmarshalErr := json.Unmarshal(b, &th); unmarshalErr != nil {
|
|
return nil, fmt.Errorf("decode embedded theme %q: %w", path, unmarshalErr)
|
|
}
|
|
|
|
if strings.TrimSpace(th.Name) == "" {
|
|
th.Name = strings.TrimSuffix(filepath.Base(path), ".json")
|
|
}
|
|
|
|
out[th.Name] = th
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func MapEmbededThemeToEditorTheme(em map[string]ThemeJSON) map[string]EditorTheme {
|
|
out := make(map[string]EditorTheme, len(em))
|
|
|
|
for name, in := range em {
|
|
line := styleFromJSON(in.Line)
|
|
lineBg := colorString(in.Line.BG)
|
|
|
|
syntaxExact := make(map[string]lipgloss.Style, len(in.Syntax.Exact))
|
|
for capture, col := range in.Syntax.Exact {
|
|
c := normalizeCaptureKey(capture)
|
|
if c == "" {
|
|
continue
|
|
}
|
|
syntaxExact[c] = syntaxColorStyle(col, lineBg)
|
|
}
|
|
|
|
syntaxGroup := make(map[string]lipgloss.Style, len(in.Syntax.Group))
|
|
for group, col := range in.Syntax.Group {
|
|
g := normalizeCaptureKey(group)
|
|
if g == "" {
|
|
continue
|
|
}
|
|
syntaxGroup[g] = syntaxColorStyle(col, lineBg)
|
|
}
|
|
|
|
key := strings.TrimSpace(name)
|
|
if key == "" {
|
|
key = strings.TrimSpace(in.Name)
|
|
}
|
|
if key == "" {
|
|
continue
|
|
}
|
|
|
|
out[key] = EditorTheme{
|
|
Cursors: CursorTheme{
|
|
Normal: styleFromJSON(in.Cursors.Normal),
|
|
Insert: styleFromJSON(in.Cursors.Insert),
|
|
Command: styleFromJSON(in.Cursors.Command),
|
|
Replace: styleFromJSON(in.Cursors.Replace),
|
|
},
|
|
Gutter: GutterTheme{
|
|
Default: styleFromJSON(in.Gutter.Default),
|
|
CurrentLine: styleFromJSON(in.Gutter.CurrentLine),
|
|
},
|
|
VisualHightlight: styleFromJSON(in.VisualHighlight),
|
|
StatusBar: StatusBarTheme{
|
|
Default: styleFromJSON(in.StatusBar.Default),
|
|
},
|
|
CommandLine: CommandLineTheme{
|
|
Error: styleFromJSON(in.CommandLine.Error),
|
|
OutputBorder: styleFromJSON(in.CommandLine.OutputBorder),
|
|
ContinueMessage: styleFromJSON(in.CommandLine.ContinueMessage),
|
|
},
|
|
Line: line,
|
|
Background: styleFromJSON(in.Background),
|
|
Syntax: SyntaxTheme{
|
|
Exact: syntaxExact,
|
|
Group: syntaxGroup,
|
|
},
|
|
}
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
// MapEmbeddedThemeToEditorTheme is a correctly spelled alias for
|
|
// MapEmbededThemeToEditorTheme.
|
|
func MapEmbeddedThemeToEditorTheme(em map[string]ThemeJSON) map[string]EditorTheme {
|
|
return MapEmbededThemeToEditorTheme(em)
|
|
}
|
|
|
|
func styleFromJSON(in ColorStyleJSON) lipgloss.Style {
|
|
out := lipgloss.NewStyle()
|
|
|
|
if fg := colorString(in.FG); fg != "" {
|
|
out = out.Foreground(lipgloss.Color(fg))
|
|
}
|
|
if bg := colorString(in.BG); bg != "" {
|
|
out = out.Background(lipgloss.Color(bg))
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func colorString(c string) string {
|
|
return strings.TrimSpace(c)
|
|
}
|
|
|
|
func normalizeCaptureKey(k string) string {
|
|
k = strings.TrimSpace(strings.ToLower(k))
|
|
k = strings.TrimPrefix(k, "@")
|
|
return k
|
|
}
|
|
|
|
func syntaxColorStyle(fg, bg string) lipgloss.Style {
|
|
out := lipgloss.NewStyle()
|
|
|
|
if f := colorString(fg); f != "" {
|
|
out = out.Foreground(lipgloss.Color(f))
|
|
}
|
|
if b := colorString(bg); b != "" {
|
|
out = out.Background(lipgloss.Color(b))
|
|
}
|
|
|
|
return out
|
|
}
|