summaryrefslogtreecommitdiff
path: root/internal/cli
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-23 22:43:59 +0200
committerPaul Buetow <paul@buetow.org>2026-02-23 22:43:59 +0200
commitb14a1ccfbf60b0866911788176491af5907702eb (patch)
treeeccb1293b62e3ccb8d7e24f9a6170e5c2141bc3b /internal/cli
parent13272b89c8e343f53f0633fd057d7a0054113524 (diff)
Release v0.5.2v0.5.2
Diffstat (limited to 'internal/cli')
-rw-r--r--internal/cli/cli.go36
-rw-r--r--internal/cli/cli_test.go35
2 files changed, 67 insertions, 4 deletions
diff --git a/internal/cli/cli.go b/internal/cli/cli.go
index ef00010..7152496 100644
--- a/internal/cli/cli.go
+++ b/internal/cli/cli.go
@@ -156,6 +156,7 @@ func (c *CLI) run(ctx context.Context, argv []string) int {
// c.lastResult is updated by dispatch and accessible between iterations.
func (c *CLI) shellLoop(ctx context.Context) int {
ec := 0
+ logMsg("Interactive mode (vi keys): Ctrl-] for normal mode, i for insert | Enter fuzzy picker | ctrl-t/y/o/e (or alt-t/y/o/e) in picker")
for {
line, err := c.sh.ReadLine(ctx)
@@ -170,12 +171,16 @@ func (c *CLI) shellLoop(ctx context.Context) int {
argv := strings.Fields(line)
if len(argv) == 0 {
- // Empty input — run fzf picker just as the Ruby nil branch does.
- result, fzfErr := c.st.Fzf(ctx)
+ // Empty input — run fzf picker.
+ result, fzfErr := c.st.FzfInteractive(ctx)
if fzfErr != nil {
warn(fzfErr.Error())
- } else if result != "" {
- c.lastResult = result
+ continue
+ }
+ if result.Description != "" {
+ c.lastResult = result.Description
+ logMsg(fmt.Sprintf("Picked: %s", result.Description))
+ ec = c.dispatchPickerAction(ctx, result)
}
continue
}
@@ -198,6 +203,29 @@ func (c *CLI) shellLoop(ctx context.Context) int {
return ec
}
+func pickerActionArgv(action store.PickerAction, description string) []string {
+ switch action {
+ case store.PickerCat:
+ return []string{"cat", description}
+ case store.PickerPaste:
+ return []string{"paste", description}
+ case store.PickerOpen:
+ return []string{"open", description}
+ case store.PickerEdit:
+ return []string{"edit", description}
+ default:
+ return nil
+ }
+}
+
+func (c *CLI) dispatchPickerAction(ctx context.Context, result store.PickerResult) int {
+ argv := pickerActionArgv(result.Action, result.Description)
+ if len(argv) == 0 {
+ return 0
+ }
+ return c.dispatch(ctx, argv)
+}
+
// dispatch routes a parsed argv slice to the appropriate handler.
// It returns an exit code and updates c.lastResult when a non-empty result
// is produced. The function is split into helpers to keep each branch under
diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go
index 250601c..2ad52db 100644
--- a/internal/cli/cli_test.go
+++ b/internal/cli/cli_test.go
@@ -364,6 +364,41 @@ func TestMakeActionFn_nil(t *testing.T) {
}
}
+// ---- pickerActionArgv -------------------------------------------------------
+
+// TestPickerActionArgv verifies the direct mapping from picker action keys to
+// CLI argv commands used by interactive empty-line fzf selection.
+func TestPickerActionArgv(t *testing.T) {
+ desc := "docs/secret.txt"
+
+ cases := []struct {
+ name string
+ action store.PickerAction
+ want []string
+ }{
+ {name: "select", action: store.PickerSelect, want: nil},
+ {name: "cat", action: store.PickerCat, want: []string{"cat", desc}},
+ {name: "paste", action: store.PickerPaste, want: []string{"paste", desc}},
+ {name: "open", action: store.PickerOpen, want: []string{"open", desc}},
+ {name: "edit", action: store.PickerEdit, want: []string{"edit", desc}},
+ {name: "unknown", action: store.PickerAction("weird"), want: nil},
+ }
+
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ got := pickerActionArgv(tc.action, desc)
+ if len(got) != len(tc.want) {
+ t.Fatalf("pickerActionArgv(%q) len = %d; want %d (%v)", tc.action, len(got), len(tc.want), got)
+ }
+ for i := range got {
+ if got[i] != tc.want[i] {
+ t.Fatalf("pickerActionArgv(%q)[%d] = %q; want %q", tc.action, i, got[i], tc.want[i])
+ }
+ }
+ })
+ }
+}
+
// ---- remaining dispatchSearch branches --------------------------------------
// TestDispatch_searchActions exercises all SearchActions entries on an empty