unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* undo weirdness with insert-file-contents
@ 2008-02-28  7:46 Miles Bader
  2008-02-28  9:56 ` martin rudalics
  2008-02-28 17:45 ` Glenn Morris
  0 siblings, 2 replies; 28+ messages in thread
From: Miles Bader @ 2008-02-28  7:46 UTC (permalink / raw)
  To: emacs-devel

I define a "message-setup-hook" which for some reason has the unintended
side-effect of turning off undo in the result message-mode buffer
[buffer-undo-list has a value of t].

I've tracked the problem down to a call to `insert-file-contents' in the
hook function; If I remove the call to insert-file-contents, undo
doesn't get turned off.

I looked at the C code for insert-file-contents, and its handling of the
undo list looks pretty messy, it seems to be saving it and restoring
multiple times (sometimes nested?) using multiple methods of restoring
the old value....

The following cut-down message-setup-hook illustrates the problem:

   (defvar file-to-insert "~/.profile")
   (defun test-message-setup-hook ()
     (save-excursion
       (insert "Foo: bar\n")
       (insert-file-contents file-to-insert)))
   (setq message-setup-hook nil)
   (add-hook 'message-setup-hook 'test-message-setup-hook)

[Substitute an appropriate filename for the value of the file-to-insert
variable; anything is OK as long as it exists and is readable.  I just
used "~/.profile" because it usually exists :-]

To test this, in "emacs -Q", evaluate the above code, and then do
"M-x message-mail RET" to start composing in message-mode.  Insert some
test and then try to undo it -- it should fail, because undo is turned
off in that buffer.

If you then do (setq message-setup-hook nil) and try
"M-x message-mail RET" again, undo should work properly this time.

Anyone know more about the treatment of bufer-undo-list in
insert-file-contents?

Thanks,

-Miles

-- 
Love is a snowmobile racing across the tundra.  Suddenly it flips over,
pinning you underneath.  At night the ice weasels come.  --Nietzsche




^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-28  7:46 undo weirdness with insert-file-contents Miles Bader
@ 2008-02-28  9:56 ` martin rudalics
  2008-02-28 11:01   ` Miles Bader
  2008-02-28 17:45 ` Glenn Morris
  1 sibling, 1 reply; 28+ messages in thread
From: martin rudalics @ 2008-02-28  9:56 UTC (permalink / raw)
  To: Miles Bader; +Cc: emacs-devel

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

> I looked at the C code for insert-file-contents, and its handling of the
> undo list looks pretty messy, it seems to be saving it and restoring
> multiple times (sometimes nested?) using multiple methods of restoring
> the old value....

... which is messy because I tried to avoid that intermediate steps of the
decoding process show up in the undo-list.  Please try the attached patch.

[-- Attachment #2: fileio.patch --]
[-- Type: text/plain, Size: 514 bytes --]

*** fileio.c.~1.602.~	Thu Feb 14 20:41:44 2008
--- fileio.c	Thu Feb 28 10:41:36 2008
***************
*** 4772,4778 ****
--- 4772,4783 ----
  		/* In the non-visiting case record only the final insertion. */
  		current_buffer->undo_list =
  		  Fcons (Fcons (lbeg, lend), Fcdr (old_undo));
+ 	      eles
+ 		current_buffer->undo_list =
+ 		  Fcons (Fcons (lbeg, lend), old_undo);
  	    }
+ 	  else
+ 	    current_buffer->undo_list = old_undo;
  	}
        else
  	/* If undo_list was Qt before, keep it that way.

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-28  9:56 ` martin rudalics
@ 2008-02-28 11:01   ` Miles Bader
  2008-02-28 13:12     ` martin rudalics
  0 siblings, 1 reply; 28+ messages in thread
From: Miles Bader @ 2008-02-28 11:01 UTC (permalink / raw)
  To: martin rudalics; +Cc: emacs-devel

martin rudalics <rudalics@gmx.at> writes:
>> I looked at the C code for insert-file-contents, and its handling of the
>> undo list looks pretty messy, it seems to be saving it and restoring
>> multiple times (sometimes nested?) using multiple methods of restoring
>> the old value....
>
> ... which is messy because I tried to avoid that intermediate steps of the
> decoding process show up in the undo-list.  Please try the attached patch.

That avoids the "undo disabled" effect, but the resulting undo list
seems to be screwed up:  hitting undo as the first thing in the
resulting buffer ends up deleting a buffer range that is pretty clearly
bogus (the deleted range is from the first character in the buffer to
part-way through a word).

Not sure what's ending up where, but here's the (head of the) message
buffer as first displayed:

Foo: bar
Blat: Foop
To: 
Subject: 
From: Miles Bader <miles@dhapc248.dev.necel.com>
--text follows this line--

The "Foo: bar\n" was inserted by the (insert...) call inside the hook, the
"Blat: Foop\n" was inserted by the call to insert-file-contents (I made
it point to a file other than ~/.profile :-), and the rest inserted by
message-mode (not sure whether before or after the hook was called).

The value of buffer-undo-list is:

(nil
 (10 . 21)
 (1 . 21)
 (t 0 . 0))

-Miles

-- 
"An atheist doesn't have to be someone who thinks he has a proof that there
can't be a god.  He only has to be someone who believes that the evidence
on the God question is at a similar level to the evidence on the werewolf
question."  [John McCarthy]




^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-28 11:01   ` Miles Bader
@ 2008-02-28 13:12     ` martin rudalics
  2008-02-28 16:52       ` Stefan Monnier
  0 siblings, 1 reply; 28+ messages in thread
From: martin rudalics @ 2008-02-28 13:12 UTC (permalink / raw)
  To: Miles Bader; +Cc: emacs-devel

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

 > The value of buffer-undo-list is:
 >
 > (nil
 >  (10 . 21)
 >  (1 . 21)
 >  (t 0 . 0))

The (1 . 21) entry is silly and obviously breaks undoing the prior
(insert "Foo: bar\n").  Although EQ (XCAR (tem), lbeg) was not very
intelligent, setting this to (XCAR (tem) == lbeg) doesn't seem to help
either.  Please try again with the attached patch.

GDB doesn't work here currently, so I can't debug this.  But could you
try finding out in `insert-file-contents' (1) why the test in

	      if (CONSP (tem) && INTEGERP (XCAR (tem)) &&
		  INTEGERP (XCDR (tem)) && (XCAR (tem) == lbeg))
		/* In the non-visiting case record only the final insertion. */
		current_buffer->undo_list =
		  Fcons (Fcons (lbeg, lend), Fcdr (old_undo));

fails and (2) which value old_undo has here?  Thanks in advance.

