Support include when loading themes from dirs
This commit is contained in:
parent
0b09d18b36
commit
7ce64fcde0
@ -29,7 +29,9 @@ type ConfigLine struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ConfigParser 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
|
bad_lines []ConfigLine
|
||||||
seen_includes map[string]bool
|
seen_includes map[string]bool
|
||||||
@ -73,7 +75,16 @@ func (self *ConfigParser) parse(scanner Scanner, name, base_path_for_includes st
|
|||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
line := strings.TrimLeft(scanner.Text(), " ")
|
line := strings.TrimLeft(scanner.Text(), " ")
|
||||||
lnum++
|
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
|
continue
|
||||||
}
|
}
|
||||||
key, val, _ := strings.Cut(line, " ")
|
key, val, _ := strings.Cut(line, " ")
|
||||||
@ -149,6 +160,9 @@ func (self *ConfigParser) ParseFiles(paths ...string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if self.SourceHandler != nil {
|
||||||
|
self.SourceHandler(utils.UnsafeBytesToString(raw), path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,12 +4,12 @@ package themes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"kitty/tools/config"
|
||||||
"kitty/tools/utils"
|
"kitty/tools/utils"
|
||||||
"kitty/tools/utils/style"
|
"kitty/tools/utils/style"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -136,52 +136,45 @@ type ThemeMetadata struct {
|
|||||||
Author string `json:"author"`
|
Author string `json:"author"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse_theme_metadata(raw string) *ThemeMetadata {
|
func parse_theme_metadata(path string) (*ThemeMetadata, string, error) {
|
||||||
scanner := bufio.NewScanner(strings.NewReader(raw))
|
|
||||||
var in_metadata, in_blurb, finished_metadata bool
|
var in_metadata, in_blurb, finished_metadata bool
|
||||||
ans := ThemeMetadata{}
|
ans := ThemeMetadata{}
|
||||||
settings := utils.NewSet[string]()
|
settings := utils.NewSet[string]()
|
||||||
for scanner.Scan() {
|
read_is_dark := func(key, val string) (err error) {
|
||||||
line := strings.TrimSpace(scanner.Text())
|
settings.Add(key)
|
||||||
if line == "" {
|
if key == "background" {
|
||||||
continue
|
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, "## ")
|
is_block := strings.HasPrefix(line, "## ")
|
||||||
if in_metadata && !is_block {
|
if in_metadata && !is_block {
|
||||||
finished_metadata = true
|
finished_metadata = true
|
||||||
}
|
}
|
||||||
if finished_metadata {
|
if finished_metadata {
|
||||||
if line[0] != '#' {
|
return
|
||||||
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
|
|
||||||
}
|
}
|
||||||
if !in_metadata && is_block {
|
if !in_metadata && is_block {
|
||||||
in_metadata = true
|
in_metadata = true
|
||||||
}
|
}
|
||||||
if !in_metadata {
|
if !in_metadata {
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
line = line[3:]
|
line = line[3:]
|
||||||
if in_blurb {
|
if in_blurb {
|
||||||
ans.Blurb += " " + line
|
ans.Blurb += " " + line
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
key, val, found := strings.Cut(line, ":")
|
key, val, found := strings.Cut(line, ":")
|
||||||
if !found {
|
if !found {
|
||||||
continue
|
return
|
||||||
}
|
}
|
||||||
key = strings.TrimSpace(strings.ToLower(key))
|
key = strings.TrimSpace(strings.ToLower(key))
|
||||||
val = strings.TrimSpace(val)
|
val = strings.TrimSpace(val)
|
||||||
@ -198,9 +191,16 @@ func parse_theme_metadata(raw string) *ThemeMetadata {
|
|||||||
case "license":
|
case "license":
|
||||||
ans.License = val
|
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()
|
ans.Num_settings = settings.Len()
|
||||||
return &ans
|
return &ans, source, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Theme struct {
|
type Theme struct {
|
||||||
@ -217,7 +217,7 @@ type Themes struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var camel_case_pat = (&utils.Once[*regexp.Regexp]{Run: func() *regexp.Regexp {
|
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
|
}}).Get
|
||||||
|
|
||||||
func theme_name_from_file_name(fname string) string {
|
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 {
|
for _, e := range entries {
|
||||||
if !e.IsDir() && strings.HasSuffix(e.Name(), ".conf") {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
conf := utils.UnsafeBytesToString(confb)
|
|
||||||
m := parse_theme_metadata(conf)
|
|
||||||
if m.Name == "" {
|
if m.Name == "" {
|
||||||
m.Name = theme_name_from_file_name(e.Name())
|
m.Name = theme_name_from_file_name(e.Name())
|
||||||
}
|
}
|
||||||
|
|||||||
46
tools/themes/collection_test.go
Normal file
46
tools/themes/collection_test.go
Normal 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")
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user