From cf3c3b18471605f6596bbc71aa1f90872e4f71ea Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Mon, 13 Jul 2026 19:58:25 +0200 Subject: [PATCH 1/2] Kill the partially created buffer when terminal creation quits ghostel--create cleaned up after errors but not after C-g: quit is not a subtype of error, so a keyboard quit during initialization (e.g. at a dir-locals prompt in ghostel-mode) left behind an empty ghostel-mode buffer with no terminal and no process. --- lisp/ghostel.el | 5 +++-- test/ghostel-project-test.el | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lisp/ghostel.el b/lisp/ghostel.el index 723af301..51cd965b 100644 --- a/lisp/ghostel.el +++ b/lisp/ghostel.el @@ -5197,7 +5197,8 @@ spawn after initialization." DISPLAY-ACTION, when non-nil, is passed to `pop-to-buffer' before terminal creation so size detection observes the window that will display the terminal. Optional ROWS and COLS are passed through to -`ghostel--init-buffer'." +`ghostel--init-buffer'. If initialization is interrupted by an error +or quit, the partially created buffer is killed before re-signaling." (let ((buffer (generate-new-buffer name))) (condition-case err (progn @@ -5209,7 +5210,7 @@ display the terminal. Optional ROWS and COLS are passed through to (pop-to-buffer buffer display-action)) (ghostel--init-buffer buffer rows cols) buffer) - (error + ((error quit) (when (buffer-live-p buffer) (kill-buffer buffer)) (signal (car err) (cdr err)))))) diff --git a/test/ghostel-project-test.el b/test/ghostel-project-test.el index 3a891605..30b66449 100644 --- a/test/ghostel-project-test.el +++ b/test/ghostel-project-test.el @@ -295,6 +295,23 @@ same name must get separate buffers." (when (buffer-live-p buf) (kill-buffer buf))))) +(ert-deftest ghostel-test-create-kills-buffer-on-quit () + "`ghostel--create' kills the partially created buffer on quit. +A keyboard quit can arrive during initialization, e.g. at a dir-locals +prompt in `ghostel-mode'. Uses `condition-case' directly since +`should-error' only traps `error', not `quit'." + (cl-letf (((symbol-function 'ghostel--init-buffer) + (lambda (&rest _) (signal 'quit nil)))) + (unwind-protect + (progn + (should (eq 'quit + (condition-case err + (progn (ghostel--create " *ghostel-test-quit*") nil) + (quit (car err))))) + (should-not (get-buffer " *ghostel-test-quit*"))) + (when-let* ((leftover (get-buffer " *ghostel-test-quit*"))) + (kill-buffer leftover))))) + (ert-deftest ghostel-test-returns-buffer () "`ghostel' returns the (live) Ghostel buffer." (let ((created (generate-new-buffer "*ghostel-return-test*")) From 6c8a93510b011364615bbed276142443bee548b0 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Mon, 13 Jul 2026 22:09:48 +0200 Subject: [PATCH 2/2] Use a non-hex marker in the fish backspace test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test typed "abc" and required it to vanish from the screen after a backspace. fish's default prompt shows the checkout's VCS state, which on the detached HEAD CI tests is the short merge-commit sha — PR merge shas containing "abc" (like babcf18c) kept the marker permanently on screen and timed the test out. Type "wxyz" instead: letters outside [0-9a-f] can never appear in a sha. --- test/ghostel-shell-test.el | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/test/ghostel-shell-test.el b/test/ghostel-shell-test.el index 1a28acb6..1ea54a76 100644 --- a/test/ghostel-shell-test.el +++ b/test/ghostel-shell-test.el @@ -119,8 +119,8 @@ newest-first list aligns with the buffer-order regions." ;; Send a real command before testing line editing. The initial ;; output may be a terminal query or startup redraw; waiting for ;; command output proves fish's reader is accepting input. Private - ;; mode keeps history/autosuggestions from repainting `abc' after - ;; it has been backspaced to `ab'. + ;; mode keeps history/autosuggestions from repainting `wxyz' after + ;; it has been backspaced to `wxy'. (ghostel--write-pty ghostel--term "printf '%s%s\\n' GHOSTEL_FISH _READY\n") (ghostel-test--wait-until @@ -129,25 +129,30 @@ newest-first list aligns with the buffer-order regions." (ghostel-test--rendered-terminal-text))) proc 10) - ;; Type "abc" then backspace. - (ghostel--write-pty ghostel--term "abc") + ;; Type "wxyz" then backspace. The typed text must + ;; contain letters outside [0-9a-f]: fish's default + ;; prompt shows the checkout's VCS state, and on a + ;; detached HEAD (as in CI) that is the short commit + ;; sha, so a hex-only marker can appear verbatim in + ;; the prompt and never leave the screen. + (ghostel--write-pty ghostel--term "wxyz") (ghostel-test--wait-until (lambda () - (string-match-p "abc" (ghostel-test--rendered-terminal-text))) + (string-match-p "wxyz" (ghostel-test--rendered-terminal-text))) proc 5) - (should (string-match-p "abc" (ghostel-test--rendered-terminal-text))) + (should (string-match-p "wxyz" (ghostel-test--rendered-terminal-text))) ;; Send backspace (\x7f) and verify it works. (ghostel--write-pty ghostel--term "\x7f") (ghostel-test--wait-until (lambda () (let ((state (ghostel-test--rendered-terminal-text))) - (and (string-match-p "ab" state) - (not (string-match-p "abc" state))))) + (and (string-match-p "wxy" state) + (not (string-match-p "wxyz" state))))) proc 5) (let ((state (ghostel-test--rendered-terminal-text))) - (should (string-match-p "ab" state)) - (should-not (string-match-p "abc" state))))))) + (should (string-match-p "wxy" state)) + (should-not (string-match-p "wxyz" state))))))) (ert-deftest ghostel-test-fish-auto-inject-loads-integration () "Fish auto-inject shim chains to ghostel.fish and cleans XDG_DATA_DIRS."