diff --git a/evil-ghostel.el b/evil-ghostel.el index 72ea611..1415658 100644 --- a/evil-ghostel.el +++ b/evil-ghostel.el @@ -80,9 +80,13 @@ placement math the native module performs in `src/render.zig'." "Preserve Emacs point during redraws in evil normal state. ORIG-FN is the advised `ghostel--redraw' called with TERM and FULL. Without this, the ~30fps redraw timer would snap point back to -the terminal cursor, undoing any evil `normal-mode' navigation." +the terminal cursor, undoing any evil `normal-mode' navigation. +`emacs-state' is evil's vanilla-Emacs escape hatch; point should +follow the terminal cursor there just like it does in insert-state, +otherwise the cursor gets stuck wherever it was on state entry while +the TUI keeps redrawing elsewhere." (if (and evil-ghostel-mode - (not (eq evil-state 'insert)) + (not (memq evil-state '(insert emacs))) (not (ghostel--mode-enabled term 1049))) (let ((saved-point (point))) (funcall orig-fn term full) @@ -117,7 +121,7 @@ Set by the `I'/`A' advice which send Home/End directly.") (evil-ghostel--reset-cursor-point))) (defun evil-ghostel--insert-state-entry () - "Sync terminal cursor to Emacs point when entering insert state. + "Sync terminal cursor to Emacs point when entering `emacs-state'. Skipped when `evil-ghostel--sync-inhibit' is set (by I/A advice which already sent Ctrl-a/Ctrl-e). When point is on a different row from the terminal cursor, snap @@ -336,6 +340,10 @@ state transitions." #'evil-ghostel--normal-state-entry nil t) (add-hook 'evil-insert-state-entry-hook #'evil-ghostel--insert-state-entry nil t) + ;; Reuse the insert-state sync when entering emacs-state — both + ;; states expect point to follow the terminal cursor. + (add-hook 'evil-emacs-state-entry-hook + #'evil-ghostel--insert-state-entry nil t) (advice-add 'evil-insert-line :before #'evil-ghostel--before-insert-line) (advice-add 'evil-append-line :before #'evil-ghostel--before-append-line) (advice-add 'evil-delete :around #'evil-ghostel--around-delete) @@ -353,6 +361,8 @@ state transitions." #'evil-ghostel--normal-state-entry t) (remove-hook 'evil-insert-state-entry-hook #'evil-ghostel--insert-state-entry t) + (remove-hook 'evil-emacs-state-entry-hook + #'evil-ghostel--insert-state-entry t) (advice-remove 'evil-insert-line #'evil-ghostel--before-insert-line) (advice-remove 'evil-append-line #'evil-ghostel--before-append-line) (advice-remove 'evil-delete #'evil-ghostel--around-delete) diff --git a/test/evil-ghostel-test.el b/test/evil-ghostel-test.el index 01a85c7..309b94c 100644 --- a/test/evil-ghostel-test.el +++ b/test/evil-ghostel-test.el @@ -233,6 +233,21 @@ point in the scrollback region instead of the visible viewport." (ghostel--redraw term t)) (should (= 11 (current-column))))) +(ert-deftest evil-ghostel-test-redraw-moves-point-emacs-state () + "Test that redraws follow terminal cursor in evil emacs-state. +Emacs-state is evil's vanilla-Emacs escape hatch; point should track +the terminal cursor there just like in insert-state. Otherwise the +cursor freezes wherever it was on state entry while TUIs keep +redrawing elsewhere." + (evil-ghostel-test--with-buffer 5 40 "hello world" + (evil-emacs-state) + ;; Move point away from terminal cursor + (goto-char (point-min)) + ;; Redraw — should snap point to terminal cursor (col 11) + (let ((inhibit-read-only t)) + (ghostel--redraw term t)) + (should (= 11 (current-column))))) + ;; ----------------------------------------------------------------------- ;; Test: advice fires on evil-insert / evil-append ;; -----------------------------------------------------------------------