Support include when loading themes from dirs

This commit is contained in:
Kovid Goyal 2023-02-26 21:16:29 +05:30
parent 0b09d18b36
commit 7ce64fcde0
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 91 additions and 33 deletions

View File

@ -29,7 +29,9 @@ type ConfigLine struct {
}
type ConfigParser struct {
LineHandler func(key, val string) error
LineHandler func(key, val string) error
CommentsHandler func(line string) error
SourceHandler func(text, path string)
bad_lines []ConfigLine
seen_includes map[string]bool
@ -73,7 +75,16 @@ func (self *ConfigParser) parse(scanner Scanner, name, base_path_for_includes st
for scanner.Scan() {
line := strings.TrimLeft(scanner.Text(), " ")
lnum++
if line == "" || strings.HasPrefix(line, "#") {
if line == "" {
continue
}
if line[0] == '#' {
if self.CommentsHandler != nil {
err := self.CommentsHandler(line)
if err != nil {
self.bad_lines = append(self.bad_lines, ConfigLine{Src_file: name, Line: line, Line_number: lnum, Err: err})
}
}
continue
}
key, val, _ := strings.Cut(line, " ")
@ -149,6 +160,9 @@ func (self *ConfigParser) ParseFiles(paths ...string) error {
if err != nil {
return err
}
if self.SourceHandler != nil {
self.SourceHandler(utils.UnsafeBytesToString(raw), path)
}
}
return nil
}

View File

@ -4,12 +4,12 @@ package themes
import (
"archive/zip"
"bufio"
"encoding/json"
"errors"
"fmt"
"io"
"io/fs"
"kitty/tools/config"
"kitty/tools/utils"
"kitty/tools/utils/style"
"net/http"
@ -136,52 +136,45 @@ type ThemeMetadata struct {
Author string `json:"author"`
}
func parse_theme_metadata(raw string) *ThemeMetadata {
scanner := bufio.NewScanner(strings.NewReader(raw))
func parse_theme_metadata(path string) (*ThemeMetadata, string, error) {
var in_metadata, in_blurb, finished_metadata bool
ans := ThemeMetadata{}
settings := utils.NewSet[string]()
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
continue
read_is_dark := func(key, val string) (err error) {
settings.Add(key)
if key == "background" {
val = strings.TrimSpace(val)
if val != "" {
bg, err := style.ParseColor(val)
if err == nil {
ans.Is_dark = utils.Max(bg.Red, bg.Green, bg.Green) < 115
}
}
}
return
}
read_metadata := func(line string) (err error) {
is_block := strings.HasPrefix(line, "## ")
if in_metadata && !is_block {
finished_metadata = true
}
if finished_metadata {
if line[0] != '#' {
key, val, found := strings.Cut(line, " ")
if found {
settings.Add(key)
if key == "background" {
val = strings.TrimSpace(val)
if val != "" {
bg, err := style.ParseColor(val)
if err == nil {
ans.Is_dark = utils.Max(bg.Red, bg.Green, bg.Green) < 115
}
}
}
}
}
continue
return
}
if !in_metadata && is_block {
in_metadata = true
}
if !in_metadata {
continue
return
}
line = line[3:]
if in_blurb {
ans.Blurb += " " + line
continue
return
}
key, val, found := strings.Cut(line, ":")
if !found {
continue
return
}
key = strings.TrimSpace(strings.ToLower(key))
val = strings.TrimSpace(val)
@ -198,9 +191,16 @@ func parse_theme_metadata(raw string) *ThemeMetadata {
case "license":
ans.License = val
}
return
}
source := ""
cp := config.ConfigParser{LineHandler: read_is_dark, CommentsHandler: read_metadata, SourceHandler: func(code, path string) { source = code }}
err := cp.ParseFiles(path)
if err != nil {
return nil, "", err
}
ans.Num_settings = settings.Len()
return &ans
return &ans, source, nil
}
type Theme struct {
@ -217,7 +217,7 @@ type Themes struct {
}
var camel_case_pat = (&utils.Once[*regexp.Regexp]{Run: func() *regexp.Regexp {
return regexp.MustCompile(`[a-z][A-Z]`)
return regexp.MustCompile(`([a-z])([A-Z])`)
}}).Get
func theme_name_from_file_name(fname string) string {
@ -237,12 +237,10 @@ func (self *Themes) add_from_dir(dirpath string) error {
}
for _, e := range entries {
if !e.IsDir() && strings.HasSuffix(e.Name(), ".conf") {
confb, err := os.ReadFile(e.Name())
m, conf, err := parse_theme_metadata(filepath.Join(dirpath, e.Name()))
if err != nil {
return err
}
conf := utils.UnsafeBytesToString(confb)
m := parse_theme_metadata(conf)
if m.Name == "" {
m.Name = theme_name_from_file_name(e.Name())
}

View File

@ -0,0 +1,46 @@
// License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
package themes
import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
)
var _ = fmt.Print
func TestThemeCollections(t *testing.T) {
for fname, expected := range map[string]string{
"moose": "Moose",
"mooseCat": "Moose Cat",
"a_bC": "A B C",
} {
actual := theme_name_from_file_name(fname)
if diff := cmp.Diff(expected, actual); diff != "" {
t.Fatalf("Unexpected theme name for %s:\n%s", fname, diff)
}
}
tdir := t.TempDir()
pt := func(expected ThemeMetadata, lines ...string) {
os.WriteFile(filepath.Join(tdir, "temp.conf"), []byte(strings.Join(lines, "\n")), 0o600)
actual, _, err := parse_theme_metadata(filepath.Join(tdir, "temp.conf"))
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(&expected, actual); diff != "" {
t.Fatalf("Failed to parse:\n%s\n\n%s", strings.Join(lines, "\n"), diff)
}
}
pt(ThemeMetadata{Name: "XYZ", Blurb: "a b", Author: "A", Is_dark: true, Num_settings: 2},
"# some crap", " ", "## ", "## author: A", "## name: XYZ", "## blurb: a", "## b", "", "color red", "background black", "include inc.conf")
os.WriteFile(filepath.Join(tdir, "inc.conf"), []byte("background white"), 0o600)
pt(ThemeMetadata{Name: "XYZ", Blurb: "a b", Author: "A", Num_settings: 2},
"# some crap", " ", "## ", "## author: A", "## name: XYZ", "## blurb: a", "## b", "", "color red", "background black", "include inc.conf")
}