[-- Attachment #2: fileio.patch --]
[-- Type: text/plain, Size: 1008 bytes --]

*** fileio.c.~1.602.~	Thu Feb 14 20:41:44 2008
--- fileio.c	Thu Feb 28 13:59:42 2008
***************
*** 4674,4680 ****
  
        /* Save old undo list and don't record undo for decoding. */
        old_undo = current_buffer->undo_list;
-       current_buffer->undo_list = Qt;
  
        if (NILP (replace))
  	{
--- 4674,4679 ----
***************
*** 4768,4774 ****
  	    {
  	      Lisp_Object tem = XCAR (old_undo);
  	      if (CONSP (tem) && INTEGERP (XCAR (tem)) &&
! 		  INTEGERP (XCDR (tem)) && EQ (XCAR (tem), lbeg))
  		/* In the non-visiting case record only the final insertion. */
  		current_buffer->undo_list =
  		  Fcons (Fcons (lbeg, lend), Fcdr (old_undo));
--- 4767,4773 ----
  	    {
  	      Lisp_Object tem = XCAR (old_undo);
  	      if (CONSP (tem) && INTEGERP (XCAR (tem)) &&
! 		  INTEGERP (XCDR (tem)) && (XCAR (tem) == lbeg))
  		/* In the non-visiting case record only the final insertion. */
  		current_buffer->undo_list =
  		  Fcons (Fcons (lbeg, lend), Fcdr (old_undo));

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-28 13:12     ` martin rudalics
@ 2008-02-28 16:52       ` Stefan Monnier
  2008-02-28 19:31         ` martin rudalics
  0 siblings, 1 reply; 28+ messages in thread
From: Stefan Monnier @ 2008-02-28 16:52 UTC (permalink / raw)
  To: martin rudalics; +Cc: emacs-devel, Miles Bader

> The (1 . 21) entry is silly and obviously breaks undoing the prior
> (insert "Foo: bar\n").  Although EQ (XCAR (tem), lbeg) was not very
> intelligent, setting this to (XCAR (tem) == lbeg) doesn't seem to help
> either.  Please try again with the attached patch.

I recommend you compile your Emacs with -DUSE_LISP_UNION_TYPE so the
compiler will explain to you where you're going wrong: XCAR (tem) is
a Lisp_Object, not an integer, so it cannot be compared with ==, but
only with EQ (which doesn't strike me as particularly non-intelligent in
this instance).


        Stefan




^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-28  7:46 undo weirdness with insert-file-contents Miles Bader
  2008-02-28  9:56 ` martin rudalics
@ 2008-02-28 17:45 ` Glenn Morris
  2008-02-28 19:35   ` martin rudalics
  2008-02-29  5:50   ` Bill Wohler
  1 sibling, 2 replies; 28+ messages in thread
From: Glenn Morris @ 2008-02-28 17:45 UTC (permalink / raw)
  To: Miles Bader; +Cc: emacs-devel

Miles Bader wrote:

> I define a "message-setup-hook" which for some reason has the unintended
> side-effect of turning off undo in the result message-mode buffer
> [buffer-undo-list has a value of t].

Here's a simpler example of the same thing (?):

http://lists.gnu.org/archive/html/emacs-devel/2008-02/msg02302.html

  From:   Bill Wohler
  Subject:     23.0.60; Disabled undo
  Date:        Sat, 23 Feb 2008 22:07:26 -0800




^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-28 16:52       ` Stefan Monnier
@ 2008-02-28 19:31         ` martin rudalics
  2008-02-28 20:01           ` Miles Bader
  2008-02-28 21:35           ` Stefan Monnier
  0 siblings, 2 replies; 28+ messages in thread
From: martin rudalics @ 2008-02-28 19:31 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel, Miles Bader

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

> I recommend you compile your Emacs with -DUSE_LISP_UNION_TYPE so the
> compiler will explain to you where you're going wrong: XCAR (tem) is
> a Lisp_Object, not an integer, so it cannot be compared with ==, but
> only with EQ (which doesn't strike me as particularly non-intelligent in
> this instance).

Indeed.  Miles please try to debug with the attached patch instead.

[-- Attachment #2: fileio.patch --]
[-- Type: text/plain, Size: 333 bytes --]

*** fileio.c.~1.602.~	Thu Feb 14 20:41:44 2008
--- fileio.c	Thu Feb 28 20:21:18 2008
***************
*** 4674,4680 ****
  
        /* Save old undo list and don't record undo for decoding. */
        old_undo = current_buffer->undo_list;
-       current_buffer->undo_list = Qt;
  
        if (NILP (replace))
  	{
--- 4674,4679 ----

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-28 17:45 ` Glenn Morris
@ 2008-02-28 19:35   ` martin rudalics
  2008-02-28 20:28     ` Glenn Morris
  2008-02-29  5:50   ` Bill Wohler
  1 sibling, 1 reply; 28+ messages in thread
From: martin rudalics @ 2008-02-28 19:35 UTC (permalink / raw)
  To: Glenn Morris; +Cc: Bill Wohler, emacs-devel

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

>>I define a "message-setup-hook" which for some reason has the unintended
>>side-effect of turning off undo in the result message-mode buffer
>>[buffer-undo-list has a value of t].
> 
> 
> Here's a simpler example of the same thing (?):

Thanks for noting.

> http://lists.gnu.org/archive/html/emacs-devel/2008-02/msg02302.html
> 
>   From:   Bill Wohler
>   Subject:     23.0.60; Disabled undo
>   Date:        Sat, 23 Feb 2008 22:07:26 -0800

Bill, please try whether the attached patch works.

[-- Attachment #2: fileio.patch --]
[-- Type: text/plain, Size: 333 bytes --]

*** fileio.c.~1.602.~	Thu Feb 14 20:41:44 2008
--- fileio.c	Thu Feb 28 20:21:18 2008
***************
*** 4674,4680 ****
  
        /* Save old undo list and don't record undo for decoding. */
        old_undo = current_buffer->undo_list;
-       current_buffer->undo_list = Qt;
  
        if (NILP (replace))
  	{
--- 4674,4679 ----

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-28 19:31         ` martin rudalics
@ 2008-02-28 20:01           ` Miles Bader
  2008-02-28 22:19             ` martin rudalics
  2008-02-28 21:35           ` Stefan Monnier
  1 sibling, 1 reply; 28+ messages in thread
From: Miles Bader @ 2008-02-28 20:01 UTC (permalink / raw)
  To: martin rudalics; +Cc: Stefan Monnier, emacs-devel

martin rudalics <rudalics@gmx.at> writes:
> Indeed.  Miles please try to debug with the attached patch instead.

Should that patch replace the one you sent yesterday, or is it in
addition to it?  [sorry I can't actually test it until tomorrow.]

Thanks,

-Miles

-- 
White, adj. and n. Black.




^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-28 19:35   ` martin rudalics
@ 2008-02-28 20:28     ` Glenn Morris
  2008-02-28 22:20       ` martin rudalics
  2008-02-29 22:42       ` martin rudalics
  0 siblings, 2 replies; 28+ messages in thread
From: Glenn Morris @ 2008-02-28 20:28 UTC (permalink / raw)
  To: martin rudalics; +Cc: Bill Wohler, emacs-devel

martin rudalics wrote:

> Bill, please try whether the attached patch works.

Works for me. However the behaviour differs from Emacs 22:

(progn
  (pop-to-buffer "foo")
  (insert-file-contents "/etc/issue")
  (goto-char (point-max))
  (insert-file-contents "/etc/issue"))
M-x undo

now works in both 22 and 23, but in 22 the buffer is marked as not
modified after undo. It 23 it is marked as modified.




^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-28 19:31         ` martin rudalics
  2008-02-28 20:01           ` Miles Bader
@ 2008-02-28 21:35           ` Stefan Monnier
  2008-02-28 22:21             ` martin rudalics
  1 sibling, 1 reply; 28+ messages in thread
From: Stefan Monnier @ 2008-02-28 21:35 UTC (permalink / raw)
  To: martin rudalics; +Cc: emacs-devel, Miles Bader

>> I recommend you compile your Emacs with -DUSE_LISP_UNION_TYPE so the
>> compiler will explain to you where you're going wrong: XCAR (tem) is
>> a Lisp_Object, not an integer, so it cannot be compared with ==, but
>> only with EQ (which doesn't strike me as particularly non-intelligent in
>> this instance).

> Indeed.  Miles please try to debug with the attached patch instead.

> *** fileio.c.~1.602.~	Thu Feb 14 20:41:44 2008
> --- fileio.c	Thu Feb 28 20:21:18 2008
> ***************
> *** 4674,4680 ****
  
>         /* Save old undo list and don't record undo for decoding. */
>         old_undo = current_buffer->undo_list;
> -       current_buffer->undo_list = Qt;
  
But this patch is clearly problematic, if one believe the comment: it
would cause the generation of undo info during the decoding step.
I understand that this info will be thrown away later, but we don't want
to pay the corresponding price.


        Stefan




^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-28 20:01           ` Miles Bader
@ 2008-02-28 22:19             ` martin rudalics
  0 siblings, 0 replies; 28+ messages in thread
From: martin rudalics @ 2008-02-28 22:19 UTC (permalink / raw)
  To: Miles Bader; +Cc: emacs-devel

 > Should that patch replace the one you sent yesterday, or is it in
 > addition to it?  [sorry I can't actually test it until tomorrow.]

It should replace it.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-28 20:28     ` Glenn Morris
@ 2008-02-28 22:20       ` martin rudalics
  2008-02-29 22:42       ` martin rudalics
  1 sibling, 0 replies; 28+ messages in thread
From: martin rudalics @ 2008-02-28 22:20 UTC (permalink / raw)
  To: Glenn Morris; +Cc: emacs-devel

 > (progn
 >   (pop-to-buffer "foo")
 >   (insert-file-contents "/etc/issue")
 >   (goto-char (point-max))
 >   (insert-file-contents "/etc/issue"))
 > M-x undo
 >
 > now works in both 22 and 23, but in 22 the buffer is marked as not
 > modified after undo. It 23 it is marked as modified.

... which is wrong, obviously.

Could you try to debug `insert-file-contents' around the lines 4768-4779
of fileio.c

	      Lisp_Object tem = XCAR (old_undo);
	      if (CONSP (tem) && INTEGERP (XCAR (tem)) &&
		  INTEGERP (XCDR (tem)) && EQ (XCAR (tem), lbeg))
		/* In the non-visiting case record only the final insertion. */
		current_buffer->undo_list =
		  Fcons (Fcons (lbeg, lend), Fcdr (old_undo));
	    }
	}
       else
	/* If undo_list was Qt before, keep it that way.
	   Otherwise start with an empty undo_list. */
	current_buffer->undo_list = EQ (old_undo, Qt) ? Qt : Qnil;

to see where the (t 0 . 0) entry gets removed?  Sorry but I currently
can't debug that myself.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-28 21:35           ` Stefan Monnier
@ 2008-02-28 22:21             ` martin rudalics
  0 siblings, 0 replies; 28+ messages in thread
From: martin rudalics @ 2008-02-28 22:21 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Glenn Morris, emacs-devel, Miles Bader

 >>-       current_buffer->undo_list = Qt;
 >
 > But this patch is clearly problematic, if one believe the comment: it
 > would cause the generation of undo info during the decoding step.
 > I understand that this info will be thrown away later, but we don't want
 > to pay the corresponding price.

The comment is mine, don't trust it.  Emacs 22 records undo information
all through the decoding step.  That's what I wanted to avoid.  But I'm
afraid two insertions get collapsed into one by the undo recording
mechanism and I'm comparing the wrong starting positions.  I don't yet
understand how this happens.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-28 17:45 ` Glenn Morris
  2008-02-28 19:35   ` martin rudalics
@ 2008-02-29  5:50   ` Bill Wohler
  1 sibling, 0 replies; 28+ messages in thread
From: Bill Wohler @ 2008-02-29  5:50 UTC (permalink / raw)
  To: emacs-devel

Glenn Morris <rgm@gnu.org> writes:

> Miles Bader wrote:
>
>> I define a "message-setup-hook" which for some reason has the unintended
>> side-effect of turning off undo in the result message-mode buffer
>> [buffer-undo-list has a value of t].
>
> Here's a simpler example of the same thing (?):
>
> http://lists.gnu.org/archive/html/emacs-devel/2008-02/msg02302.html
>
>   From:   Bill Wohler
>   Subject:     23.0.60; Disabled undo
>   Date:        Sat, 23 Feb 2008 22:07:26 -0800

Thanks, Glenn. Yes, I suspect that it is the same thing. My original
message, however, didn't get quite the same attention as Miles' :-).

Martin, I'll assume that I'd observe the same as Glenn. I'll be happy
to try out new patches.

> now works in both 22 and 23, but in 22 the buffer is marked as not
> modified after undo. It 23 it is marked as modified.

I agree with Martin; the buffer, which is now in its original pristine
state, should not be marked modified.

-- 
Bill Wohler <wohler@newt.com>  http://www.newt.com/wohler/  GnuPG ID:610BD9AD





^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-28 20:28     ` Glenn Morris
  2008-02-28 22:20       ` martin rudalics
@ 2008-02-29 22:42       ` martin rudalics
  2008-03-02  5:02         ` Stefan Monnier
  1 sibling, 1 reply; 28+ messages in thread
From: martin rudalics @ 2008-02-29 22:42 UTC (permalink / raw)
  To: Glenn Morris; +Cc: Miles Bader, Bill Wohler, Stefan Monnier, emacs-devel

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

This is trickier than I thought.  The primary reason of the bug is that
record_insert combines two adjacent insertions thus messing up my
comparison after format decoding wrt the start of the inserted region.
Unless someone has a better idea, I'd handle this by inserting an undo
boundary for `insert-file-contents'.  The second problem is that
`insert-file-contents' does not consider an insertion a buffer change
when the buffer is empty.  This is obviously TRT in the file-visiting
case, but doesn't seem quite right in the non-visiting case.  Both
issues are involved in the buggy behaviors observed.  Please try another
time with the attached patch (I'm afraid it won't be the last).

[-- Attachment #2: fileio.patch --]
[-- Type: text/plain, Size: 1282 bytes --]

*** fileio.c.~1.602.~	Thu Feb 14 20:41:44 2008
--- fileio.c	Fri Feb 29 23:30:18 2008
***************
*** 3730,3735 ****
--- 3730,3736 ----
    int read_quit = 0;
    Lisp_Object old_Vdeactivate_mark = Vdeactivate_mark;
    int we_locked_file = 0;
+   Lisp_Object original_undo_list = current_buffer->undo_list;
  
    if (current_buffer->base_buffer && ! NILP (visit))
      error ("Cannot do file visiting in an indirect buffer");
***************
*** 4589,4594 ****
--- 4590,4610 ----
  	current_buffer->enable_multibyte_characters = Qnil;
      }
  
+   if (inserted > 0
+       && CONSP (current_buffer->undo_list)
+       && (! NILP (XCAR (current_buffer->undo_list))))
+     /* Insert an undo boundary unless there's one here. */
+     current_buffer->undo_list =
+       Fcons (Qnil, current_buffer->undo_list);
+   else if (inserted > 0
+ 	   && (! EQ (original_undo_list, Qt))
+ 	   && (NILP (visit)) && (NILP (replace)))
+     /* Assure that the first insertion is counted as a change. */
+     {
+       current_buffer->undo_list = Qnil;
+       record_first_change ();
+     }
+ 
    coding.dst_multibyte = ! NILP (current_buffer->enable_multibyte_characters);
    if (CODING_MAY_REQUIRE_DECODING (&coding)
        && (inserted > 0 || CODING_REQUIRE_FLUSHING (&coding)))

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-02-29 22:42       ` martin rudalics
@ 2008-03-02  5:02         ` Stefan Monnier
  2008-03-02 12:44           ` martin rudalics
  0 siblings, 1 reply; 28+ messages in thread
From: Stefan Monnier @ 2008-03-02  5:02 UTC (permalink / raw)
  To: martin rudalics; +Cc: Glenn Morris, Miles Bader, Bill Wohler, emacs-devel

> +   if (inserted > 0
> +       && CONSP (current_buffer->undo_list)
> +       && (! NILP (XCAR (current_buffer->undo_list))))
> +     /* Insert an undo boundary unless there's one here. */
> +     current_buffer->undo_list =
> +       Fcons (Qnil, current_buffer->undo_list);

We should use Fundo_boundary here.

> +   else if (inserted > 0
> + 	   && (! EQ (original_undo_list, Qt))
> + 	   && (NILP (visit)) && (NILP (replace)))
> +     /* Assure that the first insertion is counted as a change. */
> +     {
> +       current_buffer->undo_list = Qnil;
> +       record_first_change ();
> +     }

You use `else' between the two, but it's not clear why the two should be
mutually exclusive.  And please mention `visit' in the comment.
Also if the file is empty, is this going to mark the buffer as modified
even though nothing was changed?


        Stefan




^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-03-02  5:02         ` Stefan Monnier
@ 2008-03-02 12:44           ` martin rudalics
  2008-03-02 19:07             ` Stefan Monnier
  0 siblings, 1 reply; 28+ messages in thread
From: martin rudalics @ 2008-03-02 12:44 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Glenn Morris, emacs-devel, Bill Wohler, Miles Bader

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

 >>+     current_buffer->undo_list =
 >>+       Fcons (Qnil, current_buffer->undo_list);
 >
 >
 > We should use Fundo_boundary here.

OK - tho' I'm not sure whether pending_boundary should be considered
here.  Note that I could remove that boundary and also combine the
insertion with a previous one if that's desired.  Personally, I'd prefer
to undo two separate insertions from files separately.  In this context
note that record_insert combines two insertions only if they happen in a
left-to-right way.  IIUC this should be eventually changed when Emacs
goes bi-directional.

 >>+   else if (inserted > 0
 >>+ 	   && (! EQ (original_undo_list, Qt))
 >>+ 	   && (NILP (visit)) && (NILP (replace)))
 >>+     /* Assure that the first insertion is counted as a change. */
 >>+     {
 >>+       current_buffer->undo_list = Qnil;
 >>+       record_first_change ();
 >>+     }
 >
 >
 > You use `else' between the two, but it's not clear why the two should be
 > mutually exclusive.  And please mention `visit' in the comment.

It's hopefully a bit clearer now although I'm not sure whether I need
the empty_undo_list check at all.  BTW, what is the semantics of
`insert-file-contents' with VISIT nil and REPLACE non-nil?

 > Also if the file is empty, is this going to mark the buffer as modified
 > even though nothing was changed?

I'm afraid I don't understand the question - do you mean the case where
inserted equals zero?  I don't handle that.  But maybe I'm missing
something.

[-- Attachment #2: fileio.patch --]
[-- Type: text/plain, Size: 2815 bytes --]

*** fileio.c.~1.602.~	Thu Feb 14 20:41:44 2008
--- fileio.c	Sun Mar  2 12:03:00 2008
***************
*** 3730,3735 ****
--- 3730,3736 ----
    int read_quit = 0;
    Lisp_Object old_Vdeactivate_mark = Vdeactivate_mark;
    int we_locked_file = 0;
+   int empty_undo_list = NILP (current_buffer->undo_list);
  
    if (current_buffer->base_buffer && ! NILP (visit))
      error ("Cannot do file visiting in an indirect buffer");
***************
*** 4593,4598 ****
--- 4594,4622 ----
    if (CODING_MAY_REQUIRE_DECODING (&coding)
        && (inserted > 0 || CODING_REQUIRE_FLUSHING (&coding)))
      {
+       if (CONSP (current_buffer->undo_list)
+ 	  && ! NILP (XCAR (current_buffer->undo_list)))
+ 	/* If the undo list is non-empty and undo information is
+ 	   recorded, insert an undo boundary unless we already have one.
+ 	   This is needed to avoid that record_insert combines the
+ 	   present insertion with an earlier one and consequently the
+ 	   mechanism fails that compares the start position of the
+ 	   insertion before vs that after the format-decode step.  */
+ 	Fundo_boundary ();
+       else if (empty_undo_list
+ 	       && MODIFF <= SAVE_MODIFF
+ 	       && NILP (visit) && NILP (replace))
+ 	/* Else if the present insertion is the very first change of the
+ 	   current buffer, undo information is recorded, and we are
+ 	   neither visiting a file nor replacing buffer contents, make
+ 	   sure we record the insertion as a first change in the
+ 	   undo-list.  This is needed in order to avoid that undoing the
+ 	   insertion leaves the buffer's modified state set.  */
+ 	{
+ 	  current_buffer->undo_list = Qnil;
+ 	  record_first_change ();
+ 	}
+ 
        move_gap_both (PT, PT_BYTE);
        GAP_SIZE += inserted;
        ZV_BYTE -= inserted;
***************
*** 4604,4611 ****
        coding_system = CODING_ID_NAME (coding.id);
      }
    else if (inserted > 0)
!     adjust_after_insert (PT, PT_BYTE, PT + inserted, PT_BYTE + inserted,
! 			 inserted);
  
    /* Now INSERTED is measured in characters.  */
  
--- 4628,4651 ----
        coding_system = CODING_ID_NAME (coding.id);
      }
    else if (inserted > 0)
!     {
!       if (CONSP (current_buffer->undo_list)
! 	  && ! NILP (XCAR (current_buffer->undo_list)))
! 	/* Insert an undo boundary unless there's one here.  See
! 	   explanation above.  */
! 	Fundo_boundary ();
!       else if (empty_undo_list
! 	       && MODIFF <= SAVE_MODIFF
! 	       && NILP (visit) && NILP (replace))
! 	/* Make sure the first insertion is counted as a change.  See
! 	   explanation above.  */
! 	{
! 	  current_buffer->undo_list = Qnil;
! 	  record_first_change ();
! 	}
!       adjust_after_insert (PT, PT_BYTE, PT + inserted, PT_BYTE + inserted,
! 			   inserted);
!     }
  
    /* Now INSERTED is measured in characters.  */
  

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-03-02 12:44           ` martin rudalics
@ 2008-03-02 19:07             ` Stefan Monnier
  2008-03-02 22:05               ` martin rudalics
  2008-03-02 22:18               ` Bill Wohler
  0 siblings, 2 replies; 28+ messages in thread
From: Stefan Monnier @ 2008-03-02 19:07 UTC (permalink / raw)
  To: martin rudalics; +Cc: Glenn Morris, emacs-devel, Bill Wohler, Miles Bader

> the empty_undo_list check at all.  BTW, what is the semantics of
> `insert-file-contents' with VISIT nil and REPLACE non-nil?

It seems obvious to me that it's similar to "delete + insert".
So I guess I don't understand the question quite right.  I use this kind
of call to insert-file-contents in smerge-resolve, if you want to see
an example.

>> Also if the file is empty, is this going to mark the buffer as modified
>> even though nothing was changed?

> I'm afraid I don't understand the question - do you mean the case where
> inserted equals zero?

Yes.

> I don't handle that.

What does that mean?  Are you going to mark the buffer as modified?

> + 	/* Else if the present insertion is the very first change of the
> + 	   current buffer, undo information is recorded, and we are
> + 	   neither visiting a file nor replacing buffer contents, make
> + 	   sure we record the insertion as a first change in the
> + 	   undo-list.  This is needed in order to avoid that undoing the
> + 	   insertion leaves the buffer's modified state set.  */
> + 	{
> + 	  current_buffer->undo_list = Qnil;
> + 	  record_first_change ();
> + 	}

Normally record_first_change is called "automatically" elsewhere.
So why is it needed here?

The handling of the undo-list and modified-p status in
insert-file-contents is a real mess.  It'd be good to take a step back
and think about how it *should* work.  E.g. I don't think
insert-file-contents should mess with modified-p: it should behave just
like a normal `insert' in this respect.


        Stefan




^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-03-02 19:07             ` Stefan Monnier
@ 2008-03-02 22:05               ` martin rudalics
  2008-03-03  2:15                 ` Stefan Monnier
  2008-03-02 22:18               ` Bill Wohler
  1 sibling, 1 reply; 28+ messages in thread
From: martin rudalics @ 2008-03-02 22:05 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

 >>the empty_undo_list check at all.  BTW, what is the semantics of
 >>`insert-file-contents' with VISIT nil and REPLACE non-nil?
 >
 > It seems obvious to me that it's similar to "delete + insert".
 > So I guess I don't understand the question quite right.  I use this kind
 > of call to insert-file-contents in smerge-resolve, if you want to see
 > an example.

I asked because from reading the code I sometimes got the impression
that REPLACE => VISIT.

 >>>Also if the file is empty, is this going to mark the buffer as modified
 >>>even though nothing was changed?
 >
 >>I'm afraid I don't understand the question - do you mean the case where
 >>inserted equals zero?
 >
 > Yes.
 >
 >>I don't handle that.
 >
 > What does that mean?  Are you going to mark the buffer as modified?

Where do I mark a buffer as modified?  What I propose is to insert a
"first change" element in the undo list.  Is it that what you mean?

 > Normally record_first_change is called "automatically" elsewhere.
 > So why is it needed here?

Because it's _not_ done elsewhere.

 > The handling of the undo-list and modified-p status in
 > insert-file-contents is a real mess.

The main reason for the "mess" is that `insert-file-contents' has to
reconcile visiting a file and inserting the contents of some file into
an existing buffer.  In the first case the buffer should appear
unmodified and the undo-list empty after the insertion.  In the second
case the buffer should appear modified and the change recorded in the
undo list.

A second reason stems from the various decoding steps.  Ideally,
decoding should run transparently and only the "final" insertion get
recorded.  For this purpose you have to temporarily switch off undo
recording in the non-visiting case.  Otherwise, undoing changes could
reveal changes done by the decoding routine as can be observed with
Emacs 22.  Moreover, recording changes during decoding might be
expensive.

Finally, functions called by `insert-file-contents', for example,
`decode_coding' and `adjust_after_insert', may modify `buffer-undo-list'
in some way or the other.

 > It'd be good to take a step back
 > and think about how it *should* work.  E.g. I don't think
 > insert-file-contents should mess with modified-p: it should behave just
 > like a normal `insert' in this respect.

Its doc-string says "If second argument VISIT is non-nil, the buffer's
visited filename and last save file modtime are set, and it is marked
unmodified." hence we would have to change that first.

Maybe we could resolve these issues by renaming `insert-file-contents'
to `insert-file-contents-1' and having a new `insert-file-contents'
handle the undo list and the buffer-modified state while calling
`insert-file-contents-1' with undo disabled.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-03-02 19:07             ` Stefan Monnier
  2008-03-02 22:05               ` martin rudalics
@ 2008-03-02 22:18               ` Bill Wohler
  2008-03-03  9:09                 ` martin rudalics
  1 sibling, 1 reply; 28+ messages in thread
From: Bill Wohler @ 2008-03-02 22:18 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: martin rudalics, Miles Bader, emacs-devel, Glenn Morris

Stefan Monnier <monnier@iro.umontreal.ca> wrote:

> The handling of the undo-list and modified-p status in
> insert-file-contents is a real mess.  It'd be good to take a step back
> and think about how it *should* work.  E.g. I don't think
> insert-file-contents should mess with modified-p: it should behave just
> like a normal `insert' in this respect.

I agree. The semantics of insert-file-contents seems to be identical to
insert--only the text comes from a file rather than an argument.

I'd therefore be against Martin's suggestion of inserting an undo
boundary since that should be the caller's responsibility (typically,
the command loop). Consider the following:

    (defun bw-foo ()
      (insert "abc")
      (insert "def"))

    (defun bw-bar ()
      (insert-file-contents "/etc/motd")
      (insert-file-contents "/etc/motd"))

A single undo in either case will remove both insertions and reset
buffer-modified-p to nil if that's what it was beforehand.

There is, regrettably, a large difference between these two. Point is
left where insert-file-contents was run rather than at the end of the
inserted string as is the case with insert. Maybe we should consider
fixing this at this time as well. Maybe this is the way it used to work,
or maybe this is the way people expect it to work: I took a quick look
at the MH-E code and many invocations of insert-file-contents are
immediately followed by (goto-char (point-min)) before the inserted file
is processed.

-- 
Bill Wohler <wohler@newt.com>  http://www.newt.com/wohler/  GnuPG ID:610BD9AD




^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-03-02 22:05               ` martin rudalics
@ 2008-03-03  2:15                 ` Stefan Monnier
  2008-03-03  9:09                   ` martin rudalics
  0 siblings, 1 reply; 28+ messages in thread
From: Stefan Monnier @ 2008-03-03  2:15 UTC (permalink / raw)
  To: martin rudalics; +Cc: emacs-devel

>> What does that mean?  Are you going to mark the buffer as modified?

> Where do I mark a buffer as modified?

I don't know.  I just got the impression that your code might cause this
to happen.  If it doesn't happen, that's great.

> What I propose is to insert a "first change" element in the undo list.
> Is it that what you mean?

I'm only asking about the impact of your change, not the way it works.

>> Normally record_first_change is called "automatically" elsewhere.
>> So why is it needed here?
> Because it's _not_ done elsewhere.

Currently it's done at 3 places: record_property_change, record_point
(both of them are in undo.c) and modify_region in insdel.c.
And record_point is called from record_insert.  So normally it all
happens "automatically".

>> The handling of the undo-list and modified-p status in
>> insert-file-contents is a real mess.

> The main reason for the "mess" is that `insert-file-contents' has to
> reconcile visiting a file and inserting the contents of some file into
> an existing buffer.  In the first case the buffer should appear
> unmodified and the undo-list empty after the insertion.

When visiting a file, insert-file-contents is called from some other
piece of elisp which is the one that should take care of setting up the
undo-list and the unmodified status.  It shouldn't be
insert-file-contents's job.

> A second reason stems from the various decoding steps.  Ideally,
> decoding should run transparently and only the "final" insertion get
> recorded.  For this purpose you have to temporarily switch off undo
> recording in the non-visiting case.  Otherwise, undoing changes could
> reveal changes done by the decoding routine as can be observed with
> Emacs 22.  Moreover, recording changes during decoding might be
> expensive.

I understand this part, but it seems there's got to be a simpler way.

BTW, rather than use an undo-boundary, we could simply store the old
undo-list in some local var, then set undo-list to nil, then do the
dance, then nconc the new undo-list and the old undo-list.  Setting the
undo-list to nil temporarily hides the previous undo-list, thus
preventing merging entries.

>> It'd be good to take a step back and think about how it *should*
>> work.  E.g. I don't think insert-file-contents should mess with
>> modified-p: it should behave just like a normal `insert' in
>> this respect.

> Its doc-string says "If second argument VISIT is non-nil, the buffer's
> visited filename and last save file modtime are set, and it is marked
> unmodified." hence we would have to change that first.
> Maybe we could resolve these issues by renaming `insert-file-contents'
> to `insert-file-contents-1' and having a new `insert-file-contents'
> handle the undo list and the buffer-modified state while calling
> `insert-file-contents-1' with undo disabled.

Or rather create a new insert-file-contents-1 which doesn't take any
`visit' argument.


        Stefan




^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-03-03  2:15                 ` Stefan Monnier
@ 2008-03-03  9:09                   ` martin rudalics
  2008-03-03 21:03                     ` Stefan Monnier
  0 siblings, 1 reply; 28+ messages in thread
From: martin rudalics @ 2008-03-03  9:09 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

 > Currently it's done at 3 places: record_property_change, record_point
 > (both of them are in undo.c) and modify_region in insdel.c.
 > And record_point is called from record_insert.  So normally it all
 > happens "automatically".

Maybe it doesn't happen because the

   if (MODIFF <= SAVE_MODIFF)

check fails.  Anyway, the behavior reported by Glenn in an earlier
posting (and also observed by me) indicates it doesn't happen.

 >>The main reason for the "mess" is that `insert-file-contents' has to
 >>reconcile visiting a file and inserting the contents of some file into
 >>an existing buffer.  In the first case the buffer should appear
 >>unmodified and the undo-list empty after the insertion.
 >
 > When visiting a file, insert-file-contents is called from some other
 > piece of elisp which is the one that should take care of setting up the
 > undo-list and the unmodified status.  It shouldn't be
 > insert-file-contents's job.

Currently `insert-file-contents' is the only routine knowing 'bout
whether it was able to do something with the REPLACE argument.  That's
also why I asked whether VISIT nil/REPLACE non-nil DTRT.  We always have
the dichotomy that from the user's point of view text is replaced from
BEG to END while in fact text was replaced only from REALBEG >= BEG to
REALEND =< REND.  IIUC, the REPLACE stuff was mainly written to make
reverting smoother.  Since buffer reverting throws the undo list away,
optimizing handling of the latter was hardly a concern.

Also note: We never care about text-properties here - hence strictly
spoken the REPLACE stuff _is_ incorrect.  Suppose I visit a file and
assign some text a face property - the buffer is modified.  Next I
revert the buffer and it appears unmodified although the text property
is still there.  In fact the only change caused by reverting was to make
the buffer appear unmodified and the undo list empty.  I know, with
font-locking there's hardly a way to tell whether a text property is
something that should be considered a "buffer change" or just a "hidden
buffer change" as Alan calls it.  Anyway: When you store text properties
on file the current behavior is inherently wrong :-(

I always wondered whether the REPLACE thingy could have been implemented
easier by comparing the region around `point' before and after the
replacement.  Many routines apply such heuristics (bookmarks, etags,
diff related programs).

 >>A second reason stems from the various decoding steps.  Ideally,
 >>decoding should run transparently and only the "final" insertion get
 >>recorded.  For this purpose you have to temporarily switch off undo
 >>recording in the non-visiting case.  Otherwise, undoing changes could
 >>reveal changes done by the decoding routine as can be observed with
 >>Emacs 22.  Moreover, recording changes during decoding might be
 >>expensive.
 >
 > I understand this part, but it seems there's got to be a simpler way.
 >
 > BTW, rather than use an undo-boundary, we could simply store the old
 > undo-list in some local var, then set undo-list to nil, then do the
 > dance,

... dancing with undo enabled?

 > then nconc the new undo-list and the old undo-list.  Setting the
 > undo-list to nil temporarily hides the previous undo-list, thus
 > preventing merging entries.

But _all_ we really need are the beginning and end of the inserted text.
We can get them easily with undo recording turned off during decoding -
once the problems with VISIT and REPLACE have been resolved.

 > Or rather create a new insert-file-contents-1 which doesn't take any
 > `visit' argument.

... but a REPLACE argument, I suppose.  See the example with BEG and
REALBEG above: Undo from BEG or REALBEG?  Don't change the modified
state when the region is (apparently) identic with that on the file?





^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-03-02 22:18               ` Bill Wohler
@ 2008-03-03  9:09                 ` martin rudalics
  0 siblings, 0 replies; 28+ messages in thread
From: martin rudalics @ 2008-03-03  9:09 UTC (permalink / raw)
  To: Bill Wohler; +Cc: Glenn Morris, emacs-devel, Stefan Monnier, Miles Bader

 >>The handling of the undo-list and modified-p status in
 >>insert-file-contents is a real mess.  It'd be good to take a step back
 >>and think about how it *should* work.  E.g. I don't think
 >>insert-file-contents should mess with modified-p: it should behave just
 >>like a normal `insert' in this respect.
 >
 > I agree. The semantics of insert-file-contents seems to be identical to
 > insert--only the text comes from a file rather than an argument.

`insert-file-contents' is used to set up the buffer when visiting a
file.  In that case you hardly want the buffer appear modified after
reading in the file or `point' appear at eob.  Also the whole REPLACE
handling is hardly comparable to what `insert' does (although it's
similar to active region handling with `delete-selection-mode').

 > I'd therefore be against Martin's suggestion of inserting an undo
 > boundary since that should be the caller's responsibility (typically,
 > the command loop). Consider the following:
 >
 >     (defun bw-foo ()
 >       (insert "abc")
 >       (insert "def"))
 >
 >     (defun bw-bar ()
 >       (insert-file-contents "/etc/motd")
 >       (insert-file-contents "/etc/motd"))
 >
 > A single undo in either case will remove both insertions and reset
 > buffer-modified-p to nil if that's what it was beforehand.

I have no opinion here.  But if you insert the second region _before_
the first region you need two undos anway.  Making the behavior depend
on some imaginary left-to-right order is hardly reasonable.  But I agree
that my undo boundary is purely artifical here I should remove it after
it has served its purpose.  A cleaner solution would be to use an extra
variable, say record_insert_no_combine, bind it to Qt in
`insert-file-contents', and have record_insert not combine two adjacent
insertions when this variable is non-nil.  Afterwards I can always
combine the insertions if people want it.

 > There is, regrettably, a large difference between these two. Point is
 > left where insert-file-contents was run rather than at the end of the
 > inserted string as is the case with insert. Maybe we should consider
 > fixing this at this time as well. Maybe this is the way it used to work,
 > or maybe this is the way people expect it to work: I took a quick look
 > at the MH-E code and many invocations of insert-file-contents are
 > immediately followed by (goto-char (point-min)) before the inserted file
 > is processed.

As explained above: The primary use of `insert-file-contents' is to set
up file-visiting buffers not to modify them.





^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-03-03  9:09                   ` martin rudalics
@ 2008-03-03 21:03                     ` Stefan Monnier
  2008-03-07  9:33                       ` martin rudalics
  0 siblings, 1 reply; 28+ messages in thread
From: Stefan Monnier @ 2008-03-03 21:03 UTC (permalink / raw)
  To: martin rudalics; +Cc: emacs-devel

> I always wondered whether the REPLACE thingy could have been implemented
> easier by comparing the region around `point' before and after the
> replacement.  Many routines apply such heuristics (bookmarks, etags,
> diff related programs).

See my wishlist item in the emacsbugs page ;-)

