From: Alan Mackenzie <acm@muc.de>
To: Stefan Monnier <monnier@IRO.UMontreal.CA>
Cc: emacs-devel@gnu.org
Subject: Re: An idea: combine-change-calls
Date: Fri, 30 Mar 2018 11:46:36 +0000 [thread overview]
Message-ID: <20180330114636.GA5432@ACM> (raw)
In-Reply-To: <jwv7epu7lm3.fsf-monnier+emacs@gnu.org>
Hello, Stefan.
On Thu, Mar 29, 2018 at 15:10:47 -0400, Stefan Monnier wrote:
> > Right, I think I've got it now. The macro combine-change-calls would
> > maybe first push some sort of sentinal element onto buffer-undo-list,
> > then let ,@forms run, then massage the list so that the 2000 elements
> > created by ,@forms would become part of the `apply' element.
> Right. Now you can re-read the pseudo-code I had sent:
> (let ((elem-apply `(apply 0 ,beg ,end ,#'my-undo-combining nil)))
> (push elem-apply buffer-undo-list)
> (funcall body)
> (let ((new-bul (memq elem-apply buffer-undo-list)))
> (when new-bul
> (let ((undo-elems buffer-undo-list))
> (setf (nthcdr (- (length undo-elems) (length new-bul))
> undo-elems)
> nil)
> (setf (nth 1 elem-apply) (- end-marker end))
> (setf (nth 3 elem-apply) (marker-position end-marker))
> (setf (nth 5 elem-apply) undo-elems)
> (setq buffer-undo-list new-bul)))))
> and then
> (defun my-undo-combining (undo-elems)
> (let ((inhibit-modification-hooks t))
> (while t
> (primitive-undo 1 undo-elems))))
I've got a first version of some real working code. Not to be forgotten
is the need for my-undo-combining (which I've called
wrap-and-run-primitive-undo) itself to generate an (apply ...
my-undo-combining ...) for the use of redo.
Here's that code. It badly needs doc strings and commenting, and there
are one or two safety checks not yet there. But it is working code.
What do you think?
(defun wrap-and-run-primitive-undo (beg end list)
(let ((old-bul buffer-undo-list)
(end-marker (copy-marker end)))
(if (not inhibit-modification-hooks)
(run-hook-with-args 'before-change-functions beg end))
(let ((inhibit-modification-hooks t))
(funcall #'primitive-undo 1 list))
(unless (eq buffer-undo-list t)
(let ((ap-elt
(list 'apply
(- end end-marker)
beg
(marker-position end-marker)
#'wrap-and-run-primitive-undo
beg (marker-position end-marker) buffer-undo-list))
(ptr buffer-undo-list))
(if (not (eq buffer-undo-list old-bul))
(progn
(while (not (eq (cdr ptr) old-bul))
(setq ptr (cdr ptr)))
(setcdr ptr nil)
(push ap-elt buffer-undo-list)
(setcdr buffer-undo-list old-bul)))))
(if (not inhibit-modification-hooks)
(run-hook-with-args 'after-change-functions
beg (marker-position end-marker)
(- end beg)))))
(defmacro combine-change-calls (beg end &rest forms)
`(let ((-beg- ,beg)
(-end- ,end)
(body (lambda () ,@forms))
(old-bul buffer-undo-list)
end-marker)
(setq end-marker (copy-marker -end-))
(if (not inhibit-modification-hooks)
(run-hook-with-args 'before-change-functions -beg- -end-))
(if (eq buffer-undo-list t)
(progn
(funcall body)
(if (not inhibit-modification-hooks)
(run-hook-with-args 'after-change-functions
-beg- (marker-position end-marker)
(- -end- -beg-))))
(let ((inhibit-modification-hooks t))
(funcall body))
(let ((ap-elt
(list 'apply
(- -end- end-marker) ; DELTA
-beg-
(marker-position end-marker)
#'wrap-and-run-primitive-undo
-beg- (marker-position end-marker) buffer-undo-list))
(ptr buffer-undo-list))
(if (not (eq buffer-undo-list old-bul))
(progn
(while (not (eq (cdr ptr) old-bul))
(setq ptr (cdr ptr)))
(setcdr ptr nil)
(push ap-elt buffer-undo-list)
(setcdr buffer-undo-list old-bul)))))
(if (not inhibit-modification-hooks)
(run-hook-with-args 'after-change-functions
-beg- (marker-position end-marker)
(- -end- -beg-)))
(setq end-marker nil)))
As usual, comments and criticism of the above would be welcome.
[ .... ]
> I could imagine a macro `with-inhibit-read-only` which would bind
> inhibit-read-only to t and then tweak the buffer-undo-list similarly to
> what you're doing such that the undo is also performed with
> inhibit-read-only bound to t.
OK, thanks. The above code hasn't taken this into account
[ .... ]
> I'm not interested in destroying this information. I'm just trying to
> solve the problem at hand and it's a simple and efficient solution, and
> neither you nor I can come up with a scenario where this simpler
> solution is worse, apparently.
Well, I've wanted to see this info whilst writing c-c-c.
> > Its continued existence does not cause any disadvantage. The run time
> > which would be saved in `undo' is minimal and inconsequential.
> Agreed, the gain is in the simplicity of combine-change-calls.
I can definitely see that. ;-)
[ .... ]
> >> IIUC we agree that this is considered an unimportant use-case and it's
> >> OK to just ignore such boundaries.
> > I suppose so. What would such a boundary mean anyway? We're talking
> > about a scenario where all the change hook invocations are amalgamated.
> > How could it make sense to split that up?
> Some commands explicitly push undo boundaries in the middle of their
> execution, so that the user can easily undo the 2nd half of the
> command's execution, for example.
> This is rare enough that I think it's perfectly OK to say that such
> use-cases are not supported by combine-change-calls. [ It's also the
> only use-case I could think of where using a simple pair of
> "delete+insert" undo entries would have made a difference. ]
To be mulled over.
> Stefan
--
Alan Mackenzie (Nuremberg, Germany).
next prev parent reply other threads:[~2018-03-30 11:46 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-03-24 13:50 An idea: combine-change-calls Alan Mackenzie
2018-03-24 22:18 ` Stefan Monnier
2018-03-25 19:14 ` Alan Mackenzie
2018-03-25 20:05 ` Stefan Monnier
2018-03-26 20:17 ` Alan Mackenzie
2018-03-26 21:07 ` Stefan Monnier
2018-03-27 16:58 ` Alan Mackenzie
2018-03-27 18:30 ` Stefan Monnier
2018-03-27 19:45 ` Alan Mackenzie
2018-03-27 20:24 ` Stefan Monnier
2018-03-28 20:42 ` Alan Mackenzie
2018-03-28 21:26 ` Stefan Monnier
2018-03-29 15:10 ` Alan Mackenzie
2018-03-29 15:40 ` Stefan Monnier
2018-03-29 17:11 ` Alan Mackenzie
2018-03-29 19:10 ` Stefan Monnier
2018-03-30 11:46 ` Alan Mackenzie [this message]
2018-03-30 15:05 ` Stefan Monnier
2018-03-31 21:00 ` Alan Mackenzie
2018-03-31 23:38 ` Stefan Monnier
2018-04-01 14:24 ` Alan Mackenzie
2018-04-01 19:22 ` Stefan Monnier
2018-03-30 9:12 ` Johan Bockgård
2018-03-30 13:04 ` Stefan Monnier
2018-04-02 16:25 ` Alan Mackenzie
2018-04-02 17:52 ` Johan Bockgård
2018-04-03 0:41 ` Stefan Monnier
2018-04-03 1:43 ` Clément Pit-Claudel
2018-04-03 3:15 ` Richard Stallman
2018-03-26 21:09 ` Stefan Monnier
2018-03-27 0:36 ` Stefan Monnier
2018-03-27 17:00 ` Alan Mackenzie
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=20180330114636.GA5432@ACM \
--to=acm@muc.de \
--cc=emacs-devel@gnu.org \
--cc=monnier@IRO.UMontreal.CA \
/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).