7

NB: The post How to move point to the end of another buffer, using `with-current-buffer` and `goto-char`? presumably asks the same question as this one, but the answers given to it do not readily apply to my case here. E.g. those answers involve inserting text in the other buffer, etc. The question I'm asking here is considerably simpler, and I expect the answer to it should be correspondingly simple as well.


Suppose that

  • the Emacs frame is split into two windows, where one of the windows holds a buffer called *whatever* and the other one holds the standard *scratch* buffer;
  • the *whatever* buffer contains several lines of text;
  • the cursor in the *whatever* buffer is at the beginning of that buffer (i.e. at its (point-min));
  • the *scratch* buffer is the current one;

If I now evaluate any of the following in the *scratch* buffer, the position of the cursor in the *whatever* buffer remains unchanged:

 (with-current-buffer "*whatever*"
   (goto-char (point-max)))
 (with-current-buffer "*whatever*"
   (end-of-buffer))
 (save-excursion
   (set-buffer "*whatever*")
   (goto-char (point-max)))
 (save-excursion
   (set-buffer "*whatever*")
   (end-of-buffer))

I figure that, whatever (goto-char (point-max)) or (end-of-buffer) does to *whatever*'s cursor's position gets subsequently discarded.

  1. How must I modify any of the snippets above so that the *whatever* buffer's cursor gets positioned (persistently!) at the end of that buffer.

  2. What should I have done to answer this question myself using the Emacs docs? I tried everything I could think of.


After reading Jules Tamagnan's illuminating answer I realized that I need make a further stipulation explicit:

I'm looking for a solution that will work irrespective of whether the buffer in question is visible in a window or not.

After all, if I view buffer B in a window W, move the cursor to a particular location L, then view some other buffer C in the same window W, and finally point W back to buffer B, I expect to see the cursor at the same location L where I left it. IOW, there must be a way for a buffer to remember where its point is, irrespective of whether it is associated with a window or not.

What about the case where the target buffer is currently visible in two or more windows (W1, W2, W3, ...)? There are many reasonable possibilities for this case. The proposed solution could, for example, change the position of the cursor

  • in the most recently active window among W1, W2, W3, ...;
  • in all the windows W1, W2, W3, ...; or
  • etc., etc.

(I don't have a strong opinion on this corner case, since I expect it to be extremely rare in practice.)

  • 1
    The key sections in the manual regarding this are Point and Window Point. – cjm Apr 6 '16 at 21:59
8

This was a pretty cool question, I learned a lot that I didn't know while trying to figure this out. What I learned was that each window has its own value for point. This is important because it means that the point is not associated with the buffer but instead the actual window. As we've seen this makes a big difference.

What we need to do then is set the the value of the point in the window, the function for that is set-window-point (I found this though google, I find that if I don't know where to start looking in the build in emacs-docs looking on google may shed some light). We then need to tell set-window-point which window to select. To do that I used get-buffer-window (once again I used google to find this).

This code will only work if the buffer is open in a window. Another version should be able to be made where you open the buffer in a window, set the point and then go back to the original window configuration. The code below though is much simpler.

(set-window-point (get-buffer-window "*whatever*") (point-max))

I hope this helps, if you have any other questions or if it doesn't work exactly how you want, just let me know.

  • This makes sense; if you open a buffer in 2 windows (eg. via split window), you have 2 independent points. – Juancho Apr 6 '16 at 16:58
  • @Juancho Yeah, deep down I knew thats how it worked but it never really sank in until just now – Jules Apr 6 '16 at 17:10
4

(Jules Tamagnan's answer identifies the problem, I'll expand on it.)

Your solutions with save-excursion or save-excursion do in fact work! You can see it by checking the value of (point) in that buffer afterwards, e.g.

(progn
  (with-current-buffer "*whatever*"
    (goto-char 42))
  (with-current-buffer "*whatever*"
    (point)))

always returns 42 (provided that it's a valid position in the buffer. That's even mentioned as a gotcha in the documentation of save-excursion: it restores only the position in the original buffer, not the position in any other buffer.

If you find that the buffer position isn't the one you set, that's because something else is changing it, and that thing is most likely a window (or a hook somewhere, but most hooks have no business messing with the point — still, it could be a e.g. process output hook). In the documentation about point, there's a note:

Each buffer has its own value of point, which is independent of the value of point in other buffers. Each window also has a value of point, which is independent of the value of point in other windows on the same buffer. This is why point can have different values in various windows that display the same buffer. When a buffer appears in only one window, the buffer’s point and the window’s point normally have the same value, so the distinction is rarely important. See Window Point, for more details.

If the buffer is shown in a window, then the buffer's point is updated to the window's point every time the point changes or the window is redisplayed. As a consequence, setting the point basically only lasts as long as your Lisp code lasts: as soon as the toplevel command loop takes control, the window is redisplayed (even if it isn't visible, as far as I can tell) and the buffer position is overridden. In effect, the user's chosen position trumps the program-chosen position.

If you want to also update the point of any window that displays the buffer, you can call get-buffer-window-list to enumerate the windows:

(with-current-buffer "*whatever*"
  (goto-char …)
  (cl-dolist (window (get-buffer-window-list nil nil t))
    (set-window-point window (point))))

But consider that if the user was visiting the buffer, they might not appreciate having the point changed under their nose.

As far as I know Emacs doesn't track when windows were selected (that's “active” in Emacs terminology), you can only know whether they are currently selected. If you start caring about selected windows, beware that each frame has a selected window, and Emacs doesn't always know when frames are selected ((selected-frame) doesn't completely match the end user experience, especially with frames inside terminal emulators since Emacs has no way to know when the user has the terminal emulator window active).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.