>>> A second reason stems from the various decoding steps.  Ideally,
>>> decoding should run transparently and only the "final" insertion get
>>> recorded.  For this purpose you have to temporarily switch off undo
>>> recording in the non-visiting case.  Otherwise, undoing changes could
>>> reveal changes done by the decoding routine as can be observed with
>>> Emacs 22.  Moreover, recording changes during decoding might be
>>> expensive.
>> 
>> I understand this part, but it seems there's got to be a simpler way.
>> 
>> BTW, rather than use an undo-boundary, we could simply store the old
>> undo-list in some local var, then set undo-list to nil, then do the
>> dance,

> ... dancing with undo enabled?

>> then nconc the new undo-list and the old undo-list.  Setting the
>> undo-list to nil temporarily hides the previous undo-list, thus
>> preventing merging entries.

> But _all_ we really need are the beginning and end of the inserted text.
> We can get them easily with undo recording turned off during decoding -
> once the problems with VISIT and REPLACE have been resolved.

Good, point so we just turn off undo during the dance and then insert
the single undo-element (this is only valid for non-REPLACE, of course).

>> Or rather create a new insert-file-contents-1 which doesn't take any
>> `visit' argument.

> ... but a REPLACE argument, I suppose.  See the example with BEG and
> REALBEG above: Undo from BEG or REALBEG?

The undo-list should record the finer description of the change.


        Stefan




^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-03-03 21:03                     ` Stefan Monnier
@ 2008-03-07  9:33                       ` martin rudalics
  2008-03-07 22:04                         ` Stefan Monnier
  0 siblings, 1 reply; 28+ messages in thread
