From fe50fbeb2bf924c26737986cfbdb071302a98461 Mon Sep 17 00:00:00 2001 From: Florian Rommel Date: Sun, 19 May 2024 23:24:53 +0200 Subject: [PATCH 1/2] Cache current thing to focus Avoid calculating the thing to focus on every update. --- focus.el | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/focus.el b/focus.el index d834760..bf403b4 100644 --- a/focus.el +++ b/focus.el @@ -52,7 +52,9 @@ many derivatives should be placed by the end of the list. Things that are defined include `symbol', `list', `sexp', `defun', `filename', `url', `email', `word', `sentence', -`whitespace', `line', and `page'." +`whitespace', `line', and `page'. + +In order for changes to take effect, reenable `focus-mode'." :type '(alist :key-type symbol :valye-type symbol) :group 'focus) @@ -76,6 +78,9 @@ Things that are defined include `symbol', `list', `sexp', (defvar-local focus-current-thing nil "Overrides the choice of thing dictated by `focus-mode-to-thing' if set.") +(defvar-local focus-current-thing-cache nil + "Caches the current thing to focus.") + (defvar-local focus-buffer nil "Local reference to the buffer focus functions operate on.") @@ -94,12 +99,16 @@ The timer calls `focus-read-only-hide-cursor' after `focus-read-only-blink-seconds' seconds.") (defun focus-get-thing () - "Return the current thing, based on `focus-mode-to-thing'." + "Return the current thing, based on `focus-mode-to-thing'. + +This also sets `focus-current-thing-cache' to the current thing." (or focus-current-thing - (let* ((modes (mapcar 'car focus-mode-to-thing)) - (mode (or (cl-find major-mode modes) - (apply #'derived-mode-p modes)))) - (if mode (cdr (assoc mode focus-mode-to-thing)) 'sentence)))) + focus-current-thing-cache + (setq focus-current-thing-cache + (let* ((modes (mapcar 'car focus-mode-to-thing)) + (mode (or (cl-find major-mode modes) + (apply #'derived-mode-p modes)))) + (if mode (cdr (assoc mode focus-mode-to-thing)) 'sentence))))) (defun focus-bounds () "Return the current bounds, based on `focus-get-thing'." @@ -109,7 +118,7 @@ The timer calls `focus-read-only-hide-cursor' after (beg (org-element-property :begin elem)) (end (org-element-property :end elem))) (cons beg end))) - (t (bounds-of-thing-at-point (focus-get-thing)))))) + (t (bounds-of-thing-at-point thing))))) (defun focus-move-focus () "Move the focused section according to `focus-bounds'. @@ -143,6 +152,7 @@ It sets the `focus-pre-overlay', `focus-min-overlay', and (mapc (lambda (o) (overlay-put o 'face 'focus-unfocused)) (list focus-pre-overlay focus-post-overlay)) (add-hook 'post-command-hook 'focus-move-focus nil t) + (setq focus-current-thing-cache nil) (add-hook 'change-major-mode-hook 'focus-terminate nil t))) (defun focus-terminate () @@ -155,7 +165,8 @@ The overlays pointed to by `focus-pre-overlay', (mapc 'delete-overlay (list focus-pre-overlay focus-mid-overlay focus-post-overlay)) (remove-hook 'post-command-hook 'focus-move-focus t) - (setq focus-pre-overlay nil + (setq focus-current-thing-cache nil + focus-pre-overlay nil focus-mid-overlay nil focus-post-overlay nil))) From 74e0a30f08b17d91be0df6ef0a974f80f51cff75 Mon Sep 17 00:00:00 2001 From: Florian Rommel Date: Sun, 19 May 2024 23:27:41 +0200 Subject: [PATCH 2/2] Delay updating the focus until Emacs is idle This improves responsiveness, especially during scrolling. --- focus.el | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/focus.el b/focus.el index bf403b4..df330c5 100644 --- a/focus.el +++ b/focus.el @@ -63,6 +63,15 @@ In order for changes to take effect, reenable `focus-mode'." :type 'number :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 '((t :inherit shadow)) "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 `focus-read-only-blink-seconds' seconds.") +(defvar-local focus-update-timer nil + "Timer started from `focus-update'") + (defun focus-get-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))) (t (bounds-of-thing-at-point thing))))) -(defun focus-move-focus () +(defun focus-move-focus (buffer) "Move the focused section according to `focus-bounds'. If `focus-mode' is enabled, this command fires after each command." - (with-current-buffer focus-buffer + (with-current-buffer buffer + (setq focus-update-timer nil) (let* ((bounds (focus-bounds))) (when 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) "Move the overlays to highlight the region between LOW and HIGH." (move-overlay focus-pre-overlay (point-min) low) @@ -141,7 +166,7 @@ command." It sets the `focus-pre-overlay', `focus-min-overlay', and `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'." (unless (or focus-pre-overlay focus-post-overlay) (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) (mapc (lambda (o) (overlay-put o 'face 'focus-unfocused)) (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))) (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', `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) (mapc 'delete-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 + focus-update-timer nil focus-pre-overlay nil focus-mid-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 (region-active-p) (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 () "Unpin the focused section." (interactive) (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) "Move the point to the middle of the Nth next thing."