From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Alan Mackenzie Newsgroups: gmane.emacs.devel Subject: Re: An idea: combine-change-calls Date: Fri, 30 Mar 2018 11:46:36 +0000 Message-ID: <20180330114636.GA5432@ACM> References: <20180327165816.GB4105@ACM> <20180327194507.GD4105@ACM> <20180328204254.GC6592@ACM> <20180329151033.GA5213@ACM> <20180329171101.GB5213@ACM> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: blaine.gmane.org 1522410342 15625 195.159.176.226 (30 Mar 2018 11:45:42 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Fri, 30 Mar 2018 11:45:42 +0000 (UTC) User-Agent: Mutt/1.7.2 (2016-11-26) Cc: emacs-devel@gnu.org To: Stefan Monnier Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Fri Mar 30 13:45:37 2018 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1f1sTQ-0003vN-Az for ged-emacs-devel@m.gmane.org; Fri, 30 Mar 2018 13:45:36 +0200 Original-Received: from localhost ([::1]:57276 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1f1sVT-0003ft-HK for ged-emacs-devel@m.gmane.org; Fri, 30 Mar 2018 07:47:43 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:44413) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1f1sUW-0003fF-J1 for emacs-devel@gnu.org; Fri, 30 Mar 2018 07:46:48 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1f1sUT-0007xu-Ao for emacs-devel@gnu.org; Fri, 30 Mar 2018 07:46:44 -0400 Original-Received: from colin.muc.de ([193.149.48.1]:14571 helo=mail.muc.de) by eggs.gnu.org with smtp (Exim 4.71) (envelope-from ) id 1f1sUT-0007vk-09 for emacs-devel@gnu.org; Fri, 30 Mar 2018 07:46:41 -0400 Original-Received: (qmail 1530 invoked by uid 3782); 30 Mar 2018 11:46:38 -0000 Original-Received: from acm.muc.de (p5B14734F.dip0.t-ipconnect.de [91.20.115.79]) by colin.muc.de (tmda-ofmipd) with ESMTP; Fri, 30 Mar 2018 13:46:37 +0200 Original-Received: (qmail 5477 invoked by uid 1000); 30 Mar 2018 11:46:36 -0000 Content-Disposition: inline In-Reply-To: X-Delivery-Agent: TMDA/1.1.12 (Macallan) X-Primary-Address: acm@muc.de X-detected-operating-system: by eggs.gnu.org: FreeBSD 9.x [fuzzy] X-Received-From: 193.149.48.1 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:224177 Archived-At: 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).