From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Yuan Fu Newsgroups: gmane.emacs.help Subject: A ton of marker entry in buffer-und-list Date: Thu, 25 Feb 2021 23:32:33 -0500 Message-ID: <72F34690-3033-4CC5-8AFC-89C6158AA65D@gmail.com> Mime-Version: 1.0 (Mac OS X Mail 14.0 \(3654.60.0.2.21\)) Content-Type: multipart/mixed; boundary="Apple-Mail=_6FFD4590-9D66-4134-A074-D2DA8DFD4244" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="35809"; mail-complaints-to="usenet@ciao.gmane.io" To: help-gnu-emacs Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Fri Feb 26 05:35:57 2021 Return-path: Envelope-to: geh-help-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1lFUr6-0009Db-JC for geh-help-gnu-emacs@m.gmane-mx.org; Fri, 26 Feb 2021 05:35:56 +0100 Original-Received: from localhost ([::1]:54632 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lFUr5-0008DX-Kw for geh-help-gnu-emacs@m.gmane-mx.org; Thu, 25 Feb 2021 23:35:55 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:48166) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lFUnw-0005jb-AG for help-gnu-emacs@gnu.org; Thu, 25 Feb 2021 23:32:41 -0500 Original-Received: from mail-qk1-x732.google.com ([2607:f8b0:4864:20::732]:42397) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1lFUnt-0006Xl-9q for help-gnu-emacs@gnu.org; Thu, 25 Feb 2021 23:32:39 -0500 Original-Received: by mail-qk1-x732.google.com with SMTP id z190so7996676qka.9 for ; Thu, 25 Feb 2021 20:32:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:mime-version:subject:message-id:date:to; bh=qE+q6r9yPQFsgnnPBvcpTWIfJ9QNEf0DfxMJKDjmgRE=; b=h1Br0rwq6gcYI61ocb7sHKUZfBel0sZC8MnLu/TS8/wgpg358CObFjm0/9nUbRDu10 3GaxmPdJTbSN6C3MltznkD6nPfAgYhKaz+r++BOzFxJGYzkYKAQ2oCG8tP3L5qD39su4 42kD0SoSJ5MakP3t0QDyW3hIBJhKpVgbtQ+2gPKJADfbuJh2A3BsVH5Gavk3EZ9l7qIj IuH9TCV+aciRo4zN5fr+rptrlpo8plam+dTFEDqZ8rq4753tujc0n/QTeD4sHimH1uKK ZkCh0blpjk6JWuzgQuoaz5u6CV3dxGF1If8+I/LphzLEeCvJ89sVGsTrxEmO09idxwqU Ac7Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:mime-version:subject:message-id:date:to; bh=qE+q6r9yPQFsgnnPBvcpTWIfJ9QNEf0DfxMJKDjmgRE=; b=eZ+CpMVeY/du9Lr4BdzYaZN7gnNXGDv3c8BsPhaHp3lrpF7HsoCOPJOBRPbJgD0hJp vct2Ncmq+EJqsN8fsKdWk2CnyTtD4TZKuIpoMN56VPe8RDBsh2Ibu9TjIusHHb8GCd77 tqPF53pSLl2/9YyRwWdMFoUHm3INbnG3lq1h88zc/DCuViJ7LG46P5Bk0ZEyztHQK/aT XGFgYHDs/EiHbk2wrI8TUgJnTtQEpWed/lkf0+fo0OHw1cs2IjYAQmeqcHC2lCPruyYo 6M3V+ExmwvhfDsnLnE/1fh/kcmqPpDzMTqKoC7THuDbSRJXZ7bOl+Mft2Q1uKs2CbYrI geeQ== X-Gm-Message-State: AOAM533Wgk98fc/CO4ywknTD2G13OWaP3Qq8TfQkipB6vIsiihNbCTR0 691Z55ea9K/Y0N8uCdMa21Mkof6UYyMF1w== X-Google-Smtp-Source: ABdhPJw2uZIvBz1urTnC5Me3XyuaJFeP3I3/CzPmFYw6sGpQ4D8dyrzg+1ZEnA6DSimTjDNnwHsaKw== X-Received: by 2002:a05:620a:1e5:: with SMTP id x5mr1010209qkn.369.1614313955218; Thu, 25 Feb 2021 20:32:35 -0800 (PST) Original-Received: from ?IPv6:2601:98a:4102:3d80:8c01:636f:df47:cf9b? ([2601:98a:4102:3d80:8c01:636f:df47:cf9b]) by smtp.gmail.com with ESMTPSA id 184sm5620006qki.97.2021.02.25.20.32.34 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 25 Feb 2021 20:32:34 -0800 (PST) X-Mailer: Apple Mail (2.3654.60.0.2.21) Received-SPF: pass client-ip=2607:f8b0:4864:20::732; envelope-from=casouri@gmail.com; helo=mail-qk1-x732.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "help-gnu-emacs" Xref: news.gmane.io gmane.emacs.help:128271 Archived-At: --Apple-Mail=_6FFD4590-9D66-4134-A074-D2DA8DFD4244 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 I=E2=80=99ve been playing around with undo recently. I used = primitive-undo to undo some entries in the buffer-undo-list, and a ton = of (#marker . -1) was added to the buffer-undo-list: (nil ("a" . 27) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) (# . -1) =E2=80=A6 hundreds of this. Any idea of what is happening? What is this marker? I don=E2=80=99t expect anyone to look at my code, but if you want to see = it, here it is: --Apple-Mail=_6FFD4590-9D66-4134-A074-D2DA8DFD4244 Content-Disposition: attachment; filename=vundo.el Content-Type: application/octet-stream; x-unix-mode=0644; name="vundo.el" Content-Transfer-Encoding: quoted-printable ;;;=20vundo.el=20---=20Visual=20undo=20tree=20=20=20=20=20=20-*-=20= lexical-binding:=20t;=20-*-=0A=0A;;=20Author:=20Yuan=20Fu=20= =0A=0A;;;=20This=20file=20is=20NOT=20part=20of=20GNU=20= Emacs=0A=0A;;;=20Commentary:=0A;;=0A=0A;;;=20Code:=0A;;=0A=0A(require=20= 'pcase)=0A(require=20'cl-lib)=0A(require=20'seq)=0A=0A(defun=20= vundo--setup-test-buffer=20()=0A=20=20"Setup=20and=20pop=20a=20testing=20= buffer.=0ATYPE=20is=20the=20type=20of=20buffer=20you=20want."=0A=20=20= (interactive)=0A=20=20(let=20((buf=20(get-buffer=20"*vundo-test*")))=0A=20= =20=20=20(if=20buf=20(kill-buffer=20buf))=0A=20=20=20=20(setq=20buf=20= (get-buffer-create=20"*vundo-test*"))=0A=20=20=20=20(pop-to-buffer=20= buf)))=0A=0A;;;=20Undo=20list=20to=20mod=20list=0A=0A(cl-defstruct=20= vundo-m=0A=20=20"A=20modification=20in=20undo=20history.=0AThis=20object=20= serves=20two=20purpose:=20it=20represents=20a=20modification=20in=0Aundo=20= history,=20and=20it=20also=20represents=20the=20buffer=20state=20after=20= the=0Amodification."=0A=20=20(idx=0A=20=20=20nil=0A=20=20=20:type=20= integer=0A=20=20=20:documentation=20"The=20index=20of=20this=20= modification=20in=20history.")=0A=20=20(children=0A=20=20=20nil=0A=20=20=20= :type=20proper-list=0A=20=20=20:documentation=20"Children=20in=20tree.")=0A= =20=20(parent=0A=20=20=20nil=0A=20=20=20:type=20vundo-m=0A=20=20=20= :documentation=20"Parent=20in=20tree.")=0A=20=20(prev-eqv=0A=20=20=20nil=0A= =20=20=20:type=20vundo-m=0A=20=20=20:documentation=20"The=20previous=20= equivalent=20state.")=0A=20=20(next-eqv=0A=20=20=20nil=0A=20=20=20:type=20= vundo-m=0A=20=20=20:documentation=20"The=20next=20equivalent=20state.")=0A= =20=20(undo-list=0A=20=20=20nil=0A=20=20=20:type=20cons=0A=20=20=20= :documentation=20"The=20undo-list=20at=20this=20modification.")=0A=20=20= (point=0A=20=20=20nil=0A=20=20=20:type=20integer=0A=20=20=20= :documentation=20"Marks=20the=20text=20node=20in=20the=20vundo=20buffer=20= if=20drawn."))=0A=0A(defun=20vundo--mod-list-from=20(undo-list=20= &optional=20n=20mod-list)=0A=20=20"Generate=20and=20return=20a=20= modification=20list=20from=20UNDO-LIST.=0AIf=20N=20non-nil,=20only=20= look=20at=20the=20first=20N=20entries=20in=20UNDO-LIST.=0AIf=20MOD-LIST=20= non-nil,=20extend=20on=20MOD-LIST."=0A=20=20(let=20((bound=20(or=20n=20= (length=20undo-list)))=0A=20=20=20=20=20=20=20=20(uidx=200)=0A=20=20=20=20= =20=20=20=20(mod-list=20(or=20mod-list=20(list=20(make-vundo-m))))=0A=20=20= =20=20=20=20=20=20new-mlist)=0A=20=20=20=20(while=20(and=20(consp=20= undo-list)=20(<=20uidx=20bound))=0A=20=20=20=20=20=20;;=20Skip=20leading=20= nils.=0A=20=20=20=20=20=20(while=20(and=20(<=20uidx=20bound)=20(null=20= (nth=20uidx=20undo-list)))=0A=20=20=20=20=20=20=20=20(cl-incf=20uidx))=0A= =20=20=20=20=20=20;;=20Add=20modification.=0A=20=20=20=20=20=20(if=20(<=20= uidx=20bound)=0A=20=20=20=20=20=20=20=20=20=20(push=20(make-vundo-m=20= :undo-list=20(nthcdr=20uidx=20undo-list))=0A=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20new-mlist))=0A=20=20=20=20=20=20;;=20Skip=20through=20= the=20content=20of=20this=20modification.=0A=20=20=20=20=20=20(while=20= (nth=20uidx=20undo-list)=0A=20=20=20=20=20=20=20=20(cl-incf=20uidx)))=0A=20= =20=20=20(append=20mod-list=20new-mlist)))=0A=0A(defun=20= vundo--update-mapping=20(mod-list=20&optional=20hash-table=20n)=0A=20=20= "Update=20each=20modification=20in=20MOD-LIST.=0AAdd=20:idx=20for=20each=20= modification,=20map=20:undo-list=20back=20to=20each=0Amodification=20in=20= HASH-TABLE.=20If=20N=20non-nil,=20start=20from=20the=20Nth=0A= modification=20in=20MOD-LIST.=20Return=20HASH-TABLE."=0A=20=20(let=20= ((hash-table=20(or=20hash-table=0A=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20(make-hash-table=0A=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20:test=20#'eq=20= :weakness=20t=20:size=20200))))=0A=20=20=20=20(cl-loop=20for=20mod=20in=20= (nthcdr=20(or=20n=200)=20mod-list)=0A=20=20=20=20=20=20=20=20=20=20=20=20= =20for=20midx=20=3D=20(or=20n=200)=20then=20(1+=20midx)=0A=20=20=20=20=20= =20=20=20=20=20=20=20=20do=20(cl-assert=20(null=20(vundo-m-idx=20mod)))=0A= =20=20=20=20=20=20=20=20=20=20=20=20=20do=20(cl-assert=20(null=20= (gethash=20(vundo-m-undo-list=20mod)=0A=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20hash-table)))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20do=20= (setf=20(vundo-m-idx=20mod)=20midx)=0A=20=20=20=20=20=20=20=20=20=20=20=20= =20do=20(puthash=20(vundo-m-undo-list=20mod)=20mod=20hash-table))=0A=20=20= =20=20hash-table))=0A=0A;;=20(setq=20lst=20'(6=205=204=20nil=203=202=20= 1))=0A=0A;;=20(setq=20con=20(vundo--mod-list-from=20lst))=0A;;=20(setq=20= mlist=20(car=20con))=0A;;=20(setq=20hash=20(cdr=20con))=0A=0A;;=20(setq=20= newlst=20(append=20'(9=208=207=20nil)=20lst))=0A;;=20(setq=20ncon=20= (vundo--mod-list-incr=20newlst=20mlist=20hash))=0A;;=20(setq=20nmlist=20= (car=20ncon))=0A;;=20(setq=20nhash=20(cdr=20ncon))=0A=0A;;=20(setq=20= nnlst=20(nthcdr=204=20lst))=0A;;=20(setq=20nncon=20(vundo--mod-list-incr=20= nnlst=20mlist=20hash))=0A;;=20(setq=20nnlist=20(car=20nncon))=0A;;=20= (setq=20nhash=20(cdr=20ncon))=0A=0A=0A;;;=20Mod=20list=20to=20eqv=20list=0A= =0A(defun=20vundo--eqv-list-of=20(mod)=0A=20=20"Return=20all=20the=20= modifications=20equivalent=20to=20MOD."=0A=20=20(while=20= (vundo-m-prev-eqv=20mod)=0A=20=20=20=20(cl-assert=20(not=20(eq=20mod=20= (vundo-m-prev-eqv=20mod))))=0A=20=20=20=20(setq=20mod=20= (vundo-m-prev-eqv=20mod)))=0A=20=20;;=20At=20the=20first=20mod=20in=20= the=20equiv=20chain.=0A=20=20(let=20((eqv-list=20(list=20mod)))=0A=20=20=20= =20(while=20(vundo-m-next-eqv=20mod)=0A=20=20=20=20=20=20(cl-assert=20= (not=20(eq=20mod=20(vundo-m-next-eqv=20mod))))=0A=20=20=20=20=20=20(setq=20= mod=20(vundo-m-next-eqv=20mod))=0A=20=20=20=20=20=20(push=20mod=20= eqv-list))=0A=20=20=20=20(reverse=20eqv-list)))=0A=0A(defun=20= vundo--eqv-merge=20(mlist)=0A=20=20"Connect=20modifications=20in=20MLIST=20= to=20be=20in=20the=20same=20equivalence=20list.=0AOrder=20is=20= reserved."=0A=20=20(cl-loop=20for=20idx=20from=200=20to=20(1-=20(length=20= mlist))=0A=20=20=20=20=20=20=20=20=20=20=20for=20this=20=3D=20(nth=20idx=20= mlist)=0A=20=20=20=20=20=20=20=20=20=20=20for=20next=20=3D=20(nth=20(1+=20= idx)=20mlist)=0A=20=20=20=20=20=20=20=20=20=20=20for=20prev=20=3D=20nil=20= then=20(nth=20(1-=20idx)=20mlist)=0A=20=20=20=20=20=20=20=20=20=20=20do=20= (setf=20(vundo-m-prev-eqv=20this)=20prev)=0A=20=20=20=20=20=20=20=20=20=20= =20do=20(setf=20(vundo-m-next-eqv=20this)=20next)))=0A=0A(defun=20= vundo--sort-mod=20(mlist=20&optional=20reverse)=0A=20=20"Return=20sorted=20= modifications=20in=20MLIST=20by=20their=20idx...=0A...in=20ascending=20= order.=20If=20REVERSE=20non-nil,=20sort=20in=20descending=0Aorder."=0A=20= =20(seq-sort=20(if=20reverse=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20(lambda=20(m1=20m2)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20(>=20(vundo-m-idx=20m1)=20(vundo-m-idx=20m2)))=0A=20=20=20=20=20= =20=20=20=20=20=20=20=20=20(lambda=20(m1=20m2)=0A=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20(<=20(vundo-m-idx=20m1)=20(vundo-m-idx=20m2))))=0A= =20=20=20=20=20=20=20=20=20=20=20=20mlist))=0A=0A(defun=20= vundo--eqv-merge-mod=20(m1=20m2)=0A=20=20"Put=20M1=20and=20M2=20into=20= the=20same=20equivalence=20list."=0A=20=20(let=20((l1=20= (vundo--eqv-list-of=20m1))=0A=20=20=20=20=20=20=20=20(l2=20= (vundo--eqv-list-of=20m2)))=0A=20=20=20=20(vundo--eqv-merge=20= (vundo--sort-mod=20(cl-union=20l1=20l2)))))=0A=0A(defun=20= vundo--build-tree=20(mod-list=20mod-hash=20&optional=20from)=0A=20=20= "Connect=20equivalent=20modifications=20and=20build=20the=20tree=20in=20= MOD-LIST.=0AMOD-HASH=20maps=20undo-lists=20to=20modifications.=0AIf=20= FROM=20non-nil,=20build=20from=20FORM-th=20modification=20in=20= MOD-LIST."=0A=20=20(cl-loop=0A=20=20=20for=20m=20from=20(or=20from=200)=20= to=20(1-=20(length=20mod-list))=0A=20=20=20for=20mod=20=3D=20(nth=20m=20= mod-list)=0A=20=20=20;;=20If=20MOD=20is=20an=20undo,=20the=20buffer=20= state=20it=20represents=20is=20equivalent=0A=20=20=20;;=20to=20a=20= previous=20one.=0A=20=20=20do=20(if-let=20((prev-undo=20= (undo--last-change-was-undo-p=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20(vundo-m-undo-list=20mod))))=0A=20= =20=20=20=20=20=20=20=20=20(if=20(eq=20prev-undo=20t)=0A=20=20=20=20=20=20= =20=20=20=20=20=20=20=20;;=20FIXME:=20t=20means=20this=20undo=20is=20= region-undo,=20currently=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20;;=20= for=20the=20convenience=20of=20testing=20we=20regard=20t=20as=20undo=20= to=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20;;=20the=20beginning=20of=20= history.=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (vundo--eqv-merge-mod=20(nth=200=20mod-list)=20mod)=0A=20=20=20=20=20=20=20= =20=20=20=20=20(if-let=20((prev-m=20(gethash=20prev-undo=20mod-hash)))=0A= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(vundo--eqv-merge-mod=20= prev-m=20mod)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20(error=20= "PREV-M=20shouldn't=20be=20nil")))=0A=20=20=20=20=20=20=20=20;;=20If=20= MOD=20isn't=20an=20undo,=20it=20represents=20a=20new=20buffer=20state,=20= we=0A=20=20=20=20=20=20=20=20;;=20connect=20M-1=20with=20M,=20where=20= M-1=20is=20the=20parent=20and=20M=20is=20the=0A=20=20=20=20=20=20=20=20= ;;=20child.=0A=20=20=20=20=20=20=20=20(unless=20(eq=20m=200)=0A=20=20=20=20= =20=20=20=20=20=20(let*=20((m-1=20(nth=20(1-=20m)=20mod-list))=0A=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20;;=20TODO:=20may=20need=20to=20= optimize.=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (min-eqv-mod=20(car=20(vundo--eqv-list-of=20m-1))))=0A=20=20=20=20=20=20=20= =20=20=20=20=20(setf=20(vundo-m-parent=20mod)=20min-eqv-mod)=0A=20=20=20=20= =20=20=20=20=20=20=20=20(let=20((children=20(vundo-m-children=20= min-eqv-mod)))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20;;=20If=20= everything=20goes=20right,=20we=20should=20never=20encounter=0A=20=20=20=20= =20=20=20=20=20=20=20=20=20=20;;=20this.=0A=20=20=20=20=20=20=20=20=20=20= =20=20=20=20(cl-assert=20(not=20(memq=20mod=20children)))=0A=20=20=20=20=20= =20=20=20=20=20=20=20=20=20(setf=20(vundo-m-children=20min-eqv-mod)=0A=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (vundo--sort-mod=20(cons=20mod=20children)=20'reverse))))))))=0A=0A;;;=20= Draw=20tree=0A=0A(defun=20vundo--replace-at-col=20(from=20to=20col)=0A=20= =20"Replace=20FROM=20at=20COL=20with=20TO=20in=20each=20line=20of=20= current=20buffer.=0AIf=20a=20line=20is=20not=20COL=20columns=20long,=20= skip=20that=20line."=0A=20=20(save-excursion=0A=20=20=20=20(let=20((run=20= t))=0A=20=20=20=20=20=20(goto-char=20(point-min))=0A=20=20=20=20=20=20= (while=20run=0A=20=20=20=20=20=20=20=20(move-to-column=20col)=0A=20=20=20= =20=20=20=20=20(if=20(and=20(eq=20(current-column)=20col)=0A=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20(looking-at=20(regexp-quote=20= from)))=0A=20=20=20=20=20=20=20=20=20=20=20=20(replace-match=20to))=0A=20= =20=20=20=20=20=20=20;;=20If=20=E2=80=98forward-line=E2=80=99=20returns=20= 0,=20we=20haven=E2=80=99t=20hit=20the=20end=20of=0A=20=20=20=20=20=20=20=20= ;;=20buffer.=0A=20=20=20=20=20=20=20=20(setq=20run=20(eq=20= (forward-line)=200))))))=0A=0A(defun=20vundo--put-node-at-point=20(node)=0A= =20=20"Store=20the=20corresponding=20NODE=20as=20text=20property=20at=20= point."=0A=20=20(put-text-property=20(1-=20(point))=20(point)=0A=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20'vundo-node=0A=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20node))=0A=0A= (defun=20vundo--get-node-at-point=20()=0A=20=20"Retrieve=20the=20= corresponding=20NODE=20as=20text=20property=20at=20point."=0A=20=20= (plist-get=20(text-properties-at=20(1-=20(point)))=0A=20=20=20=20=20=20=20= =20=20=20=20=20=20'vundo-node))=0A=0A(defun=20vundo--draw-tree=20= (mod-list)=0A=20=20"Draw=20the=20tree=20in=20MOD-LIST=20in=20current=20= buffer."=0A=20=20(let*=20((root=20(nth=200=20mod-list))=0A=20=20=20=20=20= =20=20=20=20(node-queue=20(list=20root))=0A=20=20=20=20=20=20=20=20=20= (inhibit-read-only=20t))=0A=20=20=20=20(erase-buffer)=0A=20=20=20=20= (while=20node-queue=0A=20=20=20=20=20=20(let*=20((node=20(pop=20= node-queue))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20(children=20= (vundo-m-children=20node))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20= (parent=20(vundo-m-parent=20node))=0A=20=20=20=20=20=20=20=20=20=20=20=20= =20;;=20NODE=20is=20the=20nth=20child=20of=20PARENT.=0A=20=20=20=20=20=20= =20=20=20=20=20=20=20(my-idx=20(if=20parent=0A=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(seq-position=20= (vundo-m-children=20parent)=20node)))=0A=20=20=20=20=20=20=20=20=20=20=20= =20=20;;=20NODE=20is=20the=20last=20child=20of=20PARENT.=0A=20=20=20=20=20= =20=20=20=20=20=20=20=20(node-last-child-p=0A=20=20=20=20=20=20=20=20=20=20= =20=20=20=20(if=20parent=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20(eq=20node=20(car=20(last=20(vundo-m-children=20parent)))))))=0A=20= =20=20=20=20=20=20=20;;=20Go=20to=20parent.=0A=20=20=20=20=20=20=20=20= (if=20parent=20(goto-char=20(vundo-m-point=20parent)))=0A=20=20=20=20=20=20= =20=20;;=20TODO:=20more=20compact=20tree.=0A=20=20=20=20=20=20=20=20= (cond=20((null=20parent)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (insert=20"=E2=97=8F"))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20((eq=20= my-idx=200)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(insert=20= "=E2=94=80=E2=94=80=E2=97=8F"))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20= =20(t=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(let=20((col=20(max=20= 0=20(1-=20(current-column)))))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20;;=20New=20line=20at=20bottom.=0A=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20(goto-char=20(point-max))=0A=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20(insert=20"\n")=0A=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20;;=20Go=20under=20parent=20node=20in=20the=20new=20= line.=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (indent-to-column=20col)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20;;=20Connect=20parent=20down.=0A=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20(vundo--replace-at-col=20"=20"=20"=E2=94=82"=20col)=0A=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20(if=20node-last-child-p=0A=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(insert=20= "=E2=94=94=E2=94=80=E2=94=80=E2=97=8F")=0A=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20(insert=20"=E2=94=9C=E2=94=80=E2=94=80=E2=97=8F"))= )))=0A=20=20=20=20=20=20=20=20;;=20Store=20point=20so=20we=20can=20later=20= come=20back=20to=20this=20node.=0A=20=20=20=20=20=20=20=20(setf=20= (vundo-m-point=20node)=20(point))=0A=20=20=20=20=20=20=20=20;;=20= Associate=20the=20text=20node=20in=20buffer=20with=20the=20node=20= object.=0A=20=20=20=20=20=20=20=20(vundo--put-node-at-point=20node)=0A=20= =20=20=20=20=20=20=20;;=20Depth-first=20search.=0A=20=20=20=20=20=20=20=20= (setq=20node-queue=20(append=20children=20node-queue))))))=0A=0A;;;=20= Vundo=20buffer=20and=20invocation=0A=0A(defun=20vundo--buffer=20()=0A=20=20= "Return=20the=20vundo=20buffer."=0A=20=20(get-buffer-create=20"=20*vundo=20= tree*"))=0A=0A(defun=20vundo--kill-buffer-if-point-left=20(window)=0A=20=20= "Kill=20the=20vundo=20buffer=20if=20point=20left=20WINDOW.=0AWINDOW=20is=20= the=20window=20that=20was/is=20displaying=20the=20vundo=20buffer."=0A=20=20= (if=20(and=20(eq=20(window-buffer=20window)=20(vundo--buffer))=0A=20=20=20= =20=20=20=20=20=20=20=20(not=20(eq=20window=20(selected-window))))=0A=20=20= =20=20=20=20(with-selected-window=20window=0A=20=20=20=20=20=20=20=20= (kill-buffer-and-window))))=0A=0A(defvar=20vundo--mode-map=0A=20=20(let=20= ((map=20(make-sparse-keymap)))=0A=20=20=20=20(define-key=20map=20(kbd=20= "f")=20#'vundo-forward)=0A=20=20=20=20(define-key=20map=20(kbd=20"b")=20= #'vundo-backward)=0A=20=20=20=20(define-key=20map=20(kbd=20"n")=20= #'vundo-next)=0A=20=20=20=20(define-key=20map=20(kbd=20"p")=20= #'vundo-previous)=0A=20=20=20=20(define-key=20map=20(kbd=20"q")=20= #'kill-buffer-and-window)=0A=20=20=20=20(define-key=20map=20(kbd=20"i")=20= #'vundo--inspect)=0A=20=20=20=20(define-key=20map=20(kbd=20"c")=20= #'vundo--show-cursor)=0A=20=20=20=20map)=0A=20=20"Keymap=20for=20= =E2=80=98vundo--mode=E2=80=99.")=0A=0A(define-derived-mode=20vundo--mode=20= special-mode=0A=20=20"Vundo"=20"Mode=20for=20displaying=20the=20undo=20= tree."=0A=20=20(setq=20mode-line-format=20nil=0A=20=20=20=20=20=20=20=20= truncate-lines=20t=0A=20=20=20=20=20=20=20=20cursor-type=20nil)=0A=20=20= ;;=20If=20you=20leave=20the=20vundo=20buffer=20for=20the=20orig=20buffer=20= and=20do=20some=0A=20=20;;=20modifications,=20you=20have=20to=20refresh=20= the=20buffer.=20We=20can=0A=20=20;;=20auto-refresh=20or=20auto-quit,=20I=20= choose=20to=20auto-quit.=0A=20=20;;=20(add-hook=20= 'window-state-change-functions=0A=20=20;;=20=20=20=20=20=20=20=20=20=20=20= #'vundo--kill-buffer-if-point-left=0A=20=20;;=20=20=20=20=20=20=20=20=20=20= =200=20t)=0A=20=20)=0A=0A(defvar-local=20vundo--mod-list=20nil=0A=20=20= "Modification=20list=20generated=20by=20=E2=80=98vundo--mod-list-from=E2=80= =99.")=0A(defvar-local=20vundo--mod-hash=20nil=0A=20=20"Modification=20= hashmap=20generated=20by=20=E2=80=98vundo--mod-list-from=E2=80=99.")=0A= (defvar-local=20vundo--undo-list=20nil=0A=20=20"Original=20buffer's=20= `buffer-undo-list'.")=0A(defvar-local=20vundo--orig-buffer=20nil=0A=20=20= "Vundo=20buffer=20displays=20the=20undo=20tree=20for=20this=20buffer.")=0A= (defvar-local=20vundo--latest-node=20nil=0A=20=20"The=20latest=20node.")=0A= =0A(defun=20vundo--mod-list-trim=20(mod-list=20n)=0A=20=20"Remove=20MODS=20= from=20MOD-LIST.=0AKeep=20the=20first=20N=20modifications."=0A=20=20= (dolist=20(mod=20(nthcdr=20(1+=20n)=20mod-list))=0A=20=20=20=20(let=20= ((parent=20(vundo-m-parent=20mod))=0A=20=20=20=20=20=20=20=20=20=20= (eqv-list=20(vundo--eqv-list-of=20mod)))=0A=20=20=20=20=20=20(when=20= parent=0A=20=20=20=20=20=20=20=20(setf=20(vundo-m-children=20parent)=0A=20= =20=20=20=20=20=20=20=20=20=20=20=20=20(remove=20mod=20(vundo-m-children=20= parent))))=0A=20=20=20=20=20=20(when=20eqv-list=0A=20=20=20=20=20=20=20=20= (vundo--eqv-merge=20(remove=20mod=20eqv-list)))))=0A=20=20(seq-subseq=20= mod-list=200=20(1+=20n)))=0A=0A(defun=20vundo--refresh-buffer=0A=20=20=20= =20(orig-buffer=20vundo-buffer=20&optional=20incremental)=0A=20=20= "Refresh=20VUNDO-BUFFER=20with=20the=20undo=20history=20of=20= ORIG-BUFFER.=0AIf=20INCREMENTAL=20non-nil,=20reuse=20some=20date."=0A=20=20= ;;=20If=20=E2=80=98buffer-undo-list=E2=80=99=20is=20nil,=20then=20we=20= do=20nothing.=0A=20=20(with-current-buffer=20vundo-buffer=0A=20=20=20=20= (unless=20incremental=0A=20=20=20=20=20=20(setq=20vundo--undo-list=20nil=0A= =20=20=20=20=20=20=20=20=20=20=20=20vundo--mod-list=20nil=0A=20=20=20=20=20= =20=20=20=20=20=20=20vundo--mod-hash=20nil))=0A=20=20=20=20(let=20= ((undo-list=20(buffer-local-value=0A=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20'buffer-undo-list=20orig-buffer))=0A=20=20=20=20= =20=20=20=20=20=20(inhibit-read-only=20t)=0A=20=20=20=20=20=20=20=20=20=20= mod-list=20mod-hash)=0A=20=20=20=20=20=20(if=20(>=20(length=20undo-list)=20= (length=20vundo--undo-list))=0A=20=20=20=20=20=20=20=20=20=20;;=20= Adding.=0A=20=20=20=20=20=20=20=20=20=20(let=20((diff=20(-=20(length=20= undo-list)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20(length=20vundo--undo-list))))=0A=20=20=20=20=20=20=20=20=20= =20=20=20(cl-assert=20(eq=20vundo--undo-list=20(nthcdr=20diff=20= undo-list)))=0A=20=20=20=20=20=20=20=20=20=20=20=20(setq=20mod-list=20= (vundo--mod-list-from=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20undo-list=20diff=20vundo--mod-list)=0A=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20mod-hash=20= (vundo--update-mapping=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20mod-list=20vundo--mod-hash=0A=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (length=20vundo--mod-list)))=0A=20=20=20=20=20=20=20=20=20=20=20=20;;=20= Build=20tree.=0A=20=20=20=20=20=20=20=20=20=20=20=20(vundo--build-tree=20= mod-list=20mod-hash=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20(length=20vundo--mod-list)))=0A= =20=20=20=20=20=20=20=20;;=20Removing.=0A=20=20=20=20=20=20=20=20(let=20= ((ul=20undo-list))=0A=20=20=20=20=20=20=20=20=20=20(while=20(null=20(car=20= ul))=0A=20=20=20=20=20=20=20=20=20=20=20=20(setq=20undo-list=20(cdr=20= ul)))=0A=20=20=20=20=20=20=20=20=20=20(if-let*=20((new-tail=20(gethash=20= ul=20vundo--mod-hash))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20(idx=20(vundo-m-idx=20new-tail)))=0A=20=20=20=20=20=20=20=20=20= =20=20=20=20=20(setq=20mod-list=20(vundo--mod-list-trim=20= vundo--mod-list=20idx)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20mod-hash=20vundo--mod-hash)=0A=20=20=20=20=20=20=20=20=20=20=20= =20(error=20"Couldn't=20find=20modification"))))=0A=20=20=20=20=20=20;;=20= Render=20buffer.=0A=20=20=20=20=20=20(vundo--mode)=0A=20=20=20=20=20=20= (setq=20vundo--mod-list=20mod-list=0A=20=20=20=20=20=20=20=20=20=20=20=20= vundo--mod-hash=20mod-hash=0A=20=20=20=20=20=20=20=20=20=20=20=20= vundo--undo-list=20undo-list=0A=20=20=20=20=20=20=20=20=20=20=20=20= vundo--orig-buffer=20orig-buffer)=0A=20=20=20=20=20=20(vundo--draw-tree=20= mod-list)=0A=20=20=20=20=20=20;;=20Highlight=20current=20node.=0A=20=20=20= =20=20=20(let*=20((last-m=20(car=20(last=20mod-list)))=0A=20=20=20=20=20=20= =20=20=20=20=20=20=20(node=20(car=20(vundo--eqv-list-of=20last-m))))=0A=20= =20=20=20=20=20=20=20(setq=20vundo--latest-node=20node)=0A=20=20=20=20=20= =20=20=20(goto-char=20(vundo-m-point=20node))=0A=20=20=20=20=20=20=20=20= (put-text-property=20(1-=20(point))=20(point)=20'face=20'error)))))=0A=0A= (defun=20vundo=20()=0A=20=20"Display=20visual=20undo=20for=20current=20= buffer."=0A=20=20(interactive)=0A=20=20(if=20(and=20buffer-undo-list=20= (not=20(eq=20buffer-undo-list=20t)))=0A=20=20=20=20=20=20(let*=20= ((vundo-buf=20(vundo--buffer))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20= (orig-buf=20(current-buffer)))=0A=20=20=20=20=20=20=20=20= (vundo--refresh-buffer=20orig-buf=20vundo-buf)=0A=20=20=20=20=20=20=20=20= (select-window=0A=20=20=20=20=20=20=20=20=20= (display-buffer-in-side-window=0A=20=20=20=20=20=20=20=20=20=20vundo-buf=0A= =20=20=20=20=20=20=20=20=20=20'((side=20.=20bottom)=0A=20=20=20=20=20=20=20= =20=20=20=20=20(slot=20.=201)=0A=20=20=20=20=20=20=20=20=20=20=20=20= (window-height=20.=205))))=0A=20=20=20=20=20=20=20=20(goto-char=20= (vundo-m-point=20vundo--latest-node))=0A=20=20=20=20=20=20=20=20= (fit-window-to-buffer=20nil=205))=0A=20=20=20=20(message=20"There=20is=20= no=20undo=20history")))=0A=0A;;;=20Traverse=20undo=20tree=0A=0A(defun=20= vundo--calculate-shortest-route=20(from=20to)=0A=20=20"Calculate=20the=20= shortest=20route=20from=20FROM=20to=20TO=20node.=0AHere=20they=20= represent=20the=20source=20and=20dest=20buffer=20state.=20Both=20SETs=0A= are=20an=20equivalence=20set=20of=20states.=20Return=20(SOURCE=20.=20= DEST),=20meaning=0Ayou=20should=20undo=20the=20modifications=20from=20= DEST=20to=20SOURCE.=20"=0A=20=20(let=20(route-list)=0A=20=20=20=20;;=20= Find=20all=20valid=20routes.=0A=20=20=20=20(dolist=20(source=20= (vundo--eqv-list-of=20from))=0A=20=20=20=20=20=20(dolist=20(dest=20= (vundo--eqv-list-of=20to))=0A=20=20=20=20=20=20=20=20;;=20We=20only=20= allow=20route=20in=20this=20direction.=0A=20=20=20=20=20=20=20=20(if=20= (>=20(vundo-m-idx=20source)=20(vundo-m-idx=20dest))=0A=20=20=20=20=20=20=20= =20=20=20=20=20(push=20(cons=20source=20dest)=20route-list))))=0A=20=20=20= =20;;=20Find=20the=20shortest=20route.=0A=20=20=20=20(car=0A=20=20=20=20=20= (seq-sort=0A=20=20=20=20=20=20(lambda=20(r1=20r2)=0A=20=20=20=20=20=20=20= =20;;=20I.e.,=20distance=20between=20SOURCE=20and=20DEST=20in=20R1=0A=20=20= =20=20=20=20=20=20;;=20compare=20against=20distance=20in=20R2.=0A=20=20=20= =20=20=20=20=20(<=20(-=20(vundo-m-idx=20(car=20r1))=20(vundo-m-idx=20= (cdr=20r1)))=0A=20=20=20=20=20=20=20=20=20=20=20(-=20(vundo-m-idx=20(car=20= r2))=20(vundo-m-idx=20(cdr=20r2)))))=0A=20=20=20=20=20=20route-list))))=0A= =0A(defun=20vundo--list-subtract=20(l1=20l2)=0A=20=20"Return=20L1=20-=20= L2.=0A=0AE.g.,=0A=0A\(vundo--list-subtract=20'(1=202=203=204)=20'(3=20= 4))=0A=3D>=20(1=202)"=0A=20=20(let=20((len1=20(length=20l1))=0A=20=20=20=20= =20=20=20=20(len2=20(length=20l2)))=0A=20=20=20=20(cl-assert=20(>=20len1=20= len2))=0A=20=20=20=20(seq-subseq=20l1=200=20(-=20len1=20len2))))=0A=0A= (defun=20vundo--move-to-node=20(current=20dest=20orig-buffer=20mod-list)=0A= =20=20"Move=20from=20CURRENT=20node=20to=20DEST=20node=20by=20undoing=20= in=20ORIG-BUFFER.=0AORIG-BUFFER=20must=20be=20at=20CURRENT=20state.=20= MOD-LIST=20is=20the=20list=20you=0Aget=20from=20= =E2=80=98vundo--mod-list-from=E2=80=99.=20You=20should=20refresh=20vundo=20= buffer=0Aafter=20calling=20this=20function."=0A=20=20(if-let*=20((route=20= (vundo--calculate-shortest-route=0A=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20current=20dest)))=0A=20=20=20=20=20=20(let*=20= ((source=20(car=20route))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20(dest=20= (cdr=20route))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20;;=20The=20= complete=20undo-list=20that=20stops=20at=20SOURCE.=0A=20=20=20=20=20=20=20= =20=20=20=20=20=20(undo-list-at-source=20(vundo-m-undo-list=20source))=0A= =20=20=20=20=20=20=20=20=20=20=20=20=20;;=20The=20complete=20undo-list=20= that=20stops=20at=20DEST.=0A=20=20=20=20=20=20=20=20=20=20=20=20=20= (undo-list-at-dest=20(vundo-m-undo-list=20dest))=0A=20=20=20=20=20=20=20=20= =20=20=20=20=20(step=20(-=20(vundo-m-idx=20source)=20(vundo-m-idx=20= dest)))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20;;=20We=20will=20undo=20= these=20modifications.=0A=20=20=20=20=20=20=20=20=20=20=20=20=20= (planned-undo=20(vundo--list-subtract=0A=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20undo-list-at-source=0A= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20undo-list-at-dest)))=0A=20=20=20=20=20=20=20=20= (with-current-buffer=20orig-buffer=0A=20=20=20=20=20=20=20=20=20=20;;=20= Undo.=20This=20will=20undo=20modifications=20in=20PLANNED-UNDO=20and=0A=20= =20=20=20=20=20=20=20=20=20;;=20add=20new=20entries=20to=20= =E2=80=98buffer-undo-list=E2=80=99.=0A=20=20=20=20=20=20=20=20=20=20= (primitive-undo=20step=20planned-undo)=0A=20=20=20=20=20=20=20=20=20=20= ;;=20TODO:=20optimize=20this=20(finding=20max)=0A=20=20=20=20=20=20=20=20= =20=20(let=20((latest-buffer-state-idx=0A=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20;;=20If=20MOD=20has=20no=20prev-eqv,=20it=20is=20not=20= an=20undo,=20and=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20;;=20= represents=20a=20unique=20buffer=20state.=20Among=20all=20the=0A=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20;;=20MODs=20that=20represents=20= a=20unique=20buffer=20state,=20we=0A=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20;;=20find=20the=20latest=20one.=0A=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20(seq-max=20(mapcar=20#'vundo-m-idx=0A=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20(seq-filter=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(lambda=20= (mod)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(null=20(vundo-m-prev-eqv=20= mod)))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20mod-list)))))=0A=20=20=20=20=20=20= =20=20=20=20=20=20(if-let=20((possible-trim-point=0A=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(cl-loop=20for=20node=20in=20= (vundo--eqv-list-of=20dest)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20if=20(>=3D=20= (vundo-m-idx=20node)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= latest-buffer-state-idx)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20return=20node=0A=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20finally=20return=20nil)))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20;;=20Can=20trim=20undo-list,=20trim=20to=20DEST.=0A=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20(setq=20buffer-undo-list=0A=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(vundo-m-undo-list=20= possible-trim-point))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20;;=20= Can=E2=80=99t=20trim=20undo-list,=20update=20=E2=80=98undo-equiv-table=E2=80= =99.=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20(let=20((list=20= buffer-undo-list))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20;;=20= Strip=20leading=20nils.=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (while=20(eq=20(car=20list)=20nil)=0A=09=20=20=20=20=20=20=20=20=20=20= (setq=20list=20(cdr=20list)))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20;;=20FIXME:=20currently=20we=20regard=20t=20as=20pointing=20to=20= root=20node.=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(puthash=20= list=20(or=20undo-list-at-dest=20t)=0A=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20undo-equiv-table))))=0A=20=20=20=20= =20=20=20=20=20=20(if=20(>=20(length=20buffer-undo-list)=2010000)=0A=20=20= =20=20=20=20=20=20=20=20=20=20=20=20(debug))=0A=20=20=20=20=20=20=20=20=20= =20(message=20"%s=20->=20%s:=20%s=20steps,=20now=20%s=20entries,=20undo:=20= %s"=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (vundo-m-idx=20source)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20(vundo-m-idx=20dest)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20(length=20planned-undo)=0A=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20(length=20buffer-undo-list)=0A=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20planned-undo)=0A=20=20=20=20=20=20=20= =20=20=20(with-selected-window=20(get-buffer-window)=0A=20=20=20=20=20=20= =20=20=20=20=20=20(set-window-point=20nil=20(point))=0A=20=20=20=20=20=20= =20=20=20=20=20=20(recenter))))=0A=20=20=20=20(error=20"No=20possible=20= route")))=0A=0A(defun=20vundo-forward=20(arg)=0A=20=20"Move=20forward=20= ARG=20nodes=20in=20the=20undo=20tree.=0AIf=20ARG=20<=200,=20move=20= backward"=0A=20=20(interactive=20"p")=0A=20=20(let=20((step=20(abs=20= arg)))=0A=20=20=20=20(let*=20((step-fn=20(if=20(>=20arg=200)=0A=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(lambda=20= (node)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20(or=20(car=20(vundo-m-children=20node))=0A=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= node))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= (lambda=20(node)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20(or=20(vundo-m-parent=20node)=20node))))=0A=20=20=20=20= =20=20=20=20=20=20=20(node=20vundo--latest-node)=0A=20=20=20=20=20=20=20=20= =20=20=20(dest=20node))=0A=20=20=20=20=20=20(while=20(>=20step=200)=0A=20= =20=20=20=20=20=20=20(setq=20dest=20(funcall=20step-fn=20dest))=0A=20=20=20= =20=20=20=20=20(cl-decf=20step))=0A=20=20=20=20=20=20(unless=20(eq=20= node=20dest)=0A=20=20=20=20=20=20=20=20(vundo--move-to-node=0A=20=20=20=20= =20=20=20=20=20node=20dest=20vundo--orig-buffer=20vundo--mod-list)=0A=20=20= =20=20=20=20=20=20(vundo--refresh-buffer=0A=20=20=20=20=20=20=20=20=20= vundo--orig-buffer=20(current-buffer)=0A=20=20=20=20=20=20=20=20=20;;=20= 'incremental=0A=20=20=20=20=20=20=20=20=20)))))=0A=0A(defun=20= vundo-backward=20(arg)=0A=20=20"Move=20back=20ARG=20nodes=20in=20the=20= undo=20tree.=0AIf=20ARG=20<=200,=20move=20forward."=0A=20=20(interactive=20= "p")=0A=20=20(vundo-forward=20(-=20arg)))=0A=0A(defun=20vundo-next=20= (arg)=0A=20=20"Move=20to=20node=20below=20the=20current=20one.=20Move=20= ARG=20steps."=0A=20=20(interactive=20"p")=0A=20=20(let*=20((node=20= vundo--latest-node)=0A=20=20=20=20=20=20=20=20=20(parent=20= (vundo-m-parent=20node)))=0A=20=20=20=20;;=20While=20have=20parent=20but=20= no=20sibling,=20go=20up.=0A=20=20=20=20(while=20(and=20parent=20(<=3D=20= (length=20(vundo-m-children=20parent))=201))=0A=20=20=20=20=20=20(setq=20= node=20parent=0A=20=20=20=20=20=20=20=20=20=20=20=20parent=20= (vundo-m-parent=20node)))=0A=20=20=20=20;;=20Move=20to=20next/previous=20= sibling.=0A=20=20=20=20(when=20parent=0A=20=20=20=20=20=20(let*=20= ((siblings=20(vundo-m-children=20parent))=0A=20=20=20=20=20=20=20=20=20=20= =20=20=20(idx=20(seq-position=20siblings=20node))=0A=20=20=20=20=20=20=20= =20=20=20=20=20=20(new-idx=20(+=20idx=20arg))=0A=20=20=20=20=20=20=20=20=20= =20=20=20=20;;=20TODO:=20Move=20as=20far=20as=20possible=20instead=20of=20= not=0A=20=20=20=20=20=20=20=20=20=20=20=20=20;;=20moving=20when=20ARG=20= is=20too=20large.=0A=20=20=20=20=20=20=20=20=20=20=20=20=20(dest=20(or=20= (nth=20new-idx=20siblings)=20node)))=0A=20=20=20=20=20=20=20=20(unless=20= (eq=20node=20dest)=0A=20=20=20=20=20=20=20=20=20=20(vundo--move-to-node=0A= =20=20=20=20=20=20=20=20=20=20=20node=20dest=20vundo--orig-buffer=20= vundo--mod-list)=0A=20=20=20=20=20=20=20=20=20=20(vundo--refresh-buffer=0A= =20=20=20=20=20=20=20=20=20=20=20vundo--orig-buffer=20(current-buffer)=0A= =20=20=20=20=20=20=20=20=20=20=20;;=20'incremental=0A=20=20=20=20=20=20=20= =20=20=20=20))))))=0A=0A(defun=20vundo-previous=20(arg)=0A=20=20"Move=20= to=20node=20above=20the=20current=20one.=20Move=20ARG=20steps."=0A=20=20= (interactive=20"p")=0A=20=20(vundo-next=20(-=20arg)))=0A=0A;;;=20Debug=0A= =0A(defun=20vundo--inspect=20()=0A=20=20"Print=20some=20useful=20info=20= at=20point"=0A=20=20(interactive)=0A=20=20(let=20((node=20= (vundo--get-node-at-point)))=0A=20=20=20=20(message=20"States:=20%s=20= Children:=20%s=20Parent:=20%s"=0A=20=20=20=20=20=20=20=20=20=20=20=20=20= (mapcar=20(lambda=20(mod)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20(vundo-m-idx=20mod))=0A=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20(vundo--eqv-list-of=20node))=0A=20=20= =20=20=20=20=20=20=20=20=20=20=20(and=20(vundo-m-children=20node)=0A=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20(mapcar=20#'vundo-m-idx=20= (vundo-m-children=20node)))=0A=20=20=20=20=20=20=20=20=20=20=20=20=20= (and=20(vundo-m-parent=20node)=0A=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20(vundo-m-idx=20(vundo-m-parent=20node))))))=0A=0A(defun=20= vundo--show-cursor=20()=0A=20=20"Make=20cursor=20visible."=0A=20=20= (interactive)=0A=20=20(setq=20cursor-type=20t))=0A=0A=0A(provide=20= 'vundo)=0A=0A;;;=20vundo.el=20ends=20here=0A= --Apple-Mail=_6FFD4590-9D66-4134-A074-D2DA8DFD4244 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii The idea is to display an undo-tree and move between nodes with f/b/n/p. = You can enable it with M-x vundo RET. Yuan --Apple-Mail=_6FFD4590-9D66-4134-A074-D2DA8DFD4244--