fix(editor): parse EDITOR command with arguments

Amolith created

Current Crush gives the following error when I try to open my `EDITOR`
with `ctrl+o`:

```
Error reported error="exec: \"zed --wait\": executable file not found in $PATH" source=path/to/internal/tui/util/util.go:27
```

This fix properly handles EDITOR values containing arguments, like `zed
--wait`, by using the shell interpreter we already depend on to separate
the executable from its args before invoking `exec.CommandContext`.

Assisted-by: Claude Sonnet 4.5 via Crush

Change summary

internal/tui/components/chat/editor/editor.go | 37 ++++++++++++++++++++
1 file changed, 36 insertions(+), 1 deletion(-)

Detailed changes

internal/tui/components/chat/editor/editor.go 🔗

@@ -30,6 +30,7 @@ import (
 	"github.com/charmbracelet/crush/internal/tui/components/dialogs/quit"
 	"github.com/charmbracelet/crush/internal/tui/styles"
 	"github.com/charmbracelet/crush/internal/tui/util"
+	"mvdan.cc/sh/v3/syntax"
 )
 
 type Editor interface {
@@ -112,7 +113,41 @@ 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())
+
+	// Parse the EDITOR parts to separate the executable from its arguments.
+	// This properly handles EDITORs with args like `zed --wait`.
+	parsed, err := syntax.NewParser().Parse(strings.NewReader(editor), "")
+	if err != nil {
+		return util.ReportError(fmt.Errorf("failed to parse editor command: %w", err))
+	}
+
+	var cmdName string
+	var cmdArgs []string
+	if len(parsed.Stmts) > 0 && parsed.Stmts[0].Cmd != nil {
+		if callExpr, ok := parsed.Stmts[0].Cmd.(*syntax.CallExpr); ok && len(callExpr.Args) > 0 {
+			for i, arg := range callExpr.Args {
+				var argStr string
+				for _, part := range arg.Parts {
+					if lit, ok := part.(*syntax.Lit); ok {
+						argStr += lit.Value
+					}
+				}
+				if i == 0 {
+					cmdName = argStr
+				} else {
+					cmdArgs = append(cmdArgs, argStr)
+				}
+			}
+		}
+	}
+
+	// Fallback if parsing borked
+	if cmdName == "" {
+		cmdName = editor
+	}
+
+	cmdArgs = append(cmdArgs, tmpfile.Name())
+	c := exec.CommandContext(context.TODO(), cmdName, cmdArgs...)
 	c.Stdin = os.Stdin
 	c.Stdout = os.Stdout
 	c.Stderr = os.Stderr