-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathgenerate.py
More file actions
executable file
·98 lines (82 loc) · 3.13 KB
/
generate.py
File metadata and controls
executable file
·98 lines (82 loc) · 3.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# ///
# generated by ai
import re
import sys
import tomllib
from pathlib import Path
DOTFILES = Path(__file__).parent.resolve()
TEMPLATE_DIR = DOTFILES / "template"
CONFIG_DIR = DOTFILES / "config"
def deep_merge(base, override):
result = dict(base)
for key, val in override.items():
if key in result and isinstance(result[key], dict) and isinstance(val, dict):
result[key] = deep_merge(result[key], val)
else:
result[key] = val
return result
def resolve_refs(config):
# resolve $name references against the colors palette, in any section
# multi-pass until stable so chained refs (e.g. fg=$bright_white, text=$fg) resolve fully
colors = config.get("colors", {})
while True:
resolved = {k: colors.get(v[1:], v) if isinstance(v, str) and v.startswith("$") else v
for k, v in colors.items()}
if resolved == colors:
break
colors = resolved
resolved_rest = {
k: {ik: colors.get(iv[1:], iv) if isinstance(iv, str) and iv.startswith("$") else iv
for ik, iv in v.items()} if isinstance(v, dict) else v
for k, v in config.items() if k != "colors"
}
return {**resolved_rest, "colors": colors}
def flatten(obj, prefix=None):
# flatten nested dicts to dotted keys: {"colors": {"fg": "fff"}} -> {"colors.fg": "fff"}
flat = {}
for k, v in obj.items():
key = f"{prefix}.{k}" if prefix else k
if isinstance(v, dict):
flat.update(flatten(v, key))
else:
flat[key] = str(v)
return flat
def load_config(theme):
theme_file = DOTFILES / "themes" / f"{theme}.toml"
if not theme_file.exists():
print(f"Error: theme '{theme}' not found at {theme_file}", file=sys.stderr)
sys.exit(1)
with open(theme_file, "rb") as f:
config = tomllib.load(f)
local = DOTFILES / "local.toml"
if local.exists():
with open(local, "rb") as f:
config = deep_merge(config, tomllib.load(f))
return resolve_refs(config)
def render(content, vars):
# only match {{key}} where key starts with a letter/underscore, avoiding
# conflicts with go template syntax like {{.Foo}} used in oh-my-posh templates
def replace(m):
key = m.group(1)
if key not in vars:
print(f"Warning: unknown placeholder {{{{{key}}}}}", file=sys.stderr)
return m.group(0)
return vars[key]
return re.sub(r"\{\{([A-Za-z_][\w.]*)\}\}", replace, content)
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <theme>", file=sys.stderr)
available = sorted(p.stem for p in (DOTFILES / "themes").glob("*.toml"))
print(f"Available themes: {', '.join(available)}", file=sys.stderr)
sys.exit(1)
config = load_config(sys.argv[1])
vars = flatten(config)
for src in sorted(TEMPLATE_DIR.rglob("*")):
if not src.is_file():
continue
dest = CONFIG_DIR / src.relative_to(TEMPLATE_DIR)
dest.parent.mkdir(parents=True, exist_ok=True)
dest.write_text(render(src.read_text(), vars))
print(f"- generated {dest}")