all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Helmut Eller <eller.helmut@gmail.com>
To: martin rudalics <rudalics@gmx.at>
Cc: 6454@debbugs.gnu.org
Subject: bug#6454: 24.0.50; kill-buffer switches current-buffer
Date: Sun, 20 Jun 2010 20:00:33 +0200	[thread overview]
Message-ID: <m2bpb5iny6.fsf@gmail.com> (raw)
In-Reply-To: <4C1DF080.2030203@gmx.at> (martin rudalics's message of "Sun, 20 Jun 2010 12:42:08 +0200")

[-- Attachment #1: Type: text/plain, Size: 1186 bytes --]

* martin rudalics [2010-06-20 12:42+0200] writes:

>> I'm wondering a bit though: kill-buffer protects the current buffer
>> while calling kill-buffer-hook.  Wouldn't it be prudent to do that for
>> the entire function?
>
> You mean for the case where the buffer we want to kill is not the
> current buffer?

Both cases.  Even in the case where the to-be-killed buffer is not
current, kill-buffer may decide not to actually kill it and instead
return nil; this case should also not switch the buffer.

>> Actually, I think there is a small bug there: if kill-buffer-hook is a
>> list of functions, the first function could potentially switch buffer
>> and the second function would be called in the wrong buffer.
>
> I suppose you're right.  Could you propose a patch for this and the
> above issue?

Below are two patches.  The first essentially adds a save-excursion
around the whole function.

The second patch adds a function run_hook_in_buffer. It iterates over
the functions in the hook and for each function explicitly sets the
buffer before calling it.  I had to introduce a new macro
DO_HOOK_FUNCTIONS which is a big hammer for this, but I couldn't find a
better way.

Helmut


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: kill-buffer-1.patch --]
[-- Type: text/x-diff, Size: 2533 bytes --]

=== modified file 'src/buffer.c'
--- src/buffer.c	2010-06-05 00:41:32 +0000
+++ src/buffer.c	2010-06-20 17:24:02 +0000
@@ -1413,6 +1413,9 @@
   register struct Lisp_Marker *m;
   struct gcpro gcpro1;
 
+  int count = SPECPDL_INDEX ();
+  record_unwind_protect (save_excursion_restore, save_excursion_save ());
+
   if (NILP (buffer_or_name))
     buffer = Fcurrent_buffer ();
   else
@@ -1424,7 +1427,7 @@
 
   /* Avoid trouble for buffer already dead.  */
   if (NILP (b->name))
-    return Qnil;
+    return unbind_to (count, Qnil);
 
   /* Query if the buffer is still modified.  */
   if (INTERACTIVE && !NILP (b->filename)
@@ -1435,15 +1438,13 @@
 				     b->name, make_number (0)));
       UNGCPRO;
       if (NILP (tem))
-	return Qnil;
+	return unbind_to (count, Qnil);
     }
 
   /* Run hooks with the buffer to be killed the current buffer.  */
   {
-    int count = SPECPDL_INDEX ();
     Lisp_Object arglist[1];
 
-    record_unwind_protect (save_excursion_restore, save_excursion_save ());
     set_buffer_internal (b);
 
     /* First run the query functions; if any query is answered no,
@@ -1455,7 +1456,6 @@
 
     /* Then run the hooks.  */
     Frun_hooks (1, &Qkill_buffer_hook);
-    unbind_to (count, Qnil);
   }
 
   /* We have no more questions to ask.  Verify that it is valid
@@ -1464,10 +1464,10 @@
 
   /* Don't kill the minibuffer now current.  */
   if (EQ (buffer, XWINDOW (minibuf_window)->buffer))
-    return Qnil;
+    return unbind_to (count, Qnil);
 
   if (NILP (b->name))
