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

@ -30,6 +30,8 @@ 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
} }

View File

@ -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,24 +136,11 @@ 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())
if line == "" {
continue
}
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) settings.Add(key)
if key == "background" { if key == "background" {
val = strings.TrimSpace(val) val = strings.TrimSpace(val)
@ -164,24 +151,30 @@ func parse_theme_metadata(raw string) *ThemeMetadata {
} }
} }
} }
return
} }
read_metadata := func(line string) (err error) {
is_block := strings.HasPrefix(line, "## ")
if in_metadata && !is_block {
finished_metadata = true
} }
continue if finished_metadata {
return
} }
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())
} }

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")
}