emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] /srv/bzr/emacs/emacs-23 r100533: Clarify relationship betw


From: Chong Yidong
Subject: [Emacs-diffs] /srv/bzr/emacs/emacs-23 r100533: Clarify relationship between save-excursion and current buffer.
Date: Sat, 19 Mar 2011 16:31:30 -0400
User-agent: Bazaar (2.0.3)

------------------------------------------------------------
revno: 100533
committer: Chong Yidong <address@hidden>
branch nick: emacs-23
timestamp: Sat 2011-03-19 16:31:30 -0400
message:
  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.
modified:
  doc/lispref/ChangeLog
  doc/lispref/buffers.texi
  doc/lispref/positions.texi
=== modified file 'doc/lispref/ChangeLog'
--- a/doc/lispref/ChangeLog     2011-03-16 14:54:21 +0000
+++ b/doc/lispref/ChangeLog     2011-03-19 20:31:30 +0000
@@ -1,3 +1,11 @@
+2011-03-19  Chong Yidong  <address@hidden>
+
+       * 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  <address@hidden>
 
        * strings.texi (String Conversion): Don't mention

=== modified file 'doc/lispref/buffers.texi'
--- a/doc/lispref/buffers.texi  2011-01-02 23:50:46 +0000
+++ b/doc/lispref/buffers.texi  2011-03-19 20:31:30 +0000
@@ -85,43 +85,63 @@
 @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.
-
-  The way to designate a current buffer in a Lisp program is by calling
address@hidden  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
address@hidden (@pxref{Excursions}) form that will restore the
-current buffer when your function is done.  Here, as an example, is a
+  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}).
+
+  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}.
+
address@hidden current-buffer
+This function returns the current buffer.
+
address@hidden
address@hidden
+(current-buffer)
+     @result{} #<buffer buffers.texi>
address@hidden group
address@hidden example
address@hidden defun
+
address@hidden set-buffer buffer-or-name
+This function makes @var{buffer-or-name} the current buffer.
address@hidden must be an existing buffer or the name of an
+existing buffer.  The return value is the buffer made current.
+
+This function does not display the buffer in any window, so the user
+cannot necessarily see the buffer.  But Lisp programs will now operate
+on it.
address@hidden 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 to specified buffer the text of the region."
+  "Append the text of the region to BUFFER."
   (interactive "BAppend to buffer: \nr")
   (let ((oldbuf (current-buffer)))
     (save-current-buffer
@@ -131,27 +151,36 @@
 @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,
+Here, we bind a local variable to record the current buffer, and then
address@hidden 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
-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
address@hidden to make sure that the buffer current at the
-beginning is current again whenever the variable is unbound.
+buffer to the specified (and now current) buffer.
+
+  Alternatively, we can use the @code{with-current-buffer} macro:
+
address@hidden
address@hidden
+(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))))
address@hidden group
address@hidden 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
@@ -168,29 +197,9 @@
 @end example
 
 @noindent
-Using @code{save-current-buffer}, as we did, handles quitting, errors,
-and @code{throw}, as well as ordinary evaluation.
-
address@hidden current-buffer
-This function returns the current buffer.
-
address@hidden
address@hidden
-(current-buffer)
-     @result{} #<buffer buffers.texi>
address@hidden group
address@hidden example
address@hidden defun
-
address@hidden set-buffer buffer-or-name
-This function makes @var{buffer-or-name} the current buffer.
address@hidden must be an existing buffer or the name of an
-existing buffer.  The return value is the buffer made current.
-
-This function does not display the buffer in any window, so the user
-cannot necessarily see the buffer.  But Lisp programs will now operate
-on it.
address@hidden defun
+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 address@hidden
 The @code{save-current-buffer} special form saves the identity of the

=== modified file 'doc/lispref/positions.texi'
--- a/doc/lispref/positions.texi        2011-01-02 23:50:46 +0000
+++ b/doc/lispref/positions.texi        2011-03-19 20:31:30 +0000
@@ -798,69 +798,72 @@
 @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
address@hidden 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 address@hidden
 @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
address@hidden, 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.
-
address@hidden does not save the values of point and the mark for
-other buffers, so changes in other buffers remain in effect after
address@hidden 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
address@hidden or error (@pxref{Nonlocal Exits}).
+
+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.
address@hidden defspec
+
+  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:
+
address@hidden
+Warning: @code{save-excursion} defeated by @code{set-buffer}
address@hidden example
+
address@hidden
+To avoid such problems, you should call @code{save-excursion} only
+after setting the desired current buffer, as in the following example:
+
address@hidden
address@hidden
+(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))))
address@hidden group
address@hidden 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.
-
address@hidden
address@hidden
-(save-excursion @var{forms})
address@hidden
-(let ((old-buf (current-buffer))
-      (old-pnt (point-marker))
address@hidden group
-      (old-mark (copy-marker (mark-marker))))
-  (unwind-protect
-      (progn @var{forms})
-    (set-buffer old-buf)
address@hidden
-    (goto-char old-pnt)
-    (set-marker (mark-marker) old-mark)))
address@hidden group
address@hidden example
address@hidden 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
address@hidden  @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


reply via email to

[Prev in Thread] Current Thread [Next in Thread]