6

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.

Example


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)")

Example

lawlist
  • 13,099
  • 3
  • 49
  • 158

3 Answers3

2

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-start and window-end BEFORE 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`.") ))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
lawlist
  • 13,099
  • 3
  • 49
  • 158
1

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).

Drew
  • 29,895
  • 7
  • 74
  • 104
  • Thanks for taking a look at this thread. I set up some messages to test what was happening and I tried using the actual function `(window-end)` and `(window-start)` to see what was happening. I believe that the new `(window-start)` and the new `(window-end)` cannot be obtained because a redisplay has not yet occurred at the new buffer position. When using `(window-start)` and `(window-end)` within the `post-command-hook`, the new buffer layout is not taken into consideration. A redisplay, however, is something I would like to avoid. – lawlist May 29 '14 at 02:27
  • I see. Dunno if there is an easy way to figure out what `window-end` and `window-start` will return after redisplay. Sounds like you would end up reproducing a bunch of redisplay or window-calculation code. Maybe someone else has a suggestion. Or maybe you can think of an alternate approach altogether. In any case, I think this is the cause of the error you encountered. – Drew May 29 '14 at 02:31
  • Hmmm . . . just thinking out loud ... The *new* point is achieved successfully without a redisplay. Inasmuch as the *new* point is determinable, perhaps there is a way to approximate the *new* target area based on window-height, window-width, . . .? The problem with that approach is that there is no way of knowing how many characters might comprise the new *visible window* layout (without a redisplay, that is). I sure wish there were a `redisplay-internal` to take a sneak preview of what would happen when redisplay actually occurs. – lawlist May 29 '14 at 02:56
1

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.

Stefan
  • 27,908
  • 4
  • 53
  • 82
  • Thank you -- I'll read-up on `jit-lock-register` and experiment with it to see if I can figure out how `beg` and `end` are being determined, and then once I understand that, I'll see how that might be applied to my custom overlay function. This will be a work in progress . . . – lawlist May 29 '14 at 04:41