fix(editor): Ctrl-Z aborts editor and returns to profanity
Some checks failed
CI Code / Check spelling (pull_request) Successful in 16s
CI Code / Check coding style (pull_request) Failing after 34s
CI Code / Code Coverage (pull_request) Successful in 3m1s
CI Code / Linux (debian) (pull_request) Successful in 4m43s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m55s
CI Code / Linux (arch) (pull_request) Successful in 6m55s
Some checks failed
CI Code / Check spelling (pull_request) Successful in 16s
CI Code / Check coding style (pull_request) Failing after 34s
CI Code / Code Coverage (pull_request) Successful in 3m1s
CI Code / Linux (debian) (pull_request) Successful in 4m43s
CI Code / Linux (ubuntu) (pull_request) Successful in 4m55s
CI Code / Linux (arch) (pull_request) Successful in 6m55s
Replaces the SIGTSTP=SIG_IGN inheritance approach (was commit 8) with a detect-and-abort design that matches user expectation: Ctrl-Z inside the editor brings you back to profanity instead of leaving the session suspended. - src/tools/editor.c: SIGTSTP reset back to SIG_DFL in the child so vim's :stop / Ctrl-Z actually stop the editor (revert previous SIG_IGN inheritance). - src/tools/editor.c: new editor_check_stopped() — non-reaping waitpid poll for WIFSTOPPED. Lets prof_run detect Ctrl-Z / :stop / gdb-attach on the editor child. - src/tools/editor.c: editor_emergency_kill() now sends SIGCONT before SIGTERM so the abort works on a STOPPED child too. - src/tools/editor.c: _editor_exit_cb handles WIFSIGNALED cleanly with "Editor session cancelled." instead of printing garbage via WEXITSTATUS on a signaled child. - src/profanity.c: SIGTSTP / SIGCONT signal handlers set flags consumed by the main loop. Editor STOPPED -> editor_emergency_kill (returns to the chat with the pre-alt+c readline buffer intact). Plain Ctrl-Z with no editor -> mutt-style drop the process group to the shell. SIGCONT -> refresh curses with clearok + ui_resize. While editor is active, swallow suspend_requested (race: vim's kill(0,SIGTSTP) beats waitpid seeing STOPPED — next poll catches it). - src/tools/editor.h: declare editor_check_stopped and editor_is_active. Verbose comment blocks across the branch tightened to one to two lines.
This commit is contained in:
@@ -32,19 +32,16 @@ typedef struct EditorContext
|
||||
void* user_data;
|
||||
} EditorContext;
|
||||
|
||||
// Set TRUE for the lifetime of an editor session (from launch_editor up to
|
||||
// _editor_exit_cb / fork-error cleanup). The async editor model lost the old
|
||||
// editor_task.active check from the pthread implementation; without this
|
||||
// guard a second launch_editor (e.g. via a plugin dispatched through
|
||||
// g_main_context_iteration) would call ui_suspend again and fork a second
|
||||
// editor that fights the first one for the terminal, then prematurely
|
||||
// ui_resume when either exits.
|
||||
// Re-entrancy guard (lost in the async-editor rewrite). Prevents a plugin
|
||||
// from spawning a second editor that would fight the first for the terminal.
|
||||
static gboolean editor_active = FALSE;
|
||||
static pid_t editor_pid = 0;
|
||||
|
||||
static void
|
||||
_editor_exit_cb(GPid pid, gint status, gpointer data)
|
||||
{
|
||||
editor_active = FALSE;
|
||||
editor_pid = 0;
|
||||
|
||||
EditorContext* ctx = data;
|
||||
gchar* contents = NULL;
|
||||
@@ -63,8 +60,10 @@ _editor_exit_cb(GPid pid, gint status, gpointer data)
|
||||
cons_show_error("Could not read edited content: %s", error->message);
|
||||
g_error_free(error);
|
||||
}
|
||||
} else {
|
||||
cons_show_error("Editor exited with error status %d", WEXITSTATUS(status));
|
||||
} else if (WIFEXITED(status)) {
|
||||
cons_show_error("Editor exited with status %d", WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
cons_show("Editor session cancelled.");
|
||||
}
|
||||
|
||||
if (remove(ctx->filename) != 0) {
|
||||
@@ -157,7 +156,8 @@ launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* dat
|
||||
} else if (pid == 0) {
|
||||
// Child process: Inherits TTY from parent
|
||||
|
||||
// Reset signal handlers that profanity sets so the editor doesn't inherit them
|
||||
// SIGTSTP=SIG_DFL lets vim's :stop / Ctrl-Z work; profanity catches
|
||||
// the STOPPED state via editor_check_stopped() and drops to the shell.
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGTSTP, SIG_DFL);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
@@ -169,7 +169,39 @@ launch_editor(gchar* initial_content, void (*callback)(gchar* content, void* dat
|
||||
}
|
||||
|
||||
// Parent process: Watch the child asynchronously
|
||||
editor_pid = pid;
|
||||
g_child_watch_add((GPid)pid, _editor_exit_cb, ctx);
|
||||
g_strfreev(editor_argv);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
editor_emergency_kill(void)
|
||||
{
|
||||
if (!editor_active || editor_pid <= 0) {
|
||||
return;
|
||||
}
|
||||
log_warning("[Editor] aborting editor pid %d", editor_pid);
|
||||
kill(editor_pid, SIGCONT); // wake if STOPPED; no-op otherwise
|
||||
kill(editor_pid, SIGTERM);
|
||||
// _editor_exit_cb will restore the UI when the child reaps.
|
||||
}
|
||||
|
||||
gboolean
|
||||
editor_is_active(void)
|
||||
{
|
||||
return editor_active;
|
||||
}
|
||||
|
||||
gboolean
|
||||
editor_check_stopped(void)
|
||||
{
|
||||
// Poll for STOPPED without reaping (g_child_watch handles exit via its
|
||||
// own WNOHANG). Lets prof_run drop the group to the shell on Ctrl-Z.
|
||||
if (!editor_active || editor_pid <= 0) {
|
||||
return FALSE;
|
||||
}
|
||||
int status;
|
||||
pid_t r = waitpid(editor_pid, &status, WNOHANG | WUNTRACED);
|
||||
return (r == editor_pid && WIFSTOPPED(status));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user