Function: buffer-swap-text

buffer-swap-text is a function defined in buffer.c.

Signature

(buffer-swap-text BUFFER)

Documentation

Swap the text between current buffer and BUFFER.

Using this function from save-excursion might produce surprising results, see Info node (elisp)Swapping Text.

View in manual

Probably introduced at or before Emacs version 23.1.

Source Code

// Defined in /usr/src/emacs/src/buffer.c
// Skipping highlighting due to helpful-max-highlight.
{
  struct buffer *other_buffer;
  CHECK_BUFFER (buffer);
  other_buffer = XBUFFER (buffer);

  if (!BUFFER_LIVE_P (other_buffer))
    error ("Cannot swap a dead buffer's text");

  /* Actually, it probably works just fine.
   * if (other_buffer == current_buffer)
   *   error ("Cannot swap a buffer's text with itself"); */

  /* Actually, this may be workable as well, tho probably only if they're
     *both* indirect.  */
  if (other_buffer->base_buffer
      || current_buffer->base_buffer)
    error ("Cannot swap indirect buffers's text");

  { /* This is probably harder to make work.  */
    Lisp_Object tail, other;
    FOR_EACH_LIVE_BUFFER (tail, other)
      if (XBUFFER (other)->base_buffer == other_buffer
	  || XBUFFER (other)->base_buffer == current_buffer)
	error ("One of the buffers to swap has indirect buffers");
  }

#define swapfield(field, type) \
  do {							\
    type tmp##field = other_buffer->field;		\
    other_buffer->field = current_buffer->field;	\
    current_buffer->field = tmp##field;			\
  } while (0)
#define swapfield_(field, type) \
  do {							\
    type tmp##field = BVAR (other_buffer, field);		\
    bset_##field (other_buffer, BVAR (current_buffer, field));	\
    bset_##field (current_buffer, tmp##field);			\
  } while (0)

  swapfield (own_text, struct buffer_text);
  eassert (current_buffer->text == &current_buffer->own_text);
  eassert (other_buffer->text == &other_buffer->own_text);
#ifdef REL_ALLOC
  r_alloc_reset_variable ((void **) &current_buffer->own_text.beg,
			  (void **) &other_buffer->own_text.beg);
  r_alloc_reset_variable ((void **) &other_buffer->own_text.beg,
			  (void **) &current_buffer->own_text.beg);
#endif /* REL_ALLOC */

  swapfield (pt, ptrdiff_t);
  swapfield (pt_byte, ptrdiff_t);
  swapfield (begv, ptrdiff_t);
  swapfield (begv_byte, ptrdiff_t);
  swapfield (zv, ptrdiff_t);
  swapfield (zv_byte, ptrdiff_t);
  eassert (!current_buffer->base_buffer);
  eassert (!other_buffer->base_buffer);
  swapfield (indirections, ptrdiff_t);
  current_buffer->clip_changed = 1;	other_buffer->clip_changed = 1;
  swapfield (newline_cache, struct region_cache *);
  swapfield (width_run_cache, struct region_cache *);
  swapfield (bidi_paragraph_cache, struct region_cache *);
  current_buffer->prevent_redisplay_optimizations_p = 1;
  other_buffer->prevent_redisplay_optimizations_p = 1;
  swapfield (long_line_optimizations_p, bool_bf);
  swapfield_ (undo_list, Lisp_Object);
  swapfield_ (mark, Lisp_Object);
  swapfield_ (mark_active, Lisp_Object); /* Belongs with the `mark'.  */
  swapfield_ (enable_multibyte_characters, Lisp_Object);
  swapfield_ (bidi_display_reordering, Lisp_Object);
  swapfield_ (bidi_paragraph_direction, Lisp_Object);
  swapfield_ (bidi_paragraph_separate_re, Lisp_Object);
  swapfield_ (bidi_paragraph_start_re, Lisp_Object);
  /* FIXME: Not sure what we should do with these *_marker fields.
     Hopefully they're just nil anyway.  */
  swapfield_ (pt_marker, Lisp_Object);
  swapfield_ (begv_marker, Lisp_Object);
  swapfield_ (zv_marker, Lisp_Object);
  bset_point_before_scroll (current_buffer, Qnil);
  bset_point_before_scroll (other_buffer, Qnil);

  modiff_incr (&current_buffer->text->modiff, 1);
  modiff_incr (&other_buffer->text->modiff, 1);
  modiff_incr (&current_buffer->text->chars_modiff, 1);
  modiff_incr (&other_buffer->text->chars_modiff, 1);
  modiff_incr (&current_buffer->text->overlay_modiff, 1);
  modiff_incr (&other_buffer->text->overlay_modiff, 1);
  current_buffer->text->beg_unchanged = current_buffer->text->gpt;
  current_buffer->text->end_unchanged = current_buffer->text->gpt;
  other_buffer->text->beg_unchanged = other_buffer->text->gpt;
  other_buffer->text->end_unchanged = other_buffer->text->gpt;
  swap_buffer_overlays (current_buffer, other_buffer);
  {
    struct Lisp_Marker *m;
    for (m = BUF_MARKERS (current_buffer); m; m = m->next)
      if (m->buffer == other_buffer)
	m->buffer = current_buffer;
      else
	/* Since there's no indirect buffer in sight, markers on
	   BUF_MARKERS(buf) should either be for `buf' or dead.  */
	eassert (!m->buffer);
    for (m = BUF_MARKERS (other_buffer); m; m = m->next)
      if (m->buffer == current_buffer)
	m->buffer = other_buffer;
      else
	/* Since there's no indirect buffer in sight, markers on
	   BUF_MARKERS(buf) should either be for `buf' or dead.  */
	eassert (!m->buffer);
  }
  { /* Some of the C code expects that both window markers of a
       live window points to that window's buffer.  So since we
       just swapped the markers between the two buffers, we need
       to undo the effect of this swap for window markers.  */
    Lisp_Object w = selected_window, ws = Qnil;
    Lisp_Object buf1, buf2;
    XSETBUFFER (buf1, current_buffer); XSETBUFFER (buf2, other_buffer);

    while (NILP (Fmemq (w, ws)))
      {
	ws = Fcons (w, ws);
	if (MARKERP (XWINDOW (w)->pointm)
	    && (BASE_EQ (XWINDOW (w)->contents, buf1)
		|| BASE_EQ (XWINDOW (w)->contents, buf2)))
	  Fset_marker (XWINDOW (w)->pointm,
		       make_fixnum
		       (BUF_BEGV (XBUFFER (XWINDOW (w)->contents))),
		       XWINDOW (w)->contents);
	/* Blindly copied from pointm part.  */
	if (MARKERP (XWINDOW (w)->old_pointm)
	    && (BASE_EQ (XWINDOW (w)->contents, buf1)
		|| BASE_EQ (XWINDOW (w)->contents, buf2)))
	  Fset_marker (XWINDOW (w)->old_pointm,
		       make_fixnum
		       (BUF_BEGV (XBUFFER (XWINDOW (w)->contents))),
		       XWINDOW (w)->contents);
	if (MARKERP (XWINDOW (w)->start)
	    && (BASE_EQ (XWINDOW (w)->contents, buf1)
		|| BASE_EQ (XWINDOW (w)->contents, buf2)))
	  Fset_marker (XWINDOW (w)->start,
		       make_fixnum
		       (XBUFFER (XWINDOW (w)->contents)->last_window_start),
		       XWINDOW (w)->contents);
	w = Fnext_window (w, Qt, Qt);
      }
  }

  if (current_buffer->text->intervals)
    (eassert (BASE_EQ (current_buffer->text->intervals->up.obj, buffer)),
     XSETBUFFER (current_buffer->text->intervals->up.obj, current_buffer));
  if (other_buffer->text->intervals)
    (eassert (BASE_EQ (other_buffer->text->intervals->up.obj,
		       Fcurrent_buffer ())),
     XSETBUFFER (other_buffer->text->intervals->up.obj, other_buffer));

  return Qnil;
}