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. */
{
specpdl_ref 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)
{
/* Ask whether to kill the buffer, and exit if the user says
"no". */
if (NILP (call1 (Qkill_buffer__possibly_save, buffer)))
return unbind_to (count, Qnil);
/* Recheck modified. */
modified = BUF_MODIFF (b) > BUF_SAVE_MODIFF (b);
}
/* 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 (Qdelete_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 (BASE_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)
{
struct buffer *obuf = XBUFFER (other);
if (obuf->base_buffer == b)
{
Fkill_buffer (other);
if (BUFFER_LIVE_P (obuf))
error ("Unable to kill buffer whose indirect buffer `%s' cannot be killed",
SDATA (BVAR (obuf, name)));
}
}
/* 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)
&& BASE_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... */
}
delete_all_overlays (b);
free_buffer_overlays (b);
/* 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_last_name (b, BVAR (b, name));
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;
}