From: martin rudalics @ 2008-03-07  9:33 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

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

 > Good, point so we just turn off undo during the dance and then insert
 > the single undo-element (this is only valid for non-REPLACE, of course).

Hopefully for VISIT as well - we should not record deletions when
handling the VISIT&REPLACE case.  I see only three cases here:

(1) undo-list was t => leave it alone.

(2) nochange => restore undo-list to previous value.

(3) otherwise => reset undo-list to nil.

I also added a nochange arm for the non-decoding case - please have a
look.  BTW nochange makes an incompatible change to `revert-buffer': You
can no longer use `revert-buffer' to get rid of the undo list.  I think
this should be documented somehow.  Finally, we should document that
REPLACE doesn't handle differences in text properties and tell users to
write their own `revert-buffer-insert-file-contents-function' to handle
them.

martin, who still considers the non-VISIT&REPLACE case obscure and
cannot see any "example" in `smerge-resolve'.

[-- Attachment #2: fileio.patch --]
[-- Type: text/plain, Size: 6265 bytes --]

*** fileio.c	Wed Feb 27 07:49:02 2008
--- fileio.c	Fri Mar  7 10:16:10 2008
***************
*** 3721,3727 ****
    register int unprocessed;
    int count = SPECPDL_INDEX ();
    struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
!   Lisp_Object handler, val, insval, orig_filename, old_undo;
    Lisp_Object p;
    int total = 0;
    int not_regular = 0;
--- 3721,3727 ----
    register int unprocessed;
    int count = SPECPDL_INDEX ();
    struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
!   Lisp_Object handler, val, insval, orig_filename, old_undo_list;
    Lisp_Object p;
    int total = 0;
    int not_regular = 0;
***************
*** 3734,3739 ****
--- 3734,3740 ----
    int read_quit = 0;
    Lisp_Object old_Vdeactivate_mark = Vdeactivate_mark;
    int we_locked_file = 0;
+   int buffer_was_unmodified;
  
    if (current_buffer->base_buffer && ! NILP (visit))
      error ("Cannot do file visiting in an indirect buffer");
***************
*** 3744,3752 ****
    val = Qnil;
    p = Qnil;
    orig_filename = Qnil;
!   old_undo = Qnil;
  
!   GCPRO5 (filename, val, p, orig_filename, old_undo);
  
    CHECK_STRING (filename);
    filename = Fexpand_file_name (filename, Qnil);
--- 3745,3754 ----
    val = Qnil;
    p = Qnil;
    orig_filename = Qnil;
!   old_undo_list = Qnil;
!   buffer_was_unmodified = MODIFF <= SAVE_MODIFF;
  
!   GCPRO5 (filename, val, p, orig_filename, old_undo_list);
  
    CHECK_STRING (filename);
    filename = Fexpand_file_name (filename, Qnil);
***************
*** 3984,3989 ****
--- 3986,3999 ----
        set_coding_system = 1;
      }
  
