Is it possible to calculate a new window-start/end without a redisplay occurring? If so, then an example would be greatly appreciated. If not, then what is the best way to approximate it?
Example: We want to move to a new area of the buffer somewhere off screen, and place overlays when we get there. We might be using page-down or scroll-down or paragraph-down or end-of-buffer. When we get to that new point, we want to calculate the new window-start and the new window-end. However, we want to avoid a momentary naked looking buffer without any overlays. Ideally, the redisplay would occur once those overlays are added. I want to restrict new overlays to the new region based upon the new window-start/end.
Point-Min: point = 1
Old Window Start: point = 1000
Old Window End: point = 1500
New Window Start: point = 3500
New Window End: point = 4000
Point-Max: point = 6000
Problem: When using the post-command-hook to try and calculate the new window-start and new window-end, the previous display positions are being used instead -- i.e., the old window-start and the old window-end.
Here is a sample of the project I am working on. Absent fixing the window-start \ window-end problem, I get the following error:
Error in post-command-hook (my-eol-ruler-function): (error "Invalid search bound (wrong side of point)")`. The error happens when going from (point-min) to the end of the buffer with the interactive function end-of-buffer. In the context of this error, (point-max) is beyond the old window-end.
EDIT: Updated code to include a message: (message "point: %s | window-start: %s | window-end: %s | point-max: %s" (point) (window-start) (window-end) (point-max) ). The message is used to demonstrate that the new window-start and new window-end are not calculated within the post-command-hook because a redisplay has not yet occurred. However, I am trying to avoid a redisplay until after the new overlays have been placed -- otherwise, a naked buffer without overlays is visible for a split second.
(defvar my-eol-ruler nil "A horizontal ruler stretching from eol (end of line) to the window edge.") (make-variable-buffer-local 'my-eol-ruler) (defvar my-eol-pilcrow nil "A pilcrow symbol placed at the end of every line except the current line.") (make-variable-buffer-local 'my-eol-pilcrow) (defun my-eol-ruler-function () (let* ( (opoint (point)) (window-width (window-width)) (window-start (window-start)) (window-end (window-end)) (col-eovl (save-excursion (vertical-motion 1) (skip-chars-backward " \r\n" (- (point) 1)) (- (current-column) (progn (vertical-motion 0) (current-column))))) (my-current-line-length (- (- window-width col-eovl) 3)) (pilcrow (propertize (char-to-string ?\u00B6) 'face '(:foreground "white") 'cursor t)) (pilcrow-underlined (propertize (char-to-string ?\u00B6) 'face '(:foreground "white" :underline "yellow") 'cursor t)) (underline (propertize (char-to-string ?\u2009) 'display `(space :width ,my-current-line-length) 'face '(:underline "yellow") 'cursor t))) (when (or my-eol-ruler my-eol-pilcrow) (dolist (description `( ,my-eol-ruler ,my-eol-pilcrow )) (remove-overlays (point-min) (point-max) 'after-string description)) ) (setq my-eol-ruler (concat pilcrow-underlined underline)) (setq my-eol-pilcrow pilcrow) (save-excursion (end-of-line) (overlay-put (make-overlay (point) (point)) 'after-string my-eol-ruler ) ) (message "point: %s | window-start: %s | window-end: %s | point-max: %s" (point) (window-start) (window-end) (point-max) ) (save-excursion (goto-char window-end) (while (re-search-backward "\n" window-start t) (let* ( (pbol (point-at-bol)) (pbovl (save-excursion (vertical-motion 0) (point))) (peol (point)) (peol-pbol-region-p (if (region-active-p) (= peol pbol))) (eol-inside-region-p (if (region-active-p) (and (<= reg-beg peol) (> reg-end peol)))) (col-eovl (save-excursion (vertical-motion 1) (skip-chars-backward " \r\n" (- (point) 1)) (- (current-column) (progn (vertical-motion 0) (current-column))))) (my-last-column (current-column)) (window-width-bug-p (= my-last-column (- window-width 1))) (shazbot-pbol (save-excursion (end-of-line) (re-search-backward "\s\\|\t" pbol t) (+ (point) 1))) (wrapped-window-width-bug-p (= col-eovl (- window-width 1))) ) (when (or (< opoint pbol) (> opoint peol)) (overlay-put (make-overlay peol peol) 'after-string my-eol-pilcrow))))) )) (add-hook 'post-command-hook 'my-eol-ruler-function) Beginning of the buffer, before the error occurs.
End of the buffer -- the error occurs when executing the interactive function end-of-buffer from a point at the beginning of the buffer.
Error in post-command-hook (my-eol-ruler-function): (error "Invalid search bound (wrong side of point)")
See also Emacs bug tracker feature request #22404 (which has not yet been implemented, but the mailing archive contains a rough draft rudimentary patch that creates a new hook for this specific issue): https://debbugs.gnu.org/cgi/bugreport.cgi?bug=22404
- Minor-mode for testing
window-startandwindow-endBEFORE visual redisplay.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; test-mode ;; A minor-mode for testing `window-start` / `window-end` BEFORE visual redisplay. (defvar test-this-command nil "This local variable is set within the `post-command-hook`; and, is also used by the `window-scroll-functions` hook.") (make-variable-buffer-local 'test-this-command) (defun test-post-command-hook-fn () "A function attached to the `post-command-hook`." (setq test-this-command this-command) (test-demo-fn)) (defun test-window-scroll-functions-fn (win _start) "A function attached to the `window-scroll-functions` hook." (test-demo-fn)) (defun test-demo-fn () "This is a test-mode demonstration function." (when (and test-mode test-this-command (window-live-p (get-buffer-window (current-buffer))) (not (minibufferp)) (pos-visible-in-window-p (point) (get-buffer-window (current-buffer) (selected-frame)) t)) (let* ( (selected-window (selected-window)) (window-start (window-start selected-window)) (window-end (window-end selected-window t)) ) (message "window-start: %s | window-end: %s" window-start window-end) (setq test-this-command nil) ))) (define-minor-mode test-mode "A minor-mode for testing `window-start` / `window-end` BEFORE visual redisplay." :init-value nil :lighter " TEST" :keymap nil :global nil :group nil (cond (test-mode (set (make-local-variable 'scroll-conservatively) 101) (add-hook 'post-command-hook 'test-post-command-hook-fn nil t) (add-hook 'window-scroll-functions 'test-window-scroll-functions-fn nil t) (when (called-interactively-p 'any) (message "Turned ON `test-mode`."))) (t (kill-local-variable 'scroll-conservatively) (kill-local-variable 'test-this-command) (remove-hook 'post-command-hook 'test-post-command-hook-fn t) (remove-hook 'window-scroll-functions 'test-window-scroll-functions-fn t) (when (called-interactively-p 'any) (message "Turned OFF `test-mode`.") )))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Offhand, I'd say that the error is raised because you pass a BOUND arg to a search function. For example:
(re-search-backward "\n" window-start t) (re-search-backward "\s\\|\t" pbol t) Check your values of window-start and pbol. Remember that when you search backward the bound must not be greater than the current position (point).
I think you want to use jit-lock-register instead of post-command-hook. This way, the redisplay code will call you back once it has decided of a window-start and you'll be able to add the overlays you want before the buffer's content is displayed.
来源:https://stackoverflow.com/questions/23923371/emacs-calculating-new-window-start-end-without-redisplay