8

From the save-excursion docstring:

Save point, mark, and current buffer; execute BODY; restore those things.

My understanding was that the restored buffer would again be visible following the evaluation of the body, but that appears not to be the case. A basic function demonstrating the behavior that I find surprising:

(defun test-excursion ()
  (interactive)
  (save-excursion
    (find-file "just-a-test"))
  (insert "blah"))

On my system (using emacs -q; running Debian version 24.5.1) interactively running this function using M-x test-excursion I get most of the behavior I expect. A new empty file buffer is created (there is no such file on disk) named "just-a-test" and "blah" is inserted in the scratch buffer that I happened to have open at the time. However; the buffer that remains visible is "just-a-test" and not "*scratch*" which was (current-buffer) at the time the command was invoked. No additional windows were created. It is only apparent by switching back to "*scratch*" that the string was inserted as expected.

My memory may be inaccurate on this, but I recall using save-excursion when writing a function and observing the alternate behavior (the restored buffer restored as visible). Is there any chance that this behavior has changed or that there has been a regression in 24.5.1? I know that the definition has changed in 25. I checked on another system (also 24.5.1) and observed the same behavior as my system. Evaluation non-interactively (via eval-last-sexp) yields the same. switch-buffer and save-current-buffer also yield the same.

My understanding is that each window (in the emacs sense) has a current buffer (edit: perhaps appropriately referred to simply as the "displayed buffer" and is returned by current-buffer when the window has focus) which necessarily is visible and there are no other conceptions of a "visible buffer". If save-excursion restores the current buffer then shouldn't it be visible?

I've been scratching my head at this one for a while. I hope someone can help me out.

ebpa
  • 7,319
  • 26
  • 53

2 Answers2

8

The current buffer need not be visible in a window.

You're looking for save-window-excursion (or quite possibly a combination of the two).

n.b. C-uC-ha ^save- will point out all of the following:

save-current-buffer
  Function: Record which buffer is current; execute BODY; make that
            buffer current.

save-excursion
  Function: Save point, mark, and current buffer; execute BODY;
            restore those things.

save-match-data
  Macro: Execute the BODY forms, restoring the global value of the
         match data.

save-restriction
  Function: Execute BODY, saving and restoring current buffer's
            restrictions.

save-selected-window
  Macro: Execute BODY, then select the previously selected window.

save-window-excursion
  Macro: Execute BODY, then restore previous window configuration.
phils
  • 48,657
  • 3
  • 76
  • 115
  • Do all windows necessarily lack focus when current-buffer is not visible? I don't ever recall observing emacs in such a state, but it would be a rare condition in the first place. – ebpa Jun 22 '16 at 23:22
  • 2
    The documentation for `set-buffer` suggests that the command loop restores the current buffer to the selected window's buffer at the end of each command, in which case that wouldn't be a state that Emacs could get into. – phils Jun 23 '16 at 00:40
  • Confirmed by `(elisp) Current Buffer` which states "When an editing command returns to the editor command loop, Emacs automatically calls ‘set-buffer’ on the buffer shown in the selected window." – phils Jun 23 '16 at 01:26
4

Facing a similar problem, I realize this behavior is due to find-file in particular. I didn't try using save-window-excursion instead of save-excursion. What I used instead was

(with-current-buffer (find-file-noselect "just-a-test")
 (insert "blah"))

That way, just-a-test is loaded or switched to if it already visited in the background. It remains open. As far as I can see, for this simple example the two methods (this and the one by phils) is a matter of taste.