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
16 changes: 13 additions & 3 deletions evil-ghostel.el
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
15 changes: 15 additions & 0 deletions test/evil-ghostel-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -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
;; -----------------------------------------------------------------------
Expand Down
Loading