Fix issue 10198: edit-in-kitty exits with editor's exit code

- Python (kitty/launch.py): Track editor PID via monitor_pid to capture
  exit code when editor window closes. Send exit code as data in the DONE
  message instead of sending no data.
- Go (tools/cmd/edit_in_kitty/main.go): Parse exit code from DONE message
  data and use lp.Quit(exit_code) to exit with the editor's exit code.
- Go (tools/cmd/tool/confirm_and_run_shebang.go): Check exit code when
  running edit-in-kitty as a subprocess; abort execution on editor failure.
This commit is contained in:
copilot-swe-agent[bot]
2026-06-28 03:10:58 +00:00
committed by GitHub
parent cd88eb3915
commit aee532c586
3 changed files with 47 additions and 18 deletions

View File

@@ -29,7 +29,7 @@ func encode(x string) string {
type OnDataCallback = func(data_type string, data []byte) error
func edit_loop(data_to_send string, kill_if_signaled bool, on_data OnDataCallback) (err error) {
func edit_loop(data_to_send string, kill_if_signaled bool, on_data OnDataCallback) (exit_code int, err error) {
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
if err != nil {
return
@@ -52,7 +52,14 @@ func edit_loop(data_to_send string, kill_if_signaled bool, on_data OnDataCallbac
if line == "KITTY_DATA_END" {
lp.QueueWriteString(update_type + "\r\n")
if update_type == "DONE" {
lp.Quit(0)
if data.Len() > 0 {
if b, err2 := base64.StdEncoding.DecodeString(data.String()); err2 == nil {
if n, err3 := strconv.Atoi(strings.TrimSpace(string(b))); err3 == nil {
exit_code = n
}
}
}
lp.Quit(exit_code)
return nil
}
b, err := base64.StdEncoding.DecodeString(data.String())
@@ -150,7 +157,7 @@ func edit_loop(data_to_send string, kill_if_signaled bool, on_data OnDataCallbac
return
}
if canceled {
return tui.Canceled
return 1, tui.Canceled
}
ds := lp.DeathSignalName()
@@ -160,29 +167,29 @@ func edit_loop(data_to_send string, kill_if_signaled bool, on_data OnDataCallbac
lp.KillIfSignalled()
return
}
return &tui.KilledBySignal{Msg: fmt.Sprint("Killed by signal: ", ds), SignalName: ds}
return 1, &tui.KilledBySignal{Msg: fmt.Sprint("Killed by signal: ", ds), SignalName: ds}
}
return
}
func edit_in_kitty(path string, opts *Options) (err error) {
func edit_in_kitty(path string, opts *Options) (exit_code int, err error) {
read_file, err := os.Open(path)
if err != nil {
return fmt.Errorf("Failed to open %s for reading with error: %w", path, err)
return 1, fmt.Errorf("Failed to open %s for reading with error: %w", path, err)
}
defer read_file.Close()
var s unix.Stat_t
err = unix.Fstat(int(read_file.Fd()), &s)
if err != nil {
return fmt.Errorf("Failed to stat %s with error: %w", path, err)
return 1, fmt.Errorf("Failed to stat %s with error: %w", path, err)
}
if s.Size > int64(opts.MaxFileSize)*1024*1024 {
return fmt.Errorf("File size %s is too large for performant editing", humanize.Bytes(uint64(s.Size)))
return 1, fmt.Errorf("File size %s is too large for performant editing", humanize.Bytes(uint64(s.Size)))
}
file_data, err := io.ReadAll(read_file)
if err != nil {
return fmt.Errorf("Failed to read from %s with error: %w", path, err)
return 1, fmt.Errorf("Failed to read from %s with error: %w", path, err)
}
read_file.Close()
data := strings.Builder{}
@@ -199,11 +206,11 @@ func edit_in_kitty(path string, opts *Options) (err error) {
add_encoded := func(key, val string) { add(key, encode(val)) }
if unix.Access(path, unix.R_OK|unix.W_OK) != nil {
return fmt.Errorf("%s is not readable and writeable", path)
return 1, fmt.Errorf("%s is not readable and writeable", path)
}
cwd, err := os.Getwd()
if err != nil {
return fmt.Errorf("Failed to get the current working directory with error: %w", err)
return 1, fmt.Errorf("Failed to get the current working directory with error: %w", err)
}
add_encoded("cwd", cwd)
for _, arg := range os.Args[2:] {
@@ -219,12 +226,12 @@ func edit_in_kitty(path string, opts *Options) (err error) {
}
return
}
err = edit_loop(data.String(), true, write_data)
exit_code, err = edit_loop(data.String(), true, write_data)
if err != nil {
if err == tui.Canceled {
return err
return 1, err
}
return fmt.Errorf("Failed to receive edited file back from terminal with error: %w", err)
return 1, fmt.Errorf("Failed to receive edited file back from terminal with error: %w", err)
}
return
}
@@ -265,8 +272,7 @@ func EntryPoint(parent *cli.Command) *cli.Command {
if err != nil {
return 1, err
}
err = edit_in_kitty(file_path, &opts)
return 0, err
return edit_in_kitty(file_path, &opts)
},
})
AddCloneSafeOpts(sc)

View File

@@ -4,6 +4,7 @@ package tool
import (
"bufio"
"errors"
"fmt"
"os"
"os/exec"
@@ -120,7 +121,13 @@ func confirm_and_run_shebang(args []string, confirm_policy ConfirmPolicy) (rc in
editor.Stdin = os.Stdin
editor.Stdout = os.Stdout
editor.Stderr = os.Stderr
editor.Run()
if err = editor.Run(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
return exitErr.ExitCode(), nil
}
return 1, err
}
return confirm_and_run_shebang(args, ConfirmIfNeeded)
case "y":
}