refactor: retire tui.BuiltinThemes in favor of theming.List

Make internal/theming.List the single source of truth for themes:

- Drop tui.BuiltinThemes, tui.ThemeByID, and the NvChadTheme field on
  tui.Theme. tui.Theme is now a thin {ID, Name} display value.
- Wizard populates its dropdown from theming.List() and uses
  DisplayName for the label.
- neovim.resolvedTheme uses theming.Get (with theming.List()[0] as a
  fallback) instead of a caller-supplied []tui.Theme slice.
- neovim.New signature loses the themes []tui.Theme parameter; update
  cmd/archduino/main.go and all neovim tests to match.

Break the resulting neovim→theming import cycle (theming previously
imported neovim for WriteChadrc) by inverting the dependency:
theming exposes a ChadrcWriter function hook, and neovim registers
its WriteChadrc into it via package init. The theming test stubs
ChadrcWriter directly to exercise the chadrc-rewrite branch without
pulling neovim in and re-creating the cycle.

Delete the now-obsolete TestBuiltinThemesCarryNvChadName / TestThemeByID*
tests in internal/tui/state_test.go — their coverage lives in the
theming package's own tests.
This commit is contained in:
MDietermann
2026-04-14 13:35:10 +02:00
parent 12c502d48a
commit 4f7411ca95
8 changed files with 61 additions and 76 deletions

View File

@@ -17,12 +17,19 @@ import (
"github.com/MDietermann/ArduinoArchSetup/internal/config"
"github.com/MDietermann/ArduinoArchSetup/internal/module"
"github.com/MDietermann/ArduinoArchSetup/internal/platform"
"github.com/MDietermann/ArduinoArchSetup/internal/tui"
"github.com/MDietermann/ArduinoArchSetup/internal/theming"
)
//go:embed assets/*
var assets embed.FS
// Register WriteChadrc with the theming package so theming.Apply can
// rewrite chadrc.lua on theme changes without importing this package
// (which would be a cycle: neovim imports theming).
func init() {
theming.ChadrcWriter = WriteChadrc
}
// assetMap lists each asset and its destination under ~/.config/nvim.
// Order matters: Detect iterates in this order and Apply writes in this order.
var assetMap = []struct {
@@ -40,14 +47,13 @@ var assetMap = []struct {
type Module struct {
cfg config.NeovimConfig
themeID string
themes []tui.Theme
pm platform.PackageManager
git platform.GitClient
}
// New constructs a Neovim Module.
func New(cfg config.NeovimConfig, themeID string, themes []tui.Theme, pm platform.PackageManager, git platform.GitClient) Module {
return Module{cfg: cfg, themeID: themeID, themes: themes, pm: pm, git: git}
func New(cfg config.NeovimConfig, themeID string, pm platform.PackageManager, git platform.GitClient) Module {
return Module{cfg: cfg, themeID: themeID, pm: pm, git: git}
}
type state struct {
@@ -79,11 +85,11 @@ func nvimCfg() (string, error) {
}
func (m Module) resolvedTheme() string {
if t, ok := tui.ThemeByID(m.themes, m.themeID); ok && t.NvChadTheme != "" {
if t, ok := theming.Get(m.themeID); ok && t.NvChadTheme != "" {
return t.NvChadTheme
}
if len(m.themes) > 0 {
return m.themes[0].NvChadTheme
if list := theming.List(); len(list) > 0 && list[0].NvChadTheme != "" {
return list[0].NvChadTheme
}
return "gruvbox"
}

View File

@@ -10,7 +10,6 @@ import (
"github.com/MDietermann/ArduinoArchSetup/internal/config"
"github.com/MDietermann/ArduinoArchSetup/internal/module"
"github.com/MDietermann/ArduinoArchSetup/internal/platform"
"github.com/MDietermann/ArduinoArchSetup/internal/tui"
)
type fakeRunner struct {
@@ -58,7 +57,7 @@ func TestDetectFreshSystem(t *testing.T) {
home := t.TempDir()
t.Setenv("HOME", home)
r := &fakeRunner{installed: map[string]bool{}}
m := New(config.NeovimConfig{NvChadStarterRepo: "https://x/starter"}, "gruvbox", tui.BuiltinThemes, platform.NewPacman(r), platform.GitClient{Runner: r})
m := New(config.NeovimConfig{NvChadStarterRepo: "https://x/starter"}, "gruvbox", platform.NewPacman(r), platform.GitClient{Runner: r})
st, err := m.Detect(context.Background())
if err != nil {
t.Fatal(err)
@@ -81,7 +80,7 @@ func TestDetectNonNvChadTriggersBackup(t *testing.T) {
os.MkdirAll(filepath.Join(home, ".config", "nvim", "lua"), 0o755)
os.WriteFile(filepath.Join(home, ".config", "nvim", "lua", "plugins.lua"), []byte("-- lazyvim"), 0o644)
r := &fakeRunner{installed: map[string]bool{}}
m := New(config.NeovimConfig{NvChadStarterRepo: "https://x/starter"}, "gruvbox", tui.BuiltinThemes, platform.NewPacman(r), platform.GitClient{Runner: r})
m := New(config.NeovimConfig{NvChadStarterRepo: "https://x/starter"}, "gruvbox", platform.NewPacman(r), platform.GitClient{Runner: r})
st, _ := m.Detect(context.Background())
if !st.(state).BackupNeeded {
t.Fatal("expected BackupNeeded=true")
@@ -92,7 +91,7 @@ func TestPlanApplyInstallsEverything(t *testing.T) {
home := t.TempDir()
t.Setenv("HOME", home)
r := &fakeRunner{installed: map[string]bool{}}
m := New(config.NeovimConfig{NvChadStarterRepo: "https://x/starter"}, "dracula", tui.BuiltinThemes, platform.NewPacman(r), platform.GitClient{Runner: r})
m := New(config.NeovimConfig{NvChadStarterRepo: "https://x/starter"}, "dracula", platform.NewPacman(r), platform.GitClient{Runner: r})
st, _ := m.Detect(context.Background())
p, _ := m.Plan(context.Background(), st)
if len(p.Actions) == 0 {
@@ -150,7 +149,7 @@ func TestPlanAfterApplyIsEmpty(t *testing.T) {
home := t.TempDir()
t.Setenv("HOME", home)
r := &fakeRunner{installed: map[string]bool{"neovim": true}}
m := New(config.NeovimConfig{NvChadStarterRepo: "https://x/starter"}, "gruvbox", tui.BuiltinThemes, platform.NewPacman(r), platform.GitClient{Runner: r})
m := New(config.NeovimConfig{NvChadStarterRepo: "https://x/starter"}, "gruvbox", platform.NewPacman(r), platform.GitClient{Runner: r})
st, _ := m.Detect(context.Background())
p, _ := m.Plan(context.Background(), st)
_ = m.Apply(context.Background(), p, nopReporter{})
@@ -165,14 +164,16 @@ func TestUnknownThemeFallsBackToFirst(t *testing.T) {
home := t.TempDir()
t.Setenv("HOME", home)
r := &fakeRunner{installed: map[string]bool{"neovim": true}}
m := New(config.NeovimConfig{NvChadStarterRepo: "https://x/starter"}, "does-not-exist", tui.BuiltinThemes, platform.NewPacman(r), platform.GitClient{Runner: r})
m := New(config.NeovimConfig{NvChadStarterRepo: "https://x/starter"}, "does-not-exist", platform.NewPacman(r), platform.GitClient{Runner: r})
st, _ := m.Detect(context.Background())
p, _ := m.Plan(context.Background(), st)
if err := m.Apply(context.Background(), p, nopReporter{}); err != nil {
t.Fatal(err)
}
chadrc, _ := os.ReadFile(filepath.Join(home, ".config", "nvim", "lua", "chadrc.lua"))
if !strings.Contains(string(chadrc), `theme = "gruvbox"`) {
// Unknown theme IDs fall back to the first entry of theming.List(),
// which is sorted alphabetically by ID (currently "catppuccin").
if !strings.Contains(string(chadrc), `theme = "catppuccin"`) {
t.Fatalf("fallback theme not applied: %s", chadrc)
}
}