Function: kill-buffer

kill-buffer is an interactive function defined in buffer.c.

Signature

(kill-buffer &optional BUFFER-OR-NAME)

Documentation

Kill the buffer specified by BUFFER-OR-NAME.

The argument may be a buffer or the name of an existing buffer. Argument nil or omitted means kill the current buffer. Return t if the buffer is actually killed, nil otherwise.

The functions in kill-buffer-query-functions are called with the buffer to be killed as the current buffer. If any of them returns nil, the buffer is not killed. The hook kill-buffer-hook is run before the buffer is actually killed. The buffer being killed will be current while the hook is running. Functions called by any of these hooks are supposed to not change the current buffer. Neither hook is run for internal or temporary buffers created by get-buffer-create or generate-new-buffer with argument INHIBIT-BUFFER-HOOKS non-nil.

Any processes that have this buffer as the process-buffer are killed with SIGHUP. This function calls replace-buffer-in-windows for cleaning up all windows currently displaying the buffer to be killed.

Probably introduced at or before Emacs version 15.

Key Bindings

Source Code

// Defined in /usr/src/emacs/src/buffer.c
// Skipping highlighting due to helpful-max-highlight.
{
  Lisp_Object buffer;
  struct buffer *b;
  Lisp_Object tem;
  struct Lisp_Marker *m;

  if (NILP (buffer_or_name))
    buffer = Fcurrent_buffer ();
  else
    buffer = Fget_buffer (buffer_or_name);
  if (NILP (buffer))
    nsberror (buffer_or_name);

  b = XBUFFER (buffer);

  /* Avoid trouble for buffer already dead.  */
  if (!BUFFER_LIVE_P (b))
    return Qnil;

  if (thread_check_current_buffer (b))
    return Qnil;

  /* Run hooks with the buffer to be killed as the current buffer.  */
  {
    ptrdiff_t count = SPECPDL_INDEX ();
    bool modified;

    record_unwind_protect_excursion ();
    set_buffer_internal (b);

    /* First run the query functions; if any query is answered no,
       don't kill the buffer.  */
    if (!b->inhibit_buffer_hooks)
      {
	tem = CALLN (Frun_hook_with_args_until_failure,
		     Qkill_buffer_query_functions);
	if (NILP (tem))
	  return unbind_to (count, Qnil);
      }

    /* Is this a modified buffer that's visiting a file? */
    modified = !NILP (BVAR (b, filename))
      && BUF_MODIFF (b) > BUF_SAVE_MODIFF (b);

    /* Query if the buffer is still modified.  */
    if (INTERACTIVE && modified)
      {
	AUTO_STRING (format, "Buffer %s modified; kill anyway? ");
	tem = do_yes_or_no_p (CALLN (Fformat, format, BVAR (b, name)));
	if (NILP (tem))
	  return unbind_to (count, Qnil);
      }

    /* Delete the autosave file, if requested. */
    if (modified
	&& kill_buffer_delete_auto_save_files
	&& delete_auto_save_files
	&& !NILP (Frecent_auto_save_p ())
	&& STRINGP (BVAR (b, auto_save_file_name))
	&& !NILP (Ffile_exists_p (BVAR (b, auto_save_file_name)))
	/* If `auto-save-visited-mode' is on, then we're auto-saving
	   to the visited file -- don't delete it.. */
	&& NILP (Fstring_equal (BVAR (b, auto_save_file_name),
				BVAR (b, filename))))
      {
	tem = do_yes_or_no_p (build_string ("Delete auto-save file? "));
	if (!NILP (tem))
	  call0 (intern ("delete-auto-save-file-if-necessary"));
      }

    /* If the hooks have killed the buffer, exit now.  */
    if (!BUFFER_LIVE_P (b))
      return unbind_to (count, Qt);

    /* Then run the hooks.  */
    if (!b->inhibit_buffer_hooks)
      run_hook (Qkill_buffer_hook);
    unbind_to (count, Qnil);
  }

  /* If the hooks have killed the buffer, exit now.  */
  if (!BUFFER_LIVE_P (b))
    return Qt;

  /* We have no more questions to ask.  Verify that it is valid
     to kill the buffer.  This must be done after the questions
     since anything can happen within do_yes_or_no_p.  */

  /* Don't kill the minibuffer now current.  */
  if (EQ (buffer, XWINDOW (minibuf_window)->contents))
    return Qnil;

  /* When we kill an ordinary buffer which shares its buffer text
     with indirect buffer(s), we must kill indirect buffer(s) too.
     We do it at this stage so nothing terrible happens if they
     ask questions or their hooks get errors.  */
  if (!b->base_buffer && b->indirections > 0)
    {
      Lisp_Object tail, other;

      FOR_EACH_LIVE_BUFFER (tail, other)
	if (XBUFFER (other)->base_buffer == b)
	  Fkill_buffer (other);

      /* Exit if we now have killed the base buffer (Bug#11665).  */
      if (!BUFFER_LIVE_P (b))
	return Qt;
    }

  /* Run replace_buffer_in_windows before making another buffer current
     since set-window-buffer-start-and-point will refuse to make another
     buffer current if the selected window does not show the current
     buffer (bug#10114).  */
  replace_buffer_in_windows (buffer);

  /* Exit if replacing the buffer in windows has killed our buffer.  */
  if (!BUFFER_LIVE_P (b))
    return Qt;

  /* Make this buffer not be current.  Exit if it is the sole visible
     buffer.  */
  if (b == current_buffer)
    {
      tem = Fother_buffer (buffer, Qnil, Qnil);
      Fset_buffer (tem);
      if (b == current_buffer)
	return Qnil;
    }

  /* If the buffer now current is shown in the minibuffer and our buffer
     is the sole other buffer give up.  */
  XSETBUFFER (tem, current_buffer);
  if (EQ (tem, XWINDOW (minibuf_window)->contents)
      && EQ (buffer, Fother_buffer (buffer, Qnil, Qnil)))
    return Qnil;

  /* Now there is no question: we can kill the buffer.  */

  /* Unlock this buffer's file, if it is locked.  */
  unlock_buffer (b);

  kill_buffer_processes (buffer);
  kill_buffer_xwidgets (buffer);

  /* Killing buffer processes may run sentinels which may have killed
     our buffer.  */
  if (!BUFFER_LIVE_P (b))
    return Qt;

  /* These may run Lisp code and into infinite loops (if someone
     insisted on circular lists) so allow quitting here.  */
  frames_discard_buffer (buffer);

  clear_charpos_cache (b);

  tem = Vinhibit_quit;
  Vinhibit_quit = Qt;
  /* Once the buffer is removed from Vbuffer_alist, its undo_list field is
     not traced by the GC in the same way.  So set it to nil early.  */
  bset_undo_list (b, Qnil);
  /* Remove the buffer from the list of all buffers.  */
  Vbuffer_alist = Fdelq (Frassq (buffer, Vbuffer_alist), Vbuffer_alist);
  /* If replace_buffer_in_windows didn't do its job fix that now.  */
  replace_buffer_in_windows_safely (buffer);
  Vinhibit_quit = tem;

  if (b->base_buffer)
    {
      INTERVAL i;
      /* Unchain all markers that belong to this indirect buffer.
	 Don't unchain the markers that belong to the base buffer
	 or its other indirect buffers.  */
      struct Lisp_Marker **mp = &BUF_MARKERS (b);
      while ((m = *mp))
	{
	  if (m->buffer == b)
	    {
	      m->buffer = NULL;
	      *mp = m->next;
	    }
	  else
	    mp = &m->next;
	}
      /* Intervals should be owned by the base buffer (Bug#16502).  */
      i = buffer_intervals (b);
      if (i)
	{
	  Lisp_Object owner;
	  XSETBUFFER (owner, b->base_buffer);
	  set_interval_object (i, owner);
	}
    }
  else
    {
      /* Unchain all markers of this buffer and its indirect buffers.
	 and leave them pointing nowhere.  */
      for (m = BUF_MARKERS (b); m; )
	{
	  struct Lisp_Marker *next = m->next;
	  m->buffer = 0;
	  m->next = NULL;
	  m = next;
	}
      BUF_MARKERS (b) = NULL;
      set_buffer_intervals (b, NULL);

      /* Perhaps we should explicitly free the interval tree here...  */
    }
  /* Since we've unlinked the markers, the overlays can't be here any more
     either.  */
  set_buffer_overlays_before (b, NULL);
  set_buffer_overlays_after (b, NULL);

  /* Reset the local variables, so that this buffer's local values
     won't be protected from GC.  They would be protected
     if they happened to remain cached in their symbols.
     This gets rid of them for certain.  */
  reset_buffer_local_variables (b, 1);

  bset_name (b, Qnil);

  block_input ();
  if (b->base_buffer)
    {
      /* Notify our base buffer that we don't share the text anymore.  */
      eassert (b->indirections == -1);
      b->base_buffer->indirections--;
      eassert (b->base_buffer->indirections >= 0);
      /* Make sure that we wasn't confused.  */
      eassert (b->window_count == -1);
    }
  else
    {
      /* Make sure that no one shows us.  */
      eassert (b->window_count == 0);
      /* No one shares our buffer text, can free it.  */
      free_buffer_text (b);
    }

  if (b->newline_cache)
    {
      free_region_cache (b->newline_cache);
      b->newline_cache = 0;
    }
  if (b->width_run_cache)
    {
      free_region_cache (b->width_run_cache);
      b->width_run_cache = 0;
    }
  if (b->bidi_paragraph_cache)
    {
      free_region_cache (b->bidi_paragraph_cache);
      b->bidi_paragraph_cache = 0;
    }
  bset_width_table (b, Qnil);
  unblock_input ();

  run_buffer_list_update_hook (b);

  return Qt;
}