From 4f37cff65d79273b67faa83b1b1f78a69b8fddaa Mon Sep 17 00:00:00 2001 From: Amolith Date: Mon, 1 Dec 2025 06:40:42 -0700 Subject: [PATCH] fix(editor): fix opening `$EDITOR` w/ and w/o args (#1520) --- internal/tui/components/chat/editor/editor.go | 8 ++---- internal/tui/util/shell.go | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 internal/tui/util/shell.go diff --git a/internal/tui/components/chat/editor/editor.go b/internal/tui/components/chat/editor/editor.go index de1a98b34595613594e83063cc12add4ba820c84..8ae8ed3d7dd7b5f277b8d0076b97859b5c6aa73f 100644 --- a/internal/tui/components/chat/editor/editor.go +++ b/internal/tui/components/chat/editor/editor.go @@ -6,7 +6,6 @@ import ( "math/rand" "net/http" "os" - "os/exec" "path/filepath" "runtime" "slices" @@ -112,11 +111,8 @@ func (m *editorCmp) openEditor(value string) tea.Cmd { if _, err := tmpfile.WriteString(value); err != nil { return util.ReportError(err) } - c := exec.CommandContext(context.TODO(), editor, tmpfile.Name()) - c.Stdin = os.Stdin - c.Stdout = os.Stdout - c.Stderr = os.Stderr - return tea.ExecProcess(c, func(err error) tea.Msg { + cmdStr := editor + " " + tmpfile.Name() + return util.ExecShell(context.TODO(), cmdStr, func(err error) tea.Msg { if err != nil { return util.ReportError(err) } diff --git a/internal/tui/util/shell.go b/internal/tui/util/shell.go new file mode 100644 index 0000000000000000000000000000000000000000..43690c8aaacd6a396b02220536d022c674f16111 --- /dev/null +++ b/internal/tui/util/shell.go @@ -0,0 +1,26 @@ +package util + +import ( + "context" + "errors" + "os/exec" + + tea "charm.land/bubbletea/v2" + "mvdan.cc/sh/v3/shell" +) + +// ExecShell parses a shell command string and executes it with exec.Command. +// Uses shell.Fields for proper handling of shell syntax like quotes and +// arguments while preserving TTY handling for terminal editors. +func ExecShell(ctx context.Context, cmdStr string, callback tea.ExecCallback) tea.Cmd { + fields, err := shell.Fields(cmdStr, nil) + if err != nil { + return ReportError(err) + } + if len(fields) == 0 { + return ReportError(errors.New("empty command")) + } + + cmd := exec.CommandContext(ctx, fields[0], fields[1:]...) + return tea.ExecProcess(cmd, callback) +}