Delay updating the focus until Emacs is idle

This improves responsiveness, especially during scrolling.
This commit is contained in:
Florian Rommel 2024-05-19 23:27:41 +02:00
parent fe50fbeb2b
commit 74e0a30f08

View File

@ -63,6 +63,15 @@ In order for changes to take effect, reenable `focus-mode'."
:type 'number :type 'number
:group 'focus) :group 'focus)
(defcustom focus-update-idle-delay nil
"Delay (in seconds) before updating the focus after each command.
The default value of nil results in an immediate update.
Increase this value if you experience performance issues."
:type '(choice (const :tag "Immediate update" nil)
(const :tag "Delayed update (0.1s)" 0.1)
(number :tag "Custom delay"))
:group 'focus)
(defface focus-unfocused (defface focus-unfocused
'((t :inherit shadow)) '((t :inherit shadow))
"The face that overlays the unfocused area." "The face that overlays the unfocused area."
@ -98,6 +107,9 @@ In order for changes to take effect, reenable `focus-mode'."
The timer calls `focus-read-only-hide-cursor' after The timer calls `focus-read-only-hide-cursor' after
`focus-read-only-blink-seconds' seconds.") `focus-read-only-blink-seconds' seconds.")
(defvar-local focus-update-timer nil
"Timer started from `focus-update'")
(defun focus-get-thing () (defun focus-get-thing ()
"Return the current thing, based on `focus-mode-to-thing'. "Return the current thing, based on `focus-mode-to-thing'.
@ -120,16 +132,29 @@ This also sets `focus-current-thing-cache' to the current thing."
(cons beg end))) (cons beg end)))
(t (bounds-of-thing-at-point thing))))) (t (bounds-of-thing-at-point thing)))))
(defun focus-move-focus () (defun focus-move-focus (buffer)
"Move the focused section according to `focus-bounds'. "Move the focused section according to `focus-bounds'.
If `focus-mode' is enabled, this command fires after each If `focus-mode' is enabled, this command fires after each
command." command."
(with-current-buffer focus-buffer (with-current-buffer buffer
(setq focus-update-timer nil)
(let* ((bounds (focus-bounds))) (let* ((bounds (focus-bounds)))
(when bounds (when bounds
(focus-move-overlays (car bounds) (cdr bounds)))))) (focus-move-overlays (car bounds) (cdr bounds))))))
(defun focus-update ()
"Trigger an update of the focus.
When `focus-update-idle-delay' is non-nil, start update after the
specified idle delay."
(if focus-update-idle-delay
(unless focus-update-timer
(setq focus-update-timer
(run-with-idle-timer focus-update-idle-delay nil
#'focus-move-focus focus-buffer)))
(focus-move-focus focus-buffer)))
(defun focus-move-overlays (low high) (defun focus-move-overlays (low high)
"Move the overlays to highlight the region between LOW and HIGH." "Move the overlays to highlight the region between LOW and HIGH."
(move-overlay focus-pre-overlay (point-min) low) (move-overlay focus-pre-overlay (point-min) low)
@ -141,7 +166,7 @@ command."
It sets the `focus-pre-overlay', `focus-min-overlay', and It sets the `focus-pre-overlay', `focus-min-overlay', and
`focus-post-overlay' to overlays; these are invisible until `focus-post-overlay' to overlays; these are invisible until
`focus-move-focus' is run. It adds `focus-move-focus' to `focus-update' is run. It adds `focus-update' to
`post-command-hook'." `post-command-hook'."
(unless (or focus-pre-overlay focus-post-overlay) (unless (or focus-pre-overlay focus-post-overlay)
(setq focus-pre-overlay (make-overlay (point-min) (point-min)) (setq focus-pre-overlay (make-overlay (point-min) (point-min))
@ -151,8 +176,9 @@ It sets the `focus-pre-overlay', `focus-min-overlay', and
(overlay-put focus-mid-overlay 'face 'focus-focused) (overlay-put focus-mid-overlay 'face 'focus-focused)
(mapc (lambda (o) (overlay-put o 'face 'focus-unfocused)) (mapc (lambda (o) (overlay-put o 'face 'focus-unfocused))
(list focus-pre-overlay focus-post-overlay)) (list focus-pre-overlay focus-post-overlay))
(add-hook 'post-command-hook 'focus-move-focus nil t) (setq focus-current-thing-cache nil
(setq focus-current-thing-cache nil) focus-update-timer nil)
(add-hook 'post-command-hook 'focus-update nil t)
(add-hook 'change-major-mode-hook 'focus-terminate nil t))) (add-hook 'change-major-mode-hook 'focus-terminate nil t)))
(defun focus-terminate () (defun focus-terminate ()
@ -160,12 +186,15 @@ It sets the `focus-pre-overlay', `focus-min-overlay', and
The overlays pointed to by `focus-pre-overlay', The overlays pointed to by `focus-pre-overlay',
`focus-mid-overlay' and `focus-post-overlay' are deleted, and `focus-mid-overlay' and `focus-post-overlay' are deleted, and
`focus-move-focus' is removed from `post-command-hook'." `focus-update' is removed from `post-command-hook'."
(when (and focus-pre-overlay focus-post-overlay) (when (and focus-pre-overlay focus-post-overlay)
(mapc 'delete-overlay (mapc 'delete-overlay
(list focus-pre-overlay focus-mid-overlay focus-post-overlay)) (list focus-pre-overlay focus-mid-overlay focus-post-overlay))
(remove-hook 'post-command-hook 'focus-move-focus t) (remove-hook 'post-command-hook 'focus-update t)
(when focus-update-timer
(cancel-timer focus-update-timer))
(setq focus-current-thing-cache nil (setq focus-current-thing-cache nil
focus-update-timer nil
focus-pre-overlay nil focus-pre-overlay nil
focus-mid-overlay nil focus-mid-overlay nil
focus-post-overlay nil))) focus-post-overlay nil)))
@ -194,13 +223,16 @@ default is overwritten. This function simply helps set the
(when (bound-and-true-p focus-mode) (when (bound-and-true-p focus-mode)
(when (region-active-p) (when (region-active-p)
(focus-move-overlays (region-beginning) (region-end))) (focus-move-overlays (region-beginning) (region-end)))
(remove-hook 'post-command-hook 'focus-move-focus t))) (when focus-update-timer
(cancel-timer focus-update-timer))
(setq focus-update-timer nil)
(remove-hook 'post-command-hook 'focus-update t)))
(defun focus-unpin () (defun focus-unpin ()
"Unpin the focused section." "Unpin the focused section."
(interactive) (interactive)
(when (bound-and-true-p focus-mode) (when (bound-and-true-p focus-mode)
(add-hook 'post-command-hook 'focus-move-focus nil t))) (add-hook 'post-command-hook 'focus-update nil t)))
(defun focus-next-thing (&optional n) (defun focus-next-thing (&optional n)
"Move the point to the middle of the Nth next thing." "Move the point to the middle of the Nth next thing."