Clarify relationship between save-excursion and current buffer.
Suggested by Uday S Reddy. * positions.texi (Excursions): Explain the "save-excursion defeated by set-buffer" warning. * buffers.texi (Current Buffer): Copyedits. Don't recommend using save-excursion.
This commit is contained in:
parent
4e19a977b0
commit
c1bcd0d5d1
3 changed files with 152 additions and 132 deletions
|
@ -1,3 +1,11 @@
|
|||
2011-03-19 Chong Yidong <cyd@stupidchicken.com>
|
||||
|
||||
* positions.texi (Excursions): Explain the "save-excursion
|
||||
defeated by set-buffer" warning.
|
||||
|
||||
* buffers.texi (Current Buffer): Copyedits. Don't recommend using
|
||||
save-excursion. Suggested by Uday S Reddy.
|
||||
|
||||
2011-03-16 Stefan Monnier <monnier@iro.umontreal.ca>
|
||||
|
||||
* strings.texi (String Conversion): Don't mention
|
||||
|
|
|
@ -85,91 +85,17 @@ This function returns @code{t} if @var{object} is a buffer,
|
|||
@cindex changing to another buffer
|
||||
@cindex current buffer
|
||||
|
||||
There are, in general, many buffers in an Emacs session. At any time,
|
||||
one of them is designated as the @dfn{current buffer}. This is the
|
||||
buffer in which most editing takes place, because most of the primitives
|
||||
for examining or changing text in a buffer operate implicitly on the
|
||||
current buffer (@pxref{Text}). Normally the buffer that is displayed on
|
||||
the screen in the selected window is the current buffer, but this is not
|
||||
always so: a Lisp program can temporarily designate any buffer as
|
||||
current in order to operate on its contents, without changing what is
|
||||
displayed on the screen.
|
||||
There are, in general, many buffers in an Emacs session. At any
|
||||
time, one of them is designated the @dfn{current buffer}---the buffer
|
||||
in which most editing takes place. Most of the primitives for
|
||||
examining or changing text operate implicitly on the current buffer
|
||||
(@pxref{Text}).
|
||||
|
||||
The way to designate a current buffer in a Lisp program is by calling
|
||||
@code{set-buffer}. The specified buffer remains current until a new one
|
||||
is designated.
|
||||
|
||||
When an editing command returns to the editor command loop, the
|
||||
command loop designates the buffer displayed in the selected window as
|
||||
current, to prevent confusion: the buffer that the cursor is in when
|
||||
Emacs reads a command is the buffer that the command will apply to.
|
||||
(@xref{Command Loop}.) Therefore, @code{set-buffer} is not the way to
|
||||
switch visibly to a different buffer so that the user can edit it. For
|
||||
that, you must use the functions described in @ref{Displaying Buffers}.
|
||||
|
||||
@strong{Warning:} Lisp functions that change to a different current buffer
|
||||
should not depend on the command loop to set it back afterwards.
|
||||
Editing commands written in Emacs Lisp can be called from other programs
|
||||
as well as from the command loop; it is convenient for the caller if
|
||||
the subroutine does not change which buffer is current (unless, of
|
||||
course, that is the subroutine's purpose). Therefore, you should
|
||||
normally use @code{set-buffer} within a @code{save-current-buffer} or
|
||||
@code{save-excursion} (@pxref{Excursions}) form that will restore the
|
||||
current buffer when your function is done. Here, as an example, is a
|
||||
simplified version of the command @code{append-to-buffer}:
|
||||
|
||||
@example
|
||||
@group
|
||||
(defun append-to-buffer (buffer start end)
|
||||
"Append to specified buffer the text of the region."
|
||||
(interactive "BAppend to buffer: \nr")
|
||||
(let ((oldbuf (current-buffer)))
|
||||
(save-current-buffer
|
||||
(set-buffer (get-buffer-create buffer))
|
||||
(insert-buffer-substring oldbuf start end))))
|
||||
@end group
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
This function binds a local variable to record the current buffer, and
|
||||
then @code{save-current-buffer} arranges to make it current again.
|
||||
Next, @code{set-buffer} makes the specified buffer current. Finally,
|
||||
@code{insert-buffer-substring} copies the string from the original
|
||||
current buffer to the specified (and now current) buffer.
|
||||
|
||||
If the buffer appended to happens to be displayed in some window,
|
||||
the next redisplay will show how its text has changed. Otherwise, you
|
||||
will not see the change immediately on the screen. The buffer becomes
|
||||
current temporarily during the execution of the command, but this does
|
||||
not cause it to be displayed.
|
||||
|
||||
If you make local bindings (with @code{let} or function arguments) for
|
||||
a variable that may also have buffer-local bindings, make sure that the
|
||||
same buffer is current at the beginning and at the end of the local
|
||||
binding's scope. Otherwise you might bind it in one buffer and unbind
|
||||
it in another! There are two ways to do this. In simple cases, you may
|
||||
see that nothing ever changes the current buffer within the scope of the
|
||||
binding. Otherwise, use @code{save-current-buffer} or
|
||||
@code{save-excursion} to make sure that the buffer current at the
|
||||
beginning is current again whenever the variable is unbound.
|
||||
|
||||
Do not rely on using @code{set-buffer} to change the current buffer
|
||||
back, because that won't do the job if a quit happens while the wrong
|
||||
buffer is current. For instance, in the previous example, it would
|
||||
have been wrong to do this:
|
||||
|
||||
@example
|
||||
@group
|
||||
(let ((oldbuf (current-buffer)))
|
||||
(set-buffer (get-buffer-create buffer))
|
||||
(insert-buffer-substring oldbuf start end)
|
||||
(set-buffer oldbuf))
|
||||
@end group
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
Using @code{save-current-buffer}, as we did, handles quitting, errors,
|
||||
and @code{throw}, as well as ordinary evaluation.
|
||||
Normally, the buffer displayed in the selected window is the current
|
||||
buffer, but this is not always so: a Lisp program can temporarily
|
||||
designate any buffer as current in order to operate on its contents,
|
||||
without changing what is displayed on the screen. The most basic
|
||||
function for designating a current buffer is @code{set-buffer}.
|
||||
|
||||
@defun current-buffer
|
||||
This function returns the current buffer.
|
||||
|
@ -192,6 +118,89 @@ cannot necessarily see the buffer. But Lisp programs will now operate
|
|||
on it.
|
||||
@end defun
|
||||
|
||||
When an editing command returns to the editor command loop, Emacs
|
||||
automatically calls @code{set-buffer} on the buffer shown in the
|
||||
selected window. This is to prevent confusion: it ensures that the
|
||||
buffer that the cursor is in, when Emacs reads a command, is the
|
||||
buffer to which that command applies (@pxref{Command Loop}). Thus,
|
||||
you should not use @code{set-buffer} to switch visibly to a different
|
||||
buffer; for that, use the functions described in @ref{Displaying
|
||||
Buffers}.
|
||||
|
||||
When writing a Lisp function, do @emph{not} rely on this behavior of
|
||||
the command loop to restore the current buffer after an operation.
|
||||
Editing commands can also be called as Lisp functions by other
|
||||
programs, not just from the command loop; it is convenient for the
|
||||
caller if the subroutine does not change which buffer is current
|
||||
(unless, of course, that is the subroutine's purpose).
|
||||
|
||||
To operate temporarily on another buffer, put the @code{set-buffer}
|
||||
within a @code{save-current-buffer} form. Here, as an example, is a
|
||||
simplified version of the command @code{append-to-buffer}:
|
||||
|
||||
@example
|
||||
@group
|
||||
(defun append-to-buffer (buffer start end)
|
||||
"Append the text of the region to BUFFER."
|
||||
(interactive "BAppend to buffer: \nr")
|
||||
(let ((oldbuf (current-buffer)))
|
||||
(save-current-buffer
|
||||
(set-buffer (get-buffer-create buffer))
|
||||
(insert-buffer-substring oldbuf start end))))
|
||||
@end group
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
Here, we bind a local variable to record the current buffer, and then
|
||||
@code{save-current-buffer} arranges to make it current again later.
|
||||
Next, @code{set-buffer} makes the specified buffer current, and
|
||||
@code{insert-buffer-substring} copies the string from the original
|
||||
buffer to the specified (and now current) buffer.
|
||||
|
||||
Alternatively, we can use the @code{with-current-buffer} macro:
|
||||
|
||||
@example
|
||||
@group
|
||||
(defun append-to-buffer (buffer start end)
|
||||
"Append the text of the region to BUFFER."
|
||||
(interactive "BAppend to buffer: \nr")
|
||||
(let ((oldbuf (current-buffer)))
|
||||
(with-current-buffer (get-buffer-create buffer)
|
||||
(insert-buffer-substring oldbuf start end))))
|
||||
@end group
|
||||
@end example
|
||||
|
||||
In either case, if the buffer appended to happens to be displayed in
|
||||
some window, the next redisplay will show how its text has changed.
|
||||
If it is not displayed in any window, you will not see the change
|
||||
immediately on the screen. The command causes the buffer to become
|
||||
current temporarily, but does not cause it to be displayed.
|
||||
|
||||
If you make local bindings (with @code{let} or function arguments)
|
||||
for a variable that may also have buffer-local bindings, make sure
|
||||
that the same buffer is current at the beginning and at the end of the
|
||||
local binding's scope. Otherwise you might bind it in one buffer and
|
||||
unbind it in another!
|
||||
|
||||
Do not rely on using @code{set-buffer} to change the current buffer
|
||||
back, because that won't do the job if a quit happens while the wrong
|
||||
buffer is current. For instance, in the previous example, it would
|
||||
have been wrong to do this:
|
||||
|
||||
@example
|
||||
@group
|
||||
(let ((oldbuf (current-buffer)))
|
||||
(set-buffer (get-buffer-create buffer))
|
||||
(insert-buffer-substring oldbuf start end)
|
||||
(set-buffer oldbuf))
|
||||
@end group
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
Using @code{save-current-buffer} or @code{with-current-buffer}, as we
|
||||
did, correctly handles quitting, errors, and @code{throw}, as well as
|
||||
ordinary evaluation.
|
||||
|
||||
@defspec save-current-buffer body@dots{}
|
||||
The @code{save-current-buffer} special form saves the identity of the
|
||||
current buffer, evaluates the @var{body} forms, and finally restores
|
||||
|
|
|
@ -798,69 +798,72 @@ is zero or less.
|
|||
@cindex excursion
|
||||
|
||||
It is often useful to move point ``temporarily'' within a localized
|
||||
portion of the program, or to switch buffers temporarily. This is
|
||||
called an @dfn{excursion}, and it is done with the @code{save-excursion}
|
||||
special form. This construct initially remembers the identity of the
|
||||
current buffer, and its values of point and the mark, and restores them
|
||||
after the completion of the excursion.
|
||||
portion of the program. This is called an @dfn{excursion}, and it is
|
||||
done with the @code{save-excursion} special form. This construct
|
||||
remembers the initial identity of the current buffer, and its values
|
||||
of point and the mark, and restores them after the excursion
|
||||
completes. It is the standard way to move point within one part of a
|
||||
program and avoid affecting the rest of the program, and is used
|
||||
thousands of times in the Lisp sources of Emacs.
|
||||
|
||||
The forms for saving and restoring the configuration of windows are
|
||||
described elsewhere (see @ref{Window Configurations}, and @pxref{Frame
|
||||
Configurations}). When only the identity of the current buffer needs
|
||||
to be saved and restored, it is preferable to use
|
||||
@code{save-current-buffer} instead.
|
||||
If you only need to save and restore the identity of the current
|
||||
buffer, use @code{save-current-buffer} or @code{with-current-buffer}
|
||||
instead (@pxref{Current Buffer}). If you need to save or restore
|
||||
window configurations, see the forms described in @ref{Window
|
||||
Configurations} and in @ref{Frame Configurations}.
|
||||
|
||||
@defspec save-excursion body@dots{}
|
||||
@cindex mark excursion
|
||||
@cindex point excursion
|
||||
The @code{save-excursion} special form saves the identity of the current
|
||||
buffer and the values of point and the mark in it, evaluates
|
||||
@var{body}, and finally restores the buffer and its saved values of
|
||||
point and the mark. All three saved values are restored even in case of
|
||||
an abnormal exit via @code{throw} or error (@pxref{Nonlocal Exits}).
|
||||
This special form saves the identity of the current buffer and the
|
||||
values of point and the mark in it, evaluates @var{body}, and finally
|
||||
restores the buffer and its saved values of point and the mark. All
|
||||
three saved values are restored even in case of an abnormal exit via
|
||||
@code{throw} or error (@pxref{Nonlocal Exits}).
|
||||
|
||||
The @code{save-excursion} special form is the standard way to move
|
||||
point within one part of a program and avoid affecting the rest of the
|
||||
program. It is used more than 4000 times in the Lisp sources
|
||||
of Emacs.
|
||||
The value returned by @code{save-excursion} is the result of the last
|
||||
form in @var{body}, or @code{nil} if no body forms were given.
|
||||
@end defspec
|
||||
|
||||
@code{save-excursion} does not save the values of point and the mark for
|
||||
other buffers, so changes in other buffers remain in effect after
|
||||
@code{save-excursion} exits.
|
||||
Because @code{save-excursion} only saves point and mark for the
|
||||
buffer that was current at the start of the excursion, any changes
|
||||
made to point and/or mark in other buffers, during the excursion, will
|
||||
remain in effect afterward. This frequently leads to unintended
|
||||
consequences, so the byte compiler warns if you call @code{set-buffer}
|
||||
during an excursion:
|
||||
|
||||
@example
|
||||
Warning: @code{save-excursion} defeated by @code{set-buffer}
|
||||
@end example
|
||||
|
||||
@noindent
|
||||
To avoid such problems, you should call @code{save-excursion} only
|
||||
after setting the desired current buffer, as in the following example:
|
||||
|
||||
@example
|
||||
@group
|
||||
(defun append-string-to-buffer (string buffer)
|
||||
"Append STRING to the end of BUFFER."
|
||||
(with-current-buffer buffer
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(insert string))))
|
||||
@end group
|
||||
@end example
|
||||
|
||||
@cindex window excursions
|
||||
Likewise, @code{save-excursion} does not restore window-buffer
|
||||
Likewise, @code{save-excursion} does not restore window-buffer
|
||||
correspondences altered by functions such as @code{switch-to-buffer}.
|
||||
One way to restore these correspondences, and the selected window, is to
|
||||
use @code{save-window-excursion} inside @code{save-excursion}
|
||||
(@pxref{Window Configurations}).
|
||||
|
||||
The value returned by @code{save-excursion} is the result of the last
|
||||
form in @var{body}, or @code{nil} if no body forms were given.
|
||||
|
||||
@example
|
||||
@group
|
||||
(save-excursion @var{forms})
|
||||
@equiv{}
|
||||
(let ((old-buf (current-buffer))
|
||||
(old-pnt (point-marker))
|
||||
@end group
|
||||
(old-mark (copy-marker (mark-marker))))
|
||||
(unwind-protect
|
||||
(progn @var{forms})
|
||||
(set-buffer old-buf)
|
||||
@group
|
||||
(goto-char old-pnt)
|
||||
(set-marker (mark-marker) old-mark)))
|
||||
@end group
|
||||
@end example
|
||||
@end defspec
|
||||
|
||||
@strong{Warning:} Ordinary insertion of text adjacent to the saved
|
||||
point value relocates the saved value, just as it relocates all markers.
|
||||
More precisely, the saved value is a marker with insertion type
|
||||
@code{nil}. @xref{Marker Insertion Types}. Therefore, when the saved
|
||||
point value is restored, it normally comes before the inserted text.
|
||||
point value relocates the saved value, just as it relocates all
|
||||
markers. More precisely, the saved value is a marker with insertion
|
||||
type @code{nil}. @xref{Marker Insertion Types}. Therefore, when the
|
||||
saved point value is restored, it normally comes before the inserted
|
||||
text.
|
||||
|
||||
Although @code{save-excursion} saves the location of the mark, it does
|
||||
not prevent functions which modify the buffer from setting
|
||||
|
|
Loading…
Add table
Reference in a new issue