Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lisp/ghostel.el
Original file line number Diff line number Diff line change
Expand Up @@ -5015,6 +5015,14 @@ for both native and Emacs PTY paths."

;;; Major mode

(defun ghostel--change-major-mode-guard ()
"Signal a `user-error' while the buffer's terminal process is live.
A mode change runs `kill-all-local-variables', wiping the buffer-locals
the native module and PTY depend on; once the process is dead the mode
may change freely (`ghostel-compile' finalize relies on this)."
(when (process-live-p ghostel--process)
(user-error "Cannot change major mode in a live ghostel buffer")))

(define-derived-mode ghostel-mode fundamental-mode "Ghostel"
"Major mode for Ghostel terminal emulator."
(hack-dir-local-variables)
Expand Down Expand Up @@ -5071,6 +5079,7 @@ for both native and Emacs PTY paths."
(add-hook 'activate-mark-hook #'ghostel--mark-activated nil t)
(add-hook 'isearch-mode-end-hook #'ghostel-maybe-leave-input nil t)
(add-hook 'kill-buffer-query-functions #'ghostel--kill-buffer-query nil t)
(add-hook 'change-major-mode-hook #'ghostel--change-major-mode-guard nil t)
;; Show the hyperlink URI at point in eldoc.
(add-hook 'eldoc-documentation-functions #'ghostel--eldoc-link nil t)

Expand Down
34 changes: 34 additions & 0 deletions test/ghostel-modes-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,40 @@
(should (member 'ghostel-default
(cdr (assq 'default face-remapping-alist))))))

(ert-deftest ghostel-test-major-mode-change-blocked-while-live ()
"Changing the major mode signals `user-error' while the terminal runs.
The switch is aborted before `kill-all-local-variables' wipes the
buffer-locals the native module and PTY depend on."
(let ((buf (generate-new-buffer " *ghostel-test-mode-guard*"))
(proc nil))
(unwind-protect
(with-current-buffer buf
(ghostel-mode)
(setq proc (make-process :name "ghostel-test-mode-guard"
:command '("sleep" "30")
:buffer nil :noquery t))
(setq-local ghostel--process proc)
(should-error (fundamental-mode) :type 'user-error)
(should (eq major-mode 'ghostel-mode))
;; Buffer-locals survived — the hook fired before any teardown.
(should (eq ghostel--process proc)))
(when (process-live-p proc)
(delete-process proc))
(kill-buffer buf))))

(ert-deftest ghostel-test-major-mode-change-allowed-after-exit ()
"With no live process the major mode may change freely.
`ghostel-compile' relies on this: it tears down the terminal, then
switches the buffer into its view mode."
(let ((buf (generate-new-buffer " *ghostel-test-mode-guard-dead*")))
(unwind-protect
(with-current-buffer buf
(ghostel-mode)
(should (null ghostel--process))
(fundamental-mode)
(should (eq major-mode 'fundamental-mode)))
(kill-buffer buf))))

(ert-deftest ghostel-test-mode-shields-default-text-properties ()
"A global `default-text-properties' does not reach ghostel buffers.
Its `line-spacing'/`line-height' fallback would inflate rendered rows
Expand Down
Loading