-    return Qnil;
+    return unbind_to (count, Qnil);
 
   /* When we kill a base buffer, kill all its indirect buffers.
      We do it at this stage so nothing terrible happens if they
@@ -1499,7 +1499,7 @@
       tem = Fother_buffer (buffer, Qnil, Qnil);
       Fset_buffer (tem);
       if (b == current_buffer)
-	return Qnil;
+	return unbind_to (count, Qnil);
     }
 
   /* Notice if the buffer to kill is the sole visible buffer
@@ -1509,7 +1509,7 @@
     {
       tem = Fother_buffer (buffer, Qnil, Qnil);
       if (EQ (buffer, tem))
-	return Qnil;
+	return unbind_to (count, Qnil);
     }
 
   /* Now there is no question: we can kill the buffer.  */
@@ -1527,7 +1527,7 @@
      have called kill-buffer.  */
 
   if (NILP (b->name))
-    return Qnil;
+    return unbind_to (count, Qnil);
 
   clear_charpos_cache (b);
 
@@ -1609,7 +1609,7 @@
   UNBLOCK_INPUT;
   b->undo_list = Qnil;
 
-  return Qt;
+  return unbind_to (count, Qt);
 }
 \f
 /* Move the assoc for buffer BUF to the front of buffer-alist.  Since


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: kill-buffer-2.patch --]
[-- Type: text/x-diff, Size: 7056 bytes --]

=== modified file 'src/buffer.c'
--- src/buffer.c	2010-06-20 17:24:02 +0000
+++ src/buffer.c	2010-06-20 17:35:19 +0000
@@ -1382,6 +1382,34 @@
   return Qnil;
 }
 
+/* Run the hook HOOK in buffer BUFFER.
+   Make sure that each function is called in BUFFER.
+   Return the value returned by the last function. */
+static Lisp_Object
+run_hook_in_buffer (Lisp_Object hook, Lisp_Object buffer)
+{
+  Lisp_Object ret = Qnil;
+  struct gcpro gcpro1;
+  register struct buffer *b = XBUFFER (buffer);
+  int count = SPECPDL_INDEX ();
+  GCPRO1 (ret);
+
+  record_unwind_protect (set_buffer_if_live, Fcurrent_buffer ());
+  set_buffer_internal (b);
+  {
+    DO_HOOK_FUNCTIONS (state, hook, fun) 
+      {
+	eassert (b->name);
+	/* set again, because calling a function may switch buffer */
+	set_buffer_internal (b);
+	ret = call0 (fun);
+      }
+    UNGCPRO;
+  }
+  UNGCPRO;
+  return unbind_to (count, ret);
+}
+
 /*
   DEFVAR_LISP ("kill-buffer-hook", no_cell, "\
 Hook to be run (by `run-hooks', which see) when a buffer is killed.\n\
@@ -1455,7 +1483,7 @@
       return unbind_to (count, Qnil);
 
     /* Then run the hooks.  */
