From a1407cd9c960da7cb7e13b37ec444590319b8908 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 22 Jun 2026 14:25:14 +0300 Subject: Fix description editor ExecProcess flow for nq0 --- internal/ui/table.go | 39 +++++++++++++++++++++++---------------- internal/ui/table_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 16 deletions(-) (limited to 'internal') diff --git a/internal/ui/table.go b/internal/ui/table.go index e629c20..59eaaeb 100644 --- a/internal/ui/table.go +++ b/internal/ui/table.go @@ -237,6 +237,10 @@ type descEditDoneMsg struct { tempFile string } +type descEditLaunchMsg struct { + tempFile string +} + type shellDoneMsg struct { result task.RunResult err error @@ -320,7 +324,7 @@ func editCmd(id int) tea.Cmd { return tea.ExecProcess(c, func(err error) tea.Msg { return editDoneMsg{err: err} }) } -// editDescriptionCmd returns a command that opens the description in external editor +// editDescriptionCmd returns a command that prepares the description temp file. func editDescriptionCmd(description string) tea.Cmd { return func() tea.Msg { tmpPath, err := prepareDescriptionTempFile(description, func() (descriptionTempFile, error) { @@ -330,23 +334,24 @@ func editDescriptionCmd(description string) tea.Cmd { return descEditDoneMsg{err: err, tempFile: ""} } - // Get editor from environment - editor := os.Getenv("EDITOR") - if editor == "" { - editor = "vi" // fallback to vi - } - - // Create the command - c := exec.Command(editor, tmpPath) - c.Stdin = os.Stdin - c.Stdout = os.Stdout - c.Stderr = os.Stderr + return descEditLaunchMsg{tempFile: tmpPath} + } +} - // Use ExecProcess to properly handle the external TUI editor - return tea.ExecProcess(c, func(err error) tea.Msg { - return descEditDoneMsg{err: err, tempFile: tmpPath} - })() +func launchDescriptionEditorCmd(tmpPath string) tea.Cmd { + editor := os.Getenv("EDITOR") + if editor == "" { + editor = "vi" } + + c := exec.Command(editor, tmpPath) + c.Stdin = os.Stdin + c.Stdout = os.Stdout + c.Stderr = os.Stderr + + return tea.ExecProcess(c, func(err error) tea.Msg { + return descEditDoneMsg{err: err, tempFile: tmpPath} + }) } func blinkCmd() tea.Cmd { @@ -608,6 +613,8 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m.handleWindowResize(msg) case editDoneMsg: return m.handleEditDone(msg) + case descEditLaunchMsg: + return m, launchDescriptionEditorCmd(msg.tempFile) case descEditDoneMsg: return m.handleDescEditDone(msg) case shellDoneMsg: diff --git a/internal/ui/table_test.go b/internal/ui/table_test.go index f255fe3..bfd866d 100644 --- a/internal/ui/table_test.go +++ b/internal/ui/table_test.go @@ -203,6 +203,48 @@ func TestHandleDescEditDoneUpdatesDescriptionAndRemovesTempFile(t *testing.T) { } } +func TestEditDescriptionCmdPreparesLaunchWithoutRunningEditor(t *testing.T) { + tmp := t.TempDir() + editorLog := filepath.Join(tmp, "editor.log") + editorPath := filepath.Join(tmp, "editor") + editorScript := "#!/bin/sh\n" + + "printf ran > " + editorLog + "\n" + if err := os.WriteFile(editorPath, []byte(editorScript), 0o755); err != nil { + t.Fatal(err) + } + + origEditor := os.Getenv("EDITOR") + os.Setenv("EDITOR", editorPath) + t.Cleanup(func() { os.Setenv("EDITOR", origEditor) }) + + cmd := editDescriptionCmd("old description") + msg := cmd() + + launchMsg, ok := msg.(descEditLaunchMsg) + if !ok { + t.Fatalf("editDescriptionCmd returned %T, want descEditLaunchMsg", msg) + } + defer func() { _ = os.Remove(launchMsg.tempFile) }() + + if _, err := os.Stat(editorLog); !os.IsNotExist(err) { + t.Fatalf("editor was run during temp-file preparation: %v", err) + } + + content, err := os.ReadFile(launchMsg.tempFile) + if err != nil { + t.Fatalf("read temp description: %v", err) + } + if string(content) != "old description" { + t.Fatalf("temp description = %q, want %q", content, "old description") + } + + m := Model{} + _, launchCmd := (&m).Update(launchMsg) + if launchCmd == nil { + t.Fatalf("descEditLaunchMsg did not return an editor launch command") + } +} + func TestPrepareDescriptionTempFileRemovesTempFileOnWriteError(t *testing.T) { tmp := t.TempDir() tempFile := filepath.Join(tmp, "desc.txt") -- cgit v1.2.3