+   /* Don't record undo information when visiting or not replacing; we
+      restore the undo list manually after the format decode step.  */
+   if (! NILP (visit) || NILP (replace))
+     {
+       old_undo_list = current_buffer->undo_list;
+       current_buffer->undo_list = Qt;
+     }
+ 
    /* If requested, replace the accessible part of the buffer
       with the file contents.  Avoid replacing text at the
       beginning or end of the buffer that matches the file contents;
***************
*** 4067,4074 ****
  	{
  	  emacs_close (fd);
  	  specpdl_ptr--;
! 	  /* Truncate the buffer to the size of the file.  */
! 	  del_range_1 (same_at_start, same_at_end, 0, 0);
  	  goto handled;
  	}
        immediate_quit = 1;
--- 4077,4088 ----
  	{
  	  emacs_close (fd);
  	  specpdl_ptr--;
! 	  if (same_at_start == same_at_end)
! 	    nochange = 1;
! 	  else
! 	    /* Truncate the buffer to the size of the file.  */
! 	    del_range_1 (same_at_start, same_at_end, 0, 0);
! 	  inserted = 0;
  	  goto handled;
  	}
        immediate_quit = 1;
***************
*** 4281,4290 ****
        if (bufpos == inserted)
  	{
  	  specpdl_ptr--;
- 	  /* Truncate the buffer to the size of the file.  */
  	  if (same_at_start == same_at_end)
  	    nochange = 1;
  	  else
  	    del_range_byte (same_at_start, same_at_end, 0);
  	  inserted = 0;
  
--- 4295,4304 ----
        if (bufpos == inserted)
  	{
  	  specpdl_ptr--;
  	  if (same_at_start == same_at_end)
  	    nochange = 1;
  	  else
+ 	    /* Truncate the buffer to the size of the file.  */
  	    del_range_byte (same_at_start, same_at_end, 0);
  	  inserted = 0;
  
***************
*** 4632,4640 ****
  
    if (!NILP (visit))
      {
-       if (!EQ (current_buffer->undo_list, Qt) && !nochange)
- 	current_buffer->undo_list = Qnil;
- 
        if (NILP (handler))
  	{
  	  current_buffer->modtime = st.st_mtime;
--- 4646,4651 ----
***************
*** 4679,4688 ****
        specbind (Qinhibit_point_motion_hooks, Qt);
        specbind (Qinhibit_modification_hooks, Qt);
  
-       /* Save old undo list and don't record undo for decoding. */
-       old_undo = current_buffer->undo_list;
-       current_buffer->undo_list = Qt;
- 
        if (NILP (replace))
  	{
  	  insval = call3 (Qformat_decode,
--- 4690,4695 ----
***************
*** 4766,4793 ****
  	  p = XCDR (p);
  	}
  
!       if (NILP (visit))
  	{
! 	  Lisp_Object lbeg, lend;
! 	  XSETINT (lbeg, PT);
! 	  XSETINT (lend, PT + inserted);
! 	  if (CONSP (old_undo))
  	    {
! 	      Lisp_Object tem = XCAR (old_undo);
! 	      if (CONSP (tem) && INTEGERP (XCAR (tem)) &&
! 		  INTEGERP (XCDR (tem)) && EQ (XCAR (tem), lbeg))
! 		/* In the non-visiting case record only the final insertion. */
! 		current_buffer->undo_list =
! 		  Fcons (Fcons (lbeg, lend), Fcdr (old_undo));
  	    }
  	}
-       else
- 	/* If undo_list was Qt before, keep it that way.
- 	   Otherwise start with an empty undo_list. */
- 	current_buffer->undo_list = EQ (old_undo, Qt) ? Qt : Qnil;
- 
        unbind_to (count, Qnil);
      }
  
    /* Call after-change hooks for the inserted text, aside from the case
       of normal visiting (not with REPLACE), which is done in a new buffer
--- 4773,4813 ----
  	  p = XCDR (p);
  	}
  
!       if (! NILP (visit))
  	{
! 	  if (! EQ (old_undo_list, Qt))
! 	    /* Reset undo list for visiting case.  */
! 	    current_buffer->undo_list =  Qnil;
! 	}
!       else if (NILP (replace) && ! EQ (old_undo_list, Qt))
! 	/* For the non-visiting, non-replacing case record the last
! 	   insertion only.  This avoids that intermediate steps get
! 	   recorded and show up during undo.  */
! 	{
! 	  current_buffer->undo_list = old_undo_list;
! 	  if (inserted > 0)
  	    {
! 	      if (buffer_was_unmodified)
! 		/* If the buffer was unmodified when this function was
! 		   called record a first buffer change (this is *not*
! 		   done by the record_insert below since the buffer is
! 		   already modified).  */
! 		record_first_change ();
! 	      record_insert (PT, inserted);
  	    }
  	}
        unbind_to (count, Qnil);
      }
+   else if (! NILP (visit))
+     {
+       if (! EQ (old_undo_list, Qt))
+ 	/* For the visiting case restore old undo list iff replacing and
+ 	   nothing changed.  Otherwise start with empty undo list.  */
+ 	current_buffer->undo_list = nochange ? old_undo_list : Qnil;
+     }
+   else if (NILP (replace))
+     /* Restore old undo list in non-visiting, non-replacing case.  */
+     current_buffer->undo_list = old_undo_list;
  
    /* Call after-change hooks for the inserted text, aside from the case
       of normal visiting (not with REPLACE), which is done in a new buffer

^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-03-07  9:33                       ` martin rudalics
@ 2008-03-07 22:04                         ` Stefan Monnier
  2008-03-08  9:55                           ` martin rudalics
  0 siblings, 1 reply; 28+ messages in thread
From: Stefan Monnier @ 2008-03-07 22:04 UTC (permalink / raw)
  To: martin rudalics; +Cc: emacs-devel

> martin, who still considers the non-VISIT&REPLACE case obscure and
> cannot see any "example" in `smerge-resolve'.

Ahem... sorry, it's in the part of smerge-resolve that I haven't
yet installed.  Basically what it does is this:

given a 3-way merge conflict, with 3 branches MINE/BASE/OTHER, I do
a narrow-to-region on the conflict, then I save all three branches of
the conflict to separate files and do:

   diff -c -w BASE MINE | patch OTHER >RESULT

and if the patch succeeds I do `insert-file-contents' of RESULT without
VISIT but with REPLACE.

It's indeed somewhat obscure, and indeed, it crashed Emacs when the
Unicode branch was merged (because the code didn't expect REPLACE in
a narrowed buffer).


        Stefan




^ permalink raw reply	[flat|nested] 28+ messages in thread

* Re: undo weirdness with insert-file-contents
  2008-03-07 22:04                         ` Stefan Monnier
@ 2008-03-08  9:55                           ` martin rudalics
  0 siblings, 0 replies; 28+ messages in thread
From: martin rudalics @ 2008-03-08  9:55 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

 > given a 3-way merge conflict, with 3 branches MINE/BASE/OTHER, I do
 > a narrow-to-region on the conflict, then I save all three branches of
 > the conflict to separate files and do:
 >
 >    diff -c -w BASE MINE | patch OTHER >RESULT
 >
 > and if the patch succeeds I do `insert-file-contents' of RESULT without
 > VISIT but with REPLACE.
 >
 > It's indeed somewhat obscure, and indeed, it crashed Emacs when the
 > Unicode branch was merged (because the code didn't expect REPLACE in
 > a narrowed buffer).

With the MINE buffer current, I suppose, to avoid visiting RESULT,
inserting it into MINE, and deleting it afterwards.  As a benefit,
`insert-file-contents' will preserve markers within the conflict region
and store less undo information.  Sounds reasonable.

The examples of non-VISIT&REPLACE I've found in the Emacs sources seem
rather obscure though.





^ permalink raw reply	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2008-03-08  9:55 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-02-28  7:46 undo weirdness with insert-file-contents Miles Bader
2008-02-28  9:56 ` martin rudalics
2008-02-28 11:01   ` Miles Bader
2008-02-28 13:12     ` martin rudalics
2008-02-28 16:52       ` Stefan Monnier
2008-02-28 19:31         ` martin rudalics
2008-02-28 20:01           ` Miles Bader
2008-02-28 22:19             ` martin rudalics
2008-02-28 21:35           ` Stefan Monnier
2008-02-28 22:21             ` martin rudalics
2008-02-28 17:45 ` Glenn Morris
2008-02-28 19:35   ` martin rudalics
2008-02-28 20:28     ` Glenn Morris
2008-02-28 22:20       ` martin rudalics
2008-02-29 22:42       ` martin rudalics
2008-03-02  5:02         ` Stefan Monnier
2008-03-02 12:44           ` martin rudalics
2008-03-02 19:07             ` Stefan Monnier
2008-03-02 22:05               ` martin rudalics
2008-03-03  2:15                 ` Stefan Monnier
2008-03-03  9:09                   ` martin rudalics
2008-03-03 21:03                     ` Stefan Monnier
2008-03-07  9:33                       ` martin rudalics
2008-03-07 22:04                         ` Stefan Monnier
2008-03-08  9:55                           ` martin rudalics
2008-03-02 22:18               ` Bill Wohler
2008-03-03  9:09                 ` martin rudalics
2008-02-29  5:50   ` Bill Wohler

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).