-    Frun_hooks (1, &Qkill_buffer_hook);
+    run_hook_in_buffer (Qkill_buffer_hook, buffer);
   }
 
   /* We have no more questions to ask.  Verify that it is valid

=== modified file 'src/eval.c'
--- src/eval.c	2010-05-14 17:53:42 +0000
+++ src/eval.c	2010-06-20 17:35:19 +0000
@@ -2673,13 +2673,62 @@
   return run_hook_with_args (nargs, args, until_failure);
 }
 
+/* Move to the next iteration state and return the current function.
+   Return Qunbound as end indicator. */
+Lisp_Object
+hook_iterator_next (struct hook_iterator_state* state)
+{
+  Lisp_Object val = state->val;
+  Lisp_Object globals = state->globals;
+
+  if (NILP (globals))
+    {
+      if (EQ (val, Qunbound) || NILP (val))
+	return Qunbound;  /* loop end marker */
+      if (!CONSP (val) || EQ (XCAR (val), Qlambda))
+	{
+	  state->val = Qnil;
+	  return val;
+	}
+      if (EQ (XCAR (val), Qt))
+	{
+	  /* t indicates this hook has a local binding;
+	     it means to run the global binding too.  */
+	  state->val = XCDR (val);
+	  state->globals = Fdefault_value (state->sym);
+	  return hook_iterator_next (state);
+	}
+      else
+	{
+	  state->val = XCDR (val);
+	  return XCAR (val);
+	}
+    }
+
+  else if (!CONSP (globals) || EQ (XCAR (globals), Qlambda))
+    {
+      state->globals = Qnil;
+      return globals;
+    }
+  else
+    {
+      state->globals = XCDR (globals);
+      /* In a global value, t should not occur.  If it does, we
+	 must ignore it to avoid an endless loop.  */
+      if (XCAR (globals) == Qt)
+	return hook_iterator_next (state);
+      else
+	return XCAR (globals);
+    }
+}
+
 /* ARGS[0] should be a hook symbol.
    Call each of the functions in the hook value, passing each of them
    as arguments all the rest of ARGS (all NARGS - 1 elements).
    COND specifies a condition to test after each call
    to decide whether to stop.
    The caller (or its caller, etc) must gcpro all of ARGS,
-   except that it isn't necessary to gcpro ARGS[0].  */
+   except that it isn't necessary to gcpro ARGS[0]. */
 
 static Lisp_Object
 run_hook_with_args (nargs, args, cond)
@@ -2687,74 +2736,32 @@
      Lisp_Object *args;
      enum run_hooks_condition cond;
 {
-  Lisp_Object sym, val, ret;
-  struct gcpro gcpro1, gcpro2, gcpro3;
+  Lisp_Object ret;
+  struct gcpro gcpro1;
 
   /* If we are dying or still initializing,
      don't do anything--it would probably crash if we tried.  */
   if (NILP (Vrun_hooks))
     return Qnil;
 
-  sym = args[0];
-  val = find_symbol_value (sym);
   ret = (cond == until_failure ? Qt : Qnil);
-
-  if (EQ (val, Qunbound) || NILP (val))
-    return ret;
-  else if (!CONSP (val) || EQ (XCAR (val), Qlambda))
-    {
-      args[0] = val;
-      return Ffuncall (nargs, args);
-    }
-  else
-    {
-      Lisp_Object globals = Qnil;
-      GCPRO3 (sym, val, globals);
-
-      for (;
-	   CONSP (val) && ((cond == to_completion)
-			   || (cond == until_success ? NILP (ret)
-			       : !NILP (ret)));
-	   val = XCDR (val))
-	{
-	  if (EQ (XCAR (val), Qt))
-	    {
-	      /* t indicates this hook has a local binding;
-		 it means to run the global binding too.  */
-	      globals = Fdefault_value (sym);
-	      if (NILP (globals)) continue;
-
-	      if (!CONSP (globals) || EQ (XCAR (globals), Qlambda))
-		{
-		  args[0] = globals;
-		  ret = Ffuncall (nargs, args);
-		}
-	      else
-		{
-		  for (;
-		       CONSP (globals) && ((cond == to_completion)
-					   || (cond == until_success ? NILP (ret)
-					       : !NILP (ret)));
-		       globals = XCDR (globals))
-		    {
-		      args[0] = XCAR (globals);
-		      /* In a global value, t should not occur.  If it does, we
-			 must ignore it to avoid an endless loop.  */
-		      if (!EQ (args[0], Qt))
-			ret = Ffuncall (nargs, args);
-		    }
-		}
-	    }
-	  else
-	    {
-	      args[0] = XCAR (val);
-	      ret = Ffuncall (nargs, args);
-	    }
-	}
-
-      UNGCPRO;
-      return ret;
-    }
+  GCPRO1 (ret);
+
+  {
+    DO_HOOK_FUNCTIONS (state, args[0], fun)
+      {
+	if ((cond == until_success) && !NILP (ret))
+	  break;
+	if ((cond == until_failure) && NILP (ret))
+	  break;
+	args[0] = fun;
+	ret = Ffuncall (nargs, args);
+      }
+    UNGCPRO;
+  }
+
+  UNGCPRO;
+  return ret;
 }
 
 /* Run a hook symbol ARGS[0], but use FUNLIST instead of the actual

=== modified file 'src/lisp.h'
--- src/lisp.h	2010-06-09 22:08:50 +0000
+++ src/lisp.h	2010-06-20 17:35:19 +0000
@@ -2853,6 +2853,40 @@
 EXFUN (Frun_hook_with_args_until_failure, MANY);
 extern Lisp_Object run_hook_list_with_args P_ ((Lisp_Object, int, Lisp_Object *));
 extern void run_hook_with_args_2 P_ ((Lisp_Object, Lisp_Object, Lisp_Object));
+
+/* Set up a while loop to iterate over the functions of a hook.
+   STATE a variable used to hold iteration state
+   HOOK_SYMBOL an expression which evaluates to the hook symbol
+   VAR variable to bind to function values
+
+   The macro needs to be used at the beginning of a block and
+   UNGCPRO must be called at the end, e.g.:
+   {
+     DO_HOOK_FUNCTIONS (state, Qkill_buffer_hook, fun) {
+       call0 (fun);
+     }
+     UNGCPRO;
+   }
+
+   Since this is a while loop, "break" can be used to terminate the
+   loop. */
+#define DO_HOOK_FUNCTIONS(STATE, HOOK_SYMBOL, VAR)		\
+  struct hook_iterator_state STATE = { Qnil, Qnil, Qnil };	\
+  struct gcpro gcpro1, gcpro2, gcpro3;				\
+  Lisp_Object VAR;						\
+  GCPRO3 ((STATE.sym), (STATE.val), (STATE.globals));		\
+  STATE.sym = HOOK_SYMBOL;					\
+  STATE.val = find_symbol_value (STATE.sym);			\
+  while (!EQ ((VAR = hook_iterator_next (&STATE)), Qunbound))
+
+/* used by the macro above */
+struct hook_iterator_state {
+  Lisp_Object sym;		/* hook symbol; read-only */
+  Lisp_Object val;		/* (buffer-local) value */
+  Lisp_Object globals;		/* global value */
+};
+Lisp_Object hook_iterator_next (struct hook_iterator_state*);
+
 EXFUN (Fand, UNEVALLED);
 EXFUN (For, UNEVALLED);
 EXFUN (Fif, UNEVALLED);


  reply	other threads:[~2010-06-20 18:00 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-06-18 11:10 bug#6454: 24.0.50; kill-buffer switches current-buffer Helmut Eller
2010-06-18 12:19 ` martin rudalics
2010-06-18 13:54   ` Stefan Monnier
2010-06-18 14:33     ` martin rudalics
2010-06-18 15:33       ` Stefan Monnier
2010-06-18 19:12         ` martin rudalics
2010-06-18 22:30           ` martin rudalics
2010-06-19 13:38 ` martin rudalics
2010-06-20  7:48   ` Helmut Eller
2010-06-20 10:42     ` martin rudalics
2010-06-20 18:00       ` Helmut Eller [this message]
2010-06-21 10:46         ` martin rudalics
2010-06-21 14:25           ` Helmut Eller
2010-06-21 15:49             ` martin rudalics
2010-06-21 16:19               ` Helmut Eller
2010-06-21 17:38                 ` martin rudalics
2011-09-21 20:44                   ` Lars Magne Ingebrigtsen
2011-09-22  7:07                     ` Helmut Eller
2011-09-23 10:56                       ` Lars Magne Ingebrigtsen
2010-06-24 23:07     ` Stefan Monnier

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=m2bpb5iny6.fsf@gmail.com \
    --to=eller.helmut@gmail.com \
    --cc=6454@debbugs.gnu.org \
    --cc=rudalics@gmx.at \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.