Skip to content

Commit a9a07f1

Browse files
committed
Suppress hl-line-mode in terminal buffer to prevent prompt flicker
global-hl-line-mode causes visible flicker during terminal redraws because the line highlight overlay is repeatedly created/destroyed. Opt the ghostel buffer out by setting global-hl-line-mode buffer-locally to nil (the officially supported mechanism, see hl-line.el commentary). In copy-mode, enable local hl-line-mode since the display is frozen.
1 parent c78b290 commit a9a07f1

2 files changed

Lines changed: 62 additions & 1 deletion

File tree

ghostel.el

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,6 +1051,10 @@ Set mark, navigate to select, then \\[ghostel-copy-mode-copy] to copy.")
10511051
(defvar-local ghostel--saved-cursor-type nil
10521052
"Saved `cursor-type' before entering copy mode.")
10531053

1054+
(defvar-local ghostel--saved-hl-line-mode nil
1055+
"Non-nil if line highlighting was active when `ghostel-mode' suppressed it.
1056+
Covers both `global-hl-line-mode' and buffer-local `hl-line-mode'.")
1057+
10541058
(defun ghostel-copy-mode ()
10551059
"Enter copy mode for selecting and copying terminal text.
10561060
The display is frozen and standard Emacs navigation keys work.
@@ -1070,6 +1074,8 @@ Press \\`q' or \\[ghostel-copy-mode-exit] to exit without copying."
10701074
;; Switch to copy mode keymap (standard Emacs keys work by default)
10711075
(setq ghostel--saved-local-map (current-local-map))
10721076
(use-local-map ghostel-copy-mode-map)
1077+
(when ghostel--saved-hl-line-mode
1078+
(hl-line-mode 1))
10731079
(setq buffer-read-only t)
10741080
(setq mode-name "Ghostel:Copy")
10751081
(force-mode-line-update)
@@ -1083,6 +1089,8 @@ Press \\`q' or \\[ghostel-copy-mode-exit] to exit without copying."
10831089
(setq cursor-type ghostel--saved-cursor-type)
10841090
(deactivate-mark)
10851091
(use-local-map ghostel--saved-local-map)
1092+
(when ghostel--saved-hl-line-mode
1093+
(hl-line-mode -1))
10861094
(setq buffer-read-only nil)
10871095
(setq mode-name "Ghostel")
10881096
(force-mode-line-update)
@@ -1728,6 +1736,24 @@ PROCESS is the shell, HEIGHT and WIDTH the final dimensions."
17281736
#'ghostel--window-adjust-process-window-size)
17291737
(add-function :after after-focus-change-function #'ghostel--focus-change))
17301738

1739+
(defun ghostel--suppress-hl-line-mode ()
1740+
"Disable hl-line highlighting to prevent redraw flicker.
1741+
Handles both `global-hl-line-mode' (which manages its own overlay via
1742+
`post-command-hook', independent of the buffer-local `hl-line-mode')
1743+
and buffer-local `hl-line-mode'."
1744+
;; global-hl-line-mode: opt this buffer out by setting the variable
1745+
;; buffer-locally to nil (as documented in the hl-line.el commentary).
1746+
(when (bound-and-true-p global-hl-line-mode)
1747+
(setq ghostel--saved-hl-line-mode t)
1748+
(setq-local global-hl-line-mode nil)
1749+
(global-hl-line-unhighlight))
1750+
;; Buffer-local hl-line-mode
1751+
(when (bound-and-true-p hl-line-mode)
1752+
(setq ghostel--saved-hl-line-mode t)
1753+
(hl-line-mode -1)))
1754+
1755+
(add-hook 'ghostel-mode-hook #'ghostel--suppress-hl-line-mode)
1756+
17311757

17321758
;;; Entry point
17331759

test/ghostel-test.el

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,40 @@
867867
(should (null cursor-type)))) ; cursor hidden again
868868
(kill-buffer buf))))
869869

870+
;; -----------------------------------------------------------------------
871+
;; Test: copy-mode hl-line-mode management
872+
;; -----------------------------------------------------------------------
873+
874+
(ert-deftest ghostel-test-copy-mode-hl-line ()
875+
"Test that global-hl-line-mode is suppressed and hl-line restored in copy-mode."
876+
(let ((buf (generate-new-buffer " *ghostel-test-hl-line*")))
877+
(unwind-protect
878+
(with-current-buffer buf
879+
(ghostel-mode)
880+
(require 'hl-line)
881+
;; Simulate global-hl-line-mode being active
882+
(let ((global-hl-line-mode t))
883+
(should global-hl-line-mode)
884+
;; Suppress should opt this buffer out
885+
(ghostel--suppress-hl-line-mode)
886+
(should ghostel--saved-hl-line-mode)
887+
;; Buffer-local global-hl-line-mode must be nil — this is the
888+
;; mechanism that prevents global-hl-line-highlight (on
889+
;; post-command-hook) from creating overlays in this buffer.
890+
(should-not global-hl-line-mode))
891+
;; Enter copy mode — local hl-line-mode should be enabled
892+
(let ((ghostel--copy-mode-active nil)
893+
(ghostel--redraw-timer nil))
894+
(ghostel-copy-mode)
895+
(should (bound-and-true-p hl-line-mode))
896+
;; Exit copy mode — local hl-line-mode disabled again
897+
(ghostel-copy-mode-exit)
898+
(should-not (bound-and-true-p hl-line-mode))))
899+
(when (buffer-live-p buf)
900+
(with-current-buffer buf
901+
(kill-local-variable 'global-hl-line-mode))
902+
(kill-buffer buf)))))
903+
870904
;; -----------------------------------------------------------------------
871905
;; Runner
872906
;; -----------------------------------------------------------------------
@@ -882,7 +916,8 @@
882916
ghostel-test-sync-theme
883917
ghostel-test-osc51-eval
884918
ghostel-test-osc51-eval-unknown
885-
ghostel-test-copy-mode-cursor)
919+
ghostel-test-copy-mode-cursor
920+
ghostel-test-copy-mode-hl-line)
886921
"Tests that require only Elisp (no native module).")
887922

888923
(defun ghostel-test-run-elisp ()

0 commit comments

Comments
 (0)