unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Stefan Monnier <monnier@iro.umontreal.ca>
To: Eli Zaretskii <eliz@gnu.org>
Cc: 24340@debbugs.gnu.org
Subject: bug#24340: insert-file-contents calls before-change-functions too late
Date: Tue, 30 Aug 2016 17:21:38 -0400	[thread overview]
Message-ID: <jwvmvjuro0v.fsf-monnier+emacsbugs@gnu.org> (raw)
In-Reply-To: <8360qim5sv.fsf@gnu.org> (Eli Zaretskii's message of "Tue, 30 Aug 2016 22:10:24 +0300")

> Thanks for the patch, but I don't want to make such pervasive changes
> for this problem.

It was for another problem (the fact that b-c-f was not called at all in
a corner case).

This is a problem (it breaks the promise that b-c-f covers all
following changes) that shows up everytime insert-file-contents is
called with a non-nil `replace' argument, which is a completely
different situation.  It's a common case.

> I thought we agreed that a single call to before-change hooks for the
> entire buffer would be an okay solution for this scenario.

Kind of, but I think it'd be a kludge compared to the patch I submitted.
This said, if you insist on fixing it some other way, that's fine by me.

It's just that this problem is a lot more common than the one we knew
about, so I think it makes it more important to fix it sooner rather
than later.

> What you suggest is exactly the slippery path which I was afraid of:
> a lot of tricky/kludgey changes whose effect we don't really
> understand, because we have no tests for the affected functionality.

Its effect is to call prepare_to_modify_buffer one more time in
a function which already calls it moments later.  Seems safer than about
90% of the changes I installed in the past.

>> +          /* FIXME: Shouldn't invalidate_buffer_caches be called by
>> +             del_range_byte or even directly by prepare_to_modify_buffer?  */
> prepare_to_modify_buffer already calls invalidate_buffer_caches.
> The direct call here is because del_range_byte is called with its last
> argument zero.

OMG, indeed, so my patch makes even more sense and is even cleaner.
See new patch below, which actually simplifies the code.

I agree that the let-binding is a bit ugly, but that's the kludge we use
currently, so my patch doesn't make it much uglier in this respect.
A cleaner way to handle this issue might be to introduce a new
buffer-local variable (like inhibit-file-supersession-check) which we'd
let-bind to t around the whole function (either inside
insert-file-contents when `replace' is non-nil, or around
revert-buffer's call to insert-file-contents).  But for now, I can live
with the current kludge.


        Stefan


diff --git a/src/fileio.c b/src/fileio.c
index bf6bf62..a63deee 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -3839,6 +3839,7 @@ by calling `format-decode', which see.  */)
       if (! giveup_match_end)
 	{
 	  ptrdiff_t temp;
+          ptrdiff_t this_count = SPECPDL_INDEX ();
 
 	  /* We win!  We can handle REPLACE the optimized way.  */
 
@@ -3868,13 +3869,19 @@ by calling `format-decode', which see.  */)
 	  beg_offset += same_at_start - BEGV_BYTE;
 	  end_offset -= ZV_BYTE - same_at_end;
 
-	  invalidate_buffer_caches (current_buffer,
-				    BYTE_TO_CHAR (same_at_start),
-				    same_at_end_charpos);
-	  del_range_byte (same_at_start, same_at_end, 0);
+          /* This binding is to avoid ask-user-about-supersession-threat
+	     being called in insert_from_buffer or del_range_bytes (via
+	     prepare_to_modify_buffer).
+             AFAICT we could avoid ask-user-about-supersession-threat by setting
+             current_buffer->modtime earlier, but we could still end up calling
+             ask-user-about-supersession-threat if the file is modified while
+             we read it, so we bind buffer-file-name instead.  */
+          specbind (intern ("buffer-file-name"), Qnil);
+	  del_range_byte (same_at_start, same_at_end);
 	  /* Insert from the file at the proper position.  */
 	  temp = BYTE_TO_CHAR (same_at_start);
 	  SET_PT_BOTH (temp, same_at_start);
+          unbind_to (this_count, Qnil);
 
 	  /* If display currently starts at beginning of line,
 	     keep it that way.  */
@@ -3979,10 +3986,9 @@ by calling `format-decode', which see.  */)
 	  /* Truncate the buffer to the size of the file.  */
 	  if (same_at_start != same_at_end)
 	    {
-	      invalidate_buffer_caches (current_buffer,
-					BYTE_TO_CHAR (same_at_start),
-					BYTE_TO_CHAR (same_at_end));
-	      del_range_byte (same_at_start, same_at_end, 0);
+              /* See previous specbind for the reason behind this.  */
+              specbind (intern ("buffer-file-name"), Qnil);
+	      del_range_byte (same_at_start, same_at_end);
 	    }
 	  inserted = 0;
 
@@ -4030,12 +4036,11 @@ by calling `format-decode', which see.  */)
 	 we are taking from the decoded string.  */
       inserted -= (ZV_BYTE - same_at_end) + (same_at_start - BEGV_BYTE);
 
+      /* See previous specbind for the reason behind this.  */
+      specbind (intern ("buffer-file-name"), Qnil);
       if (same_at_end != same_at_start)
 	{
-	  invalidate_buffer_caches (current_buffer,
-				    BYTE_TO_CHAR (same_at_start),
-				    same_at_end_charpos);
-	  del_range_byte (same_at_start, same_at_end, 0);
+	  del_range_byte (same_at_start, same_at_end);
 	  temp = GPT;
 	  eassert (same_at_start == GPT_BYTE);
 	  same_at_start = GPT_BYTE;
@@ -4056,10 +4061,6 @@ by calling `format-decode', which see.  */)
 				   same_at_start + inserted - BEGV_BYTE
 				  + BUF_BEG_BYTE (XBUFFER (conversion_buffer)))
 	   - same_at_start_charpos);
-      /* This binding is to avoid ask-user-about-supersession-threat
-	 being called in insert_from_buffer (via in
-	 prepare_to_modify_buffer).  */
-      specbind (intern ("buffer-file-name"), Qnil);
       insert_from_buffer (XBUFFER (conversion_buffer),
 			  same_at_start_charpos, inserted_chars, 0);
       /* Set `inserted' to the number of inserted characters.  */
diff --git a/src/fns.c b/src/fns.c
index 31f0fd2..d3f9a6b 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -3192,7 +3192,7 @@ into shorter lines.  */)
   SET_PT_BOTH (XFASTINT (beg), ibeg);
   insert (encoded, encoded_length);
   SAFE_FREE ();
-  del_range_byte (ibeg + encoded_length, iend + encoded_length, 1);
+  del_range_byte (ibeg + encoded_length, iend + encoded_length);
 
   /* If point was outside of the region, restore it exactly; else just
      move to the beginning of the region.  */
diff --git a/src/insdel.c b/src/insdel.c
index 5d3884b..ed914ec 100644
--- a/src/insdel.c
+++ b/src/insdel.c
@@ -1690,7 +1690,7 @@ del_range_1 (ptrdiff_t from, ptrdiff_t to, bool prepare, bool ret_string)
 /* Like del_range_1 but args are byte positions, not char positions.  */
 
 void
-del_range_byte (ptrdiff_t from_byte, ptrdiff_t to_byte, bool prepare)
+del_range_byte (ptrdiff_t from_byte, ptrdiff_t to_byte)
 {
   ptrdiff_t from, to;
 
@@ -1706,23 +1706,22 @@ del_range_byte (ptrdiff_t from_byte, ptrdiff_t to_byte, bool prepare)
   from = BYTE_TO_CHAR (from_byte);
   to = BYTE_TO_CHAR (to_byte);
 
-  if (prepare)
-    {
-      ptrdiff_t old_from = from, old_to = Z - to;
-      ptrdiff_t range_length = to - from;
-      prepare_to_modify_buffer (from, to, &from);
-      to = from + range_length;
-
-      if (old_from != from)
-	from_byte = CHAR_TO_BYTE (from);
-      if (to > ZV)
-	{
-	  to = ZV;
-	  to_byte = ZV_BYTE;
-	}
-      else if (old_to == Z - to)
-	to_byte = CHAR_TO_BYTE (to);
-    }
+  {
+    ptrdiff_t old_from = from, old_to = Z - to;
+    ptrdiff_t range_length = to - from;
+    prepare_to_modify_buffer (from, to, &from);
+    to = from + range_length;
+
+    if (old_from != from)
+      from_byte = CHAR_TO_BYTE (from);
+    if (to > ZV)
+      {
+	to = ZV;
+	to_byte = ZV_BYTE;
+      }
+    else if (old_to == Z - to)
+      to_byte = CHAR_TO_BYTE (to);
+  }
 
   del_range_2 (from, from_byte, to, to_byte, 0);
   signal_after_change (from, to - from, 0);
diff --git a/src/lisp.h b/src/lisp.h
index 97c8d9f..b757e7b 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -3514,7 +3514,7 @@ extern void insert_from_string_before_markers (Lisp_Object, ptrdiff_t,
 					       ptrdiff_t, bool);
 extern void del_range (ptrdiff_t, ptrdiff_t);
 extern Lisp_Object del_range_1 (ptrdiff_t, ptrdiff_t, bool, bool);
-extern void del_range_byte (ptrdiff_t, ptrdiff_t, bool);
+extern void del_range_byte (ptrdiff_t, ptrdiff_t);
 extern void del_range_both (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, bool);
 extern Lisp_Object del_range_2 (ptrdiff_t, ptrdiff_t,
 				ptrdiff_t, ptrdiff_t, bool);





  reply	other threads:[~2016-08-30 21:21 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-08-30 18:42 bug#24340: insert-file-contents calls before-change-functions too late Stefan Monnier
2016-08-30 19:10 ` Eli Zaretskii
2016-08-30 21:21   ` Stefan Monnier [this message]
2016-08-31 14:22     ` Eli Zaretskii
2016-08-31 14:48       ` Stefan Monnier
2016-08-31 15:03         ` Eli Zaretskii
2016-08-31 17:50           ` Stefan Monnier
2016-10-02  1:11           ` Stefan Monnier
2016-10-02  7:19             ` Eli Zaretskii
2016-10-03 13:48               ` Stefan Monnier
2016-10-03 14:13                 ` Eli Zaretskii

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

  List information: https://www.gnu.org/software/emacs/

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

  git send-email \
    --in-reply-to=jwvmvjuro0v.fsf-monnier+emacsbugs@gnu.org \
    --to=monnier@iro.umontreal.ca \
    --cc=24340@debbugs.gnu.org \
    --cc=eliz@gnu.org \
    /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 public inbox

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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).