Re: Position buffer contents at bottom of window

Joost Kremers <[email protected]> Wed, 24 Jun 2026 15:26:14 +0200
Newsgroups gmane.emacs.help
Message-ID <[email protected]>
On Wed, Jun 24 2026, Stéphane Marks wrote:
> On Wed, Jun 24, 2026 at 6:53 AM Joost Kremers <[email protected]>
> wrote:
>
>> Hi list,
>>
>> I've been trying to create a function that fills a buffer and then
>> positions the last line of the buffer at the bottom of the window.
>>
>> Doing this manually works just fine:
>>
>> ```
>> (defun my--recenter-window-to-bottom (&optional window)
>>   "Align contents of BUFFER to the bottom of WINDOW."
>>   (interactive (list (selected-window)))
>>   (when (window-live-p window)
>>     (with-current-buffer (window-buffer window)
>>       (when-let* ((_ (>= (window-end window t) (point-max)))
>>                   (edges (window-body-pixel-edges window))
>>                   (window-top (nth 1 edges))
>>                   (window-bottom (nth 3 edges))
>>                   (text-height (cdr (window-text-pixel-size window
>> (window-start window))))
>>                   (dy (- window-bottom window-top text-height)))
>>         (message "↑%d ↓%d ↕%d dy%d" window-top window-bottom text-height
>> dy)
>>         (pixel-scroll-precision-scroll-up dy)))))
>> ```
>>
>> I can bind this to a key and then calling this function does exactly what I
>> want it to do.
>>
>> The problem is that it does not seem to work from Lisp, at least not in the
>> context I'm using it. Specifically, I have a function in the buffer-local
>> value of `window-buffer-change-functions` that basically erases the buffer
>> and fills it with content. At the end, it calls `(goto-char (point-max))`
>> and then sets a timer for 0 seconds to run the function above.
>>
>> I'm using the timer because the buffer-fill functions is called from
>> `window-buffer-change-functions` which, AFAIU, is called during redisplay,
>> so I was hoping the timer would ensure that redisplay is done before
>> `my--recenter-window-to-bottom` is called.
>>
>> Things, however, just don't work... The numbers reported in the `message`
>> call are different than when I call the function interactively, and the
>> window is scrolled incorrectly, or not at all.
>>
>> I'm probably doing something very wrong here, but I'm unsure what. If
>> anyone can cast an eye and point me in the right direction, I'd be
>> grateful.
>
>
> Have you ensured that the window is the correct one?  You didn't share the
> timer code, so it is possible that you could be passing in the correct
> window at that point rather than the function being triggered by the timer
> when the selected window isn't the one you want.

This is the gist of the function that fills the buffer:

```
(defun my-update-buffer (&optional window)
  (setq window (or window (selected-window)))
  (when (window-live-p window)
    ;; This function is called from `window-buffer-change-functions', which
    ;; may run with the old buffer current, so use WINDOW to get the right
    ;; buffer.
    (with-current-buffer (window-buffer window)
      [Fill buffer with content]
      (set-window-point window (point-max)))
    ;; `window-buffer-change-functions' is run during redisplay, so defer
    ;; viewport adjustment until the window state has settled.
    (run-at-time 0 nil #'chat--recenter-window-to-bottom window)))
```

So the timer is the last call in this function. `window` is the window
argument passed from `window-buffer-change-functions`. (Note, BTW, that
this is with Emacs 30. I know in Emacs 31 `window-buffer-change-functions`
is treated a bit differently.)

-- 
Joost Kremers
Life has its moments