branch: elpa/scroll-on-jump commit 00522ba63218b7f8ea3be7da97c7be05a108f3ce Author: Stefan Monnier <monn...@iro.umontreal.ca> Commit: Campbell Barton <ideasma...@gmail.com>
Fix excessive use of macros The advice adding/removing macros were defined incorrectly (the code was rather too macro-happy, with definitions which suffered from various common pitfalls). Change several of the macros to functions. Most of those changes have no externally-visible effect, except for the changes to the ones that add/remove advice, where callers will need to adjust their code. * readme.rst: Adjust examples to new advice behavior. * scroll-on-jump.el: Add `debug` spec to macros. * (scroll-on-jump--animated-scroll-by-line) (scroll-on-jump--animated-scroll-by-px): Remove let-binding of obsolete var `inhibit-point-motion-hooks`. * (scroll-on-jump--impl): Make it a function, to avoid problems with name capture and things like that. * (scroll-on-jump, scroll-on-jump-with-scroll): Adjust definition accordingly. * (scroll-on-jump-interactive, scroll-on-jump-with-scroll-interactive): Make them functions, making use of `lexical-binding`. --- readme.rst | 4 +- scroll-on-jump.el | 177 ++++++++++++++++++++++++++---------------------------- 2 files changed, 87 insertions(+), 94 deletions(-) diff --git a/readme.rst b/readme.rst index 398e0eab00..18c5890a42 100644 --- a/readme.rst +++ b/readme.rst @@ -81,13 +81,13 @@ Before: .. code-block:: elisp - (global-set-key (kbd "<C-z>") 'undo) + (global-set-key (kbd "<C-z>") #'undo) After: .. code-block:: elisp - (global-set-key (kbd "<C-z>") (scroll-on-jump-interactive 'undo)) + (global-set-key (kbd "<C-z>") (scroll-on-jump-interactive #'undo)) Advice Example diff --git a/scroll-on-jump.el b/scroll-on-jump.el index a6164dac45..cd9bf83348 100644 --- a/scroll-on-jump.el +++ b/scroll-on-jump.el @@ -173,7 +173,7 @@ Argument ALSO-MOVE-POINT When non-nil, move the POINT as well." (defmacro scroll-on-jump--save-mark-conditionally (test-condition &rest body) "Run BODY, restoring the marks original location when TEST-CONDITION is non-nil." ;; NOTE: it's assumed the buffer will not be modified. - (declare (indent 1)) + (declare (indent 1) (debug t)) (let ((mk-pos (gensym "mk-pos"))) `(let ((,mk-pos (and ,test-condition @@ -181,7 +181,7 @@ Argument ALSO-MOVE-POINT When non-nil, move the POINT as well." (and mk (marker-position mk)))))) (prog1 (progn ,@body) - ;; Unlikely but possible the marker no longer exists. + ;; Unlikely but possible the mark no longer exists. (when ,mk-pos (let ((mk (mark-marker))) (when mk @@ -269,7 +269,6 @@ Moving the point when ALSO-MOVE-POINT is set." (cond ((not (zerop lines-scroll)) (let ((is-early-exit t) - (inhibit-point-motion-hooks t) (lines-done-abs 0) (lines-scroll-abs (abs lines-scroll)) (also-move-mark (and also-move-point (region-active-p)))) @@ -346,7 +345,6 @@ Argument ALSO-MOVE-POINT moves the point while scrolling." (cond ((not (zerop lines-scroll)) (let ((is-early-exit t) - (inhibit-point-motion-hooks t) (px-done-abs 0) (px-scroll-abs (abs (* lines-scroll char-height))) (px-scroll (* lines-scroll char-height)) @@ -526,7 +524,7 @@ Moving the point when ALSO-MOVE-POINT is set." ;; would involve error handling (see note below). (defmacro scroll-on-jump--outer-scoped-mark (point-init point-out &rest body) "Set POINT-INIT and store its value in POINT-OUT outside the scope of BODY." - (declare (indent 2)) + (declare (indent 2) (debug (form symbolp body))) `(prog1 (save-excursion (goto-char ,point-init) ,@body) @@ -534,84 +532,81 @@ Moving the point when ALSO-MOVE-POINT is set." (defmacro scroll-on-jump--inner-scoped-mark (point-init point-out &rest body) "Set POINT-INIT and store its value in POINT-OUT in the scope of BODY." - (declare (indent 2)) + (declare (indent 2) (debug (form symbolp body))) `(save-excursion (prog1 (progn (goto-char ,point-init) ,@body) (setq ,point-out (point))))) -(defmacro scroll-on-jump--impl (use-window-start &rest body) - "Main macro that wraps BODY in logic that reacts to change in `point'. +(defun scroll-on-jump--impl (use-window-start body-fun) + "Main macro that wraps BODY-FUN in logic that reacts to change in `point'. Argument USE-WINDOW-START detects window scrolling when non-nil." - (declare (indent 1)) - `(let ((buf (current-buffer)) ; Set in case we have an error. - (window (selected-window)) - - (point-prev nil) - (point-next nil) - - (window-start-prev nil) - (window-start-next nil)) - - (prog1 (let ((point-orig (point)) - ;; Postpone point-motion-hooks until later. - (inhibit-point-motion-hooks t)) - ;; Note, we could catch and re-raise errors, - ;; this has the advantage that we could get the resulting cursor location - ;; even in the case of an error. - ;; However - this makes troubleshooting problems considerably more difficult. - ;; As it wont show the full back-trace, only the error message. - ;; So don't prioritize correct jumping in the case of errors and assume errors - ;; are not something that happen after cursor motion. - (scroll-on-jump--outer-scoped-mark point-orig point-prev - (scroll-on-jump--inner-scoped-mark (window-start window) window-start-prev - (scroll-on-jump--inner-scoped-mark point-orig point-next - ;; Run the main body of this macro. - ;; It's important the result if returned (hence the `prog1' use). - ,@body)))) - - ;; Quiet unused argument warning. - (ignore point-prev) - - (cond - ;; Context changed or recursed, simply jump. - ((not - ;; Check if the buffer/context changed. - (and (eq buf (window-buffer window)) - (eq buf (current-buffer)) - (eq window (selected-window)))) - - (goto-char point-next)) - - (t ; Perform animated scroll. - - ;; It's possible the requested `point-next' exceeds the maximum point. - ;; This causes an error counting lines and calculating offsets, - ;; so clamp it here to avoid complications later. - (setq point-next (min point-next (point-max))) - - (cond - (,use-window-start - (setq window-start-next (window-start window)) - (unless (eq window-start-prev window-start-next) - (set-window-start window window-start-prev) - (let ((lines-scroll - (1- (count-screen-lines window-start-prev window-start-next t window))) - (dir - (cond - ((< window-start-prev window-start-next) - 1) - (t - -1))) - (also-move-point (not (eq (point) point-next)))) - (scroll-on-jump--scroll-impl window (* dir lines-scroll) dir also-move-point))) - (prog1 (goto-char point-next) - (redisplay t))) - (t - (scroll-on-jump-auto-center window point-prev point-next))) - - (scroll-on-jump--evil-visual-mode-workaround)))))) + (let ((buf (current-buffer)) ; Set in case we have an error. + (window (selected-window)) + + (point-prev nil) + (point-next nil) + + (window-start-prev nil) + (window-start-next nil)) + + (prog1 (let ((point-orig (point))) + ;; Note, we could catch and re-raise errors, + ;; this has the advantage that we could get the resulting cursor location + ;; even in the case of an error. + ;; However - this makes troubleshooting problems considerably more difficult. + ;; As it wont show the full back-trace, only the error message. + ;; So don't prioritize correct jumping in the case of errors and assume errors + ;; are not something that happen after cursor motion. + (scroll-on-jump--outer-scoped-mark point-orig point-prev + (scroll-on-jump--inner-scoped-mark (window-start window) window-start-prev + (scroll-on-jump--inner-scoped-mark point-orig point-next + ;; Run the main body of this function. + ;; It's important the result if returned (hence the `prog1' use). + (funcall body-fun))))) + + ;; Quiet unused argument warning. + (ignore point-prev) + + (cond + ;; Context changed or recursed, simply jump. + ((not + ;; Check if the buffer/context changed. + (and (eq buf (window-buffer window)) + (eq buf (current-buffer)) + (eq window (selected-window)))) + + (goto-char point-next)) + + (t ; Perform animated scroll. + + ;; It's possible the requested `point-next' exceeds the maximum point. + ;; This causes an error counting lines and calculating offsets, + ;; so clamp it here to avoid complications later. + (setq point-next (min point-next (point-max))) + + (cond + (use-window-start + (setq window-start-next (window-start window)) + (unless (eq window-start-prev window-start-next) + (set-window-start window window-start-prev) + (let ((lines-scroll + (1- (count-screen-lines window-start-prev window-start-next t window))) + (dir + (cond + ((< window-start-prev window-start-next) + 1) + (t + -1))) + (also-move-point (not (eq (point) point-next)))) + (scroll-on-jump--scroll-impl window (* dir lines-scroll) dir also-move-point))) + (prog1 (goto-char point-next) + (redisplay t))) + (t + (scroll-on-jump-auto-center window point-prev point-next))) + + (scroll-on-jump--evil-visual-mode-workaround)))))) ;; --------------------------------------------------------------------------- @@ -625,20 +620,19 @@ Argument USE-WINDOW-START detects window scrolling when non-nil." ;;;###autoload (defmacro scroll-on-jump (&rest body) "Main macro that wraps BODY in logic that reacts to change in `point'." - (declare (indent 0)) - `(scroll-on-jump--impl nil - ,@body)) + (declare (indent 0) (debug t)) + `(scroll-on-jump--impl nil (lambda () ,@body))) ;;;###autoload -(defmacro scroll-on-jump-interactive (fn) +(defun scroll-on-jump-interactive (fn) "Macro that wraps interactive call to function FN. Use if you want to use `scroll-on-jump' for a single `key-binding', without changing behavior anywhere else." - `(lambda () - (interactive) - (scroll-on-jump - (call-interactively ,fn)))) + (lambda () + (interactive) + (scroll-on-jump + (call-interactively fn)))) ;; Helper function (not public). (defun scroll-on-jump-advice--wrapper (old-fn &rest args) @@ -667,20 +661,19 @@ This calls (calling OLD-FN with ARGS)." ;;;###autoload (defmacro scroll-on-jump-with-scroll (&rest body) "Main macro wrapping BODY, handling change `point' and vertical scroll." - (declare (indent 0)) - `(scroll-on-jump--impl t - ,@body)) + (declare (indent 0) (debug t)) + `(scroll-on-jump--impl t (lambda () ,@body))) ;;;###autoload -(defmacro scroll-on-jump-with-scroll-interactive (fn) +(defun scroll-on-jump-with-scroll-interactive (fn) "Macro that wraps interactive call to function FN. Use if you want to use `scroll-on-jump-with-scroll' for a single `key-binding', without changing behavior anywhere else." - `(lambda () - (interactive) - (scroll-on-jump-with-scroll - (call-interactively ,fn)))) + (lambda () + (interactive) + (scroll-on-jump-with-scroll + (call-interactively fn)))) ;; Helper function (not public). (defun scroll-on-jump-advice--with-scroll-wrapper (old-fn &rest args)