From 48ad078ad55114ebac6f66d9965b2e256f804c8e Mon Sep 17 00:00:00 2001 From: Falk Harrison Date: Tue, 29 Sep 2026 10:46:31 +0200 Subject: [PATCH] fix(selector): extract selected columns without a map command --- internal/headless/selector_columns_test.go | 31 ++++++++++++ internal/headless/vars.go | 4 +- internal/ui/selector_columns_test.go | 57 ++++++++++++++++++++++ internal/ui/var_resolve_keys.go | 8 +-- 4 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 internal/headless/selector_columns_test.go create mode 100644 internal/ui/selector_columns_test.go diff --git a/internal/headless/selector_columns_test.go b/internal/headless/selector_columns_test.go new file mode 100644 index 0000000..1afec71 --- /dev/null +++ b/internal/headless/selector_columns_test.go @@ -0,0 +1,31 @@ +package headless + +import ( + "testing" + + "github.com/cheatmd-dev/cheatmd/pkg/config" + + "github.com/cheatmd-dev/cheatmd/internal/resolver" + "github.com/cheatmd-dev/cheatmd/pkg/parser" +) + +func TestResolvedSelectorColumn(t *testing.T) { + previousShell := config.Get().Shell + config.Get().Shell = "/bin/sh" + t.Cleanup(func() { config.Get().Shell = previousShell }) + for _, tc := range []struct{ name, args, want string }{ + {"select column without map", "--delimiter : --select-column 1", "alpha"}, + {"select column with map", `--delimiter : --select-column 1 --map "printf mapped"`, "mapped"}, + {"display column only", "--delimiter : --column 2", "alpha:First"}, + {"no transform", "", "alpha:First"}, + } { + t.Run(tc.name, func(t *testing.T) { + s := &RunnerSession{} + v := &resolver.VarState{Def: parser.VarDef{Args: tc.args}} + s.applyResolvedValue(v, "alpha:First") + if !v.Resolved || v.Value != tc.want { + t.Fatalf("resolved=%v, value=%q, want resolved value %q", v.Resolved, v.Value, tc.want) + } + }) + } +} diff --git a/internal/headless/vars.go b/internal/headless/vars.go index 1e63b51..82369a7 100644 --- a/internal/headless/vars.go +++ b/internal/headless/vars.go @@ -368,9 +368,7 @@ func (s *RunnerSession) extractPromptValue(pv promptVar, promptRes *promptRespon func (s *RunnerSession) applyResolvedValue(vs *resolver.VarState, val string) { selectOpts := resolver.ParseSelectorOpts(vs.Def.Args) - if selectOpts.MapCmd != "" { - val = resolver.ApplyMapTransform(val, selectOpts) - } + val = resolver.ApplyMapTransform(val, selectOpts) vs.Value = val vs.Resolved = true diff --git a/internal/ui/selector_columns_test.go b/internal/ui/selector_columns_test.go new file mode 100644 index 0000000..268a02d --- /dev/null +++ b/internal/ui/selector_columns_test.go @@ -0,0 +1,57 @@ +package ui + +import ( + "testing" + + "github.com/cheatmd-dev/cheatmd/pkg/config" + "github.com/cheatmd-dev/cheatmd/pkg/executor" + + tea "github.com/charmbracelet/bubbletea" +) + +func TestAcceptSelectorColumn(t *testing.T) { + previousShell := config.Get().Shell + config.Get().Shell = "/bin/sh" + t.Cleanup(func() { config.Get().Shell = previousShell }) + for _, tc := range []struct{ name, args, single, multi string }{ + {"select column without map", "--delimiter : --select-column 1", "alpha", "alpha:beta"}, + {"select column with map", `--delimiter : --select-column 1 --map "printf mapped"`, "mapped", "mapped:mapped"}, + {"display column only", "--delimiter : --column 2", "alpha:First", "alpha:First:beta:Second"}, + {"no transform", "", "alpha:First", "alpha:First,beta:Second"}, + } { + for _, multi := range []bool{false, true} { + mode := "single" + if multi { + mode = "multi" + } + t.Run(tc.name+"/"+mode, func(t *testing.T) { + m, index := setupTestModel() + m.executor = executor.NewExecutor(index) + v := &index.Cheats[0].Vars[0] + v.Args = tc.args + v.Shell = "printf 'alpha:First\\nbeta:Second\\n'" + want := tc.single + if multi { + v.Args += " --multi" + want = tc.multi + } + m.selected = index.Cheats[0] + m.startVarResolutionInternal() + cmd := m.prepareCurrentVar() + if cmd == nil { + t.Fatal("shell selector did not start") + } + m.Update(cmd()) + if multi { + m.handleVarResolveKey(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{' '}}) + m.handleVarResolveKey(tea.KeyMsg{Type: tea.KeyDown}) + m.handleVarResolveKey(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{' '}}) + } + m.handleVarResolveKey(tea.KeyMsg{Type: tea.KeyEnter}) + if got := m.selected.Scope["var"]; got != want { + t.Fatalf("selected value = %q, want %q", got, want) + } + }) + } + } +} diff --git a/internal/ui/var_resolve_keys.go b/internal/ui/var_resolve_keys.go index abb3bb1..900d2c8 100644 --- a/internal/ui/var_resolve_keys.go +++ b/internal/ui/var_resolve_keys.go @@ -289,9 +289,7 @@ func (m *mainModel) acceptVarValue(bypassSelection bool) tea.Cmd { } else if m.varState.selectOpts.Multi && len(vs.multiSelected) > 0 { var mapped []string for _, selected := range vs.multiSelected { - if m.varState.selectOpts.MapCmd != "" { - selected = applyMapTransform(selected, m.varState.selectOpts) - } + selected = applyMapTransform(selected, m.varState.selectOpts) mapped = append(mapped, selected) } delim := m.varState.selectOpts.Delimiter @@ -302,9 +300,7 @@ func (m *mainModel) acceptVarValue(bypassSelection bool) tea.Cmd { } else if m.varState.picker != nil { if opt, ok := m.varState.picker.Selected(); ok { selected := opt.Original - if m.varState.selectOpts.MapCmd != "" { - selected = applyMapTransform(selected, m.varState.selectOpts) - } + selected = applyMapTransform(selected, m.varState.selectOpts) value = selected } else { value = m.textInput.Value()