From ab15bfb38fb241eb519b823dc7f414136fb7f2c6 Mon Sep 17 00:00:00 2001 From: Chen Bin Date: Wed, 17 Jul 2024 13:51:03 +1000 Subject: [PATCH] replace local elpa with gnu elpa --- README.org | 12 - lisp/init-elpa.el | 11 +- localelpa/archive-contents | 12 - localelpa/compat-29.1.4.5.tar | Bin 348160 -> 0 bytes localelpa/csv-mode-1.10.el | 1949 ------- localelpa/eldoc-1.15.0.tar | Bin 51200 -> 0 bytes localelpa/gnu-elpa-keyring-update-2022.12.tar | Bin 20480 -> 0 bytes localelpa/let-alist-1.0.6.el | 184 - localelpa/nadvice-0.3.el | 124 - localelpa/seq-2.24.tar | Bin 71680 -> 0 bytes localelpa/spinner-1.7.3.el | 406 -- localelpa/undo-tree-0.7.4.el | 4751 ----------------- localelpa/xr-1.14.el | 1371 ----- 13 files changed, 4 insertions(+), 8816 deletions(-) delete mode 100644 localelpa/archive-contents delete mode 100644 localelpa/compat-29.1.4.5.tar delete mode 100644 localelpa/csv-mode-1.10.el delete mode 100644 localelpa/eldoc-1.15.0.tar delete mode 100644 localelpa/gnu-elpa-keyring-update-2022.12.tar delete mode 100644 localelpa/let-alist-1.0.6.el delete mode 100644 localelpa/nadvice-0.3.el delete mode 100644 localelpa/seq-2.24.tar delete mode 100644 localelpa/spinner-1.7.3.el delete mode 100644 localelpa/undo-tree-0.7.4.el delete mode 100644 localelpa/xr-1.14.el diff --git a/README.org b/README.org index 5ca7385ff8..aa7bbc28bf 100644 --- a/README.org +++ b/README.org @@ -65,7 +65,6 @@ - [[#network-is-blocked][Network is blocked]] - [[#email][Email]] - [[#cannot-download-packages][Cannot download packages?]] - - [[#installing-missing-packages-by-using-gnu-elpa][Installing missing packages by using GNU ELPA]] - [[#use-flycheck-to-syntax-check-code][Use flycheck to syntax check code]] - [[#disable-vim-key-bindings][Disable Vim key bindings]] - [[#evil-setup][Evil setup]] @@ -588,17 +587,6 @@ If you use Gnus for email, check =init-gnus.el= and read [[https://github.com/re Some package cannot be downloaded automatically because of network problem. Run =M-x package-refresh-content=, restart Emacs, reinstall package. -** Installing missing packages by using [[https://elpa.gnu.org/][GNU ELPA]] -The GNU ELPA repository contains FSF-sanctioned Emacs packages. It is the default repository used by =package.el=. - -GNU ELPA are *deactivated* in this setup. - -GNU ELPA is deactivated for below reasons, -- 99% packages can be found from other repositories like MELPA -- "~/.emacs.d/localelpa/" contains GNU ELPA packages required by this setup - -To re-activate GNU ELPA, please search the line "uncomment below line if you need use GNU ELPA" in =init-elpa.el= . - ** Use flycheck to syntax check code If you prefer =flycheck= instead the default syntax check solution =lazyflymake= built into this configuration. diff --git a/lisp/init-elpa.el b/lisp/init-elpa.el index b0eaa17334..cfe925aad7 100644 --- a/lisp/init-elpa.el +++ b/lisp/init-elpa.el @@ -160,8 +160,7 @@ ;; dependency on 3rd party web site. (setq package-archives '( - ;; uncomment below line if you need use GNU ELPA - ;; ("gnu" . "https://elpa.gnu.org/packages/") + ("gnu" . "https://elpa.gnu.org/packages/") ("melpa" . "https://melpa.org/packages/") ("melpa-stable" . "https://stable.melpa.org/packages/") @@ -169,14 +168,14 @@ ;; is slow or shutdown. ;; ;; {{ Option 1: 163 mirror repository: - ;; ;; ("gnu" . "https://mirrors.163.com/elpa/gnu/") + ;; ("gnu" . "https://mirrors.163.com/elpa/gnu/") ;; ("melpa" . "https://mirrors.163.com/elpa/melpa/") ;; ("melpa-stable" . "https://mirrors.163.com/elpa/stable-melpa/") ;; ;; }} ;; ;; {{ Option 2: tsinghua mirror repository ;; ;; @see https://mirror.tuna.tsinghua.edu.cn/help/elpa/ on usage: - ;; ;; ("gnu" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/gnu/") + ;; ("gnu" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/gnu/") ;; ("melpa" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/melpa/") ;; ("melpa-stable" . "http://mirrors.tuna.tsinghua.edu.cn/elpa/stable-melpa/") ;; }} @@ -195,9 +194,6 @@ You still need modify `package-archives' in \"init-elpa.el\" to PERMANENTLY use ;; Un-comment below line if you follow "Install stable version in easiest way" ;; (setq package-archives '(("myelpa" . "~/myelpa/"))) -;; my local repository is always needed. -(push (cons "localelpa" (concat my-emacs-d "localelpa/")) package-archives) - (defun my-package-generate-autoloads-hack (pkg-desc pkg-dir) "Stop package.el from leaving open autoload files lying around." (let* ((path (expand-file-name (concat @@ -520,3 +516,4 @@ If NO-REFRESH is nil, `package-refresh-contents' is called." (setq kill-buffer-query-functions (delq 'process-kill-buffer-query-function kill-buffer-query-functions)) (provide 'init-elpa) +;;; init-elpa.el ends here diff --git a/localelpa/archive-contents b/localelpa/archive-contents deleted file mode 100644 index 3e5706b123..0000000000 --- a/localelpa/archive-contents +++ /dev/null @@ -1,12 +0,0 @@ -(1 - (csv-mode . [(1 10) nil "" single]) - (eldoc . [(1 15 0) nil "" tar]) - (spinner . [(1 7 3) nil "" single]) - (nadvice . [(0 3) nil "" single]) - (xr . [(1 14) nil "" single]) - (undo-tree . [(0 7 4) nil "Treat undo history as a tree" single]) - (let-alist . [(1 0 6) nil "Easily let-bind values of an assoc-list by their names" single]) - (gnu-elpa-keyring-update . [(2022 12) nil "Update Emacs's GPG keyring for GNU ELPA " tar]) - (compat . [(29 1 4 5) nil "" tar]) - (seq . [(2 24) nil "Sequence manipulation functions" tar]) -) diff --git a/localelpa/compat-29.1.4.5.tar b/localelpa/compat-29.1.4.5.tar deleted file mode 100644 index 5ddb4cbdd3059c81e3a7fea5292699b25b414608..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 348160 zcmeFa{a#yFviF(4brpNabR*IT1GeL|6L*@}IPp0)_5kj5KC!0}AV6&)DUz_wnHhhc z%b2;n=St@LtE#p3-qHr+)9Lf{GZUZF0i^X>t5&^Nt)tQTxI61?{&8(%?atcWTc7kY=;g)b`~eC+#5)Bl-%J(@fJ^!MGh-r(2ThYtD6J9n-)|IOR$cW!h3ckiy> z=HI;y&j0qk&AXo!>%Z0lt{dn7=jZ?R*IyUaGcP)wPVx9+cr@#ehTTDbc3E_fk9)^O ze^_jvcaNsU=H0c8qVq+k81&xvkGg}-VSjksAD-SXW?$>`R*NAYj>eah{^{AQXg^#j zHrF>dI{fEO@p#hf6?>zT*}LwfS3DkF43E2-X|;GVJX*7^`)B=WF`kT0C*AXcUr*F! z8eP6FE=L!|QFmBOddL0gY|=lxnDvVOtmqDpZ;d9!`RKTRa;YWo8Kd_m#q6wC%zBga zX)!vnKfd32S$y9c_9opy@#5le&_61k_K$kQX|L!qwLY1ivE@S>N8PVpcCXL(lj3b} zGG!aZ?KDRWxLS-RT1>k;)5<5sXsqq7u-waH&}CVvYxCyIJ=zIqEoY-K`#EDkcKEJ8 z7!-%S;$qr6xfrZ!DgaXa_+0DNL+#MvMBX5Ic^>f8Q_L!Gj=!EtfcecR)3k9z&L ztgH)ijXVPqrT6@A-Le2xqvwZ%Fyk8?jmpdp+HgLsmNw+WBn>#PEG;+~Xc2wtH{6 zIPcDS!;UUrf6y!1gHiXmScclhh#QSYQb zWPa{xcQ6!SJc6+mn2}85ZceRm(RK;}MJL;bZmAaGD3qfwGtJE9x?Dw_S7E5}^XJGyC;`|Ws zaHP5IDyu#kT!@Mk|IwR_)>;?S?rHCS(XK3`@FhS0?%%c_?(eNEv8wi<2Z3p`r=oq{ zebehyoAc%Hl(pqO0!qMG#J_gGSdTy3@6L1r(J)%@A-AM`gm92XDEoG&Osr}9b~HX@ zn-70Ujp=#Z&{Hk@MSJ59MSmq`XBeIC{2`tUr;A589ZlHqwD$`H68d)p0ckipwX#F| z0iwvp;>`E9|F<)Cd}IZmCjc8lDPlO1;_U6GTcGk!UOn;C+&Iiy7jSDqxJ zw$FR#zZBEU>8y8d@Gh3yN0+DXpkDm%7+TR8&WiJC=Xf;Dhp5X_6yLsjReXCZf7nKl zfV{Iuu2n}Q!EU{6y7_pH7l`9q&}qi4==^dqnn8p*)7d5WMi5vajEn6IaC_EcHWDY+ zsTNblVGNDfn~Ex*p+QJWDdOzK687QeaDoenk2oAmi>36thjfHLBC|l!r^WQ*a60SH zE+jdqA(9b1vU>#2noV08vO69j3ysESm8szLW0-wg@&Me=7KJ!59(0YLJ{g{jTIb#2 zMRx%G*C4DBHerI_lq>nBR~*Drm+kw5I!-RHnyA6hEqcb6%e74fOGiE#4P%Qvz%#u( zKO7CF_rZvRql?Mg-tv7Ntbxk0%sz#5)L{7E|J(mxiEhf!nXGkzy)0oYU6yJFEm zggo3AmGOn1cPDQ^fkIej+gDeIy2C`5uMrNCoZ}#cDXVkS>nMF3-&-+a_Q|{cw1*_L zFC63lwIHN|?!{oniB-g}A;RGV`3qS@ZLUI2BQg-wlga2Dq2MWa)e4!NSzdC;>YdQ3 zI9R+j%itTgzk9qYNx|e<39~IpArk4;|7bK3F^g5`R@tW)dHy4oGZxV^6=*HXV}hg6_A9y- z^~oAY@f97R`t9T1(SQT*WKd3er>q!B2jyn!jI}QPg&4Q_r8b*nMwbAiFVr2TKkJ`f zj4r0)5DiOeZh8fe5cItRM~2kQ$-*2!Bo1fXN0tZCw}wUUeOHGd{Od}@pLK_)7wj(e zJpBzK#cfI4Ubyxa`Piz9l z0N(`Kw1IDl1=y8zP{yJMh$=`sw&~6IxgoKq0u|Ji$;e1|=LET=gBH%PZU5nN`{C&L zvH>zuA1ZweGTZNaMq0*b%DSpS!vN!{d9YM`a!{dzhUTi-!zA8QV zf@@Ul|FFFyL&Nq{j0vsn^s(LUFfbXNM+r5CA(2Bm2U5s%HX4t)kz$zLR%+~=i(3I( za1#J(v0-`=ZYQqNig+b9*U~bu+3=ZhOyetqF7m%*A1%Pw*V-1<3H4SYnQh4En$fNH z-cQfId;YZe%huDE+t^H=t>`At3N3EhiZ6jfb?&k;%N4bB<=Ko~nBe+ukOR*Tdo7a| zU3)0nGS`!OnAqlWhQ;y;2tatQ=`z;m$ogyY^P{=VvqbJjWKsxiaiMXi0+h`0>KJu!@aNe9wg%M&_hMQXzg2pmEqedV~23 z5Sbyof7qAJvt?Ed;DRX!aTS*K-Vabh_gGLe5J{?yoi&q@&Dy05iqii1_Z&mp>BrbG ztJci#Z~Qk5HP$8dsCO;e0{P18mDlB6FNZK9V6-U=8pZ&>d5{$)K^qG#^iE1b*Z@qy zwr>)(LdMuS{yWBU!;28i)2ZsDY;re&7_D%%ImY12po`FJ?2K-$NfdMn^~`H;+jYYn zpo`=mw~t4DtE8vdh|#(|zL=gta#!0@DP?e>1v`;>z<#sY%CQ|RCIhV6G>aV<#z+82 zAT5{0p4>*H)2vJmUy=bL)==G=St)E{zUF%yD@|aWTz zwac~oXQRZ;#M=yvCHcOd5z81!+ndvF8IDS-ln9P-?T=FM8qH33D9A9BK{UJd1ae*{ z*{jeZGeg$srv5OX8tH*XOK!+|XsFYMK#5AL$5a*rBuz0A=UN{X1-zMGCL3=#xT7Py z2|>O0W3ls7nOhNfXuM$AlUUh#-ym3?b*E>YnRL32A+AL8^0Nlvsr)i^u+w`eY7Z;u zvCm2I!`9vp9c=kew~a6O{iA2Go>#CDb6WB!t3+`Iy}Y92%@=CA!7m;vO0VaiMxqd z`~g2yq6O_+C~@hJPTAs)Y{E}qpTAu65eiJr9L3mwDB9SUZWZl66n8b0Mp!B6(b_?L z!!Pr$N*fP4%f%@am0QAXHpV)HAp`${h^M1=`6iL(I=aj|U=lfRAk^ur4)*V< zyiU4VhIWhg;)b?{Q7_aLa*z!>rtKo_j*9l9?T7eQxAvbr-`OkHitWF?*b5SE%-oDZ zsMmhIxn=GvsoFMRJquSRNnlO{{FW@XVmOmdP2V3Ey`~nrp>Dy~J?>1qC%vm1`gLAz z-^cE5u0eD7V|_Hkmdp3}4siDtM*}o;`&cNOW_BsBJ%%?qjMXN21?0X06j^5S@6rj^ zhyX6od>C$nZNF_`s`WN@R>}D(6Beq7*&d2*aiKF;3l7ZPQEabZ)tnZ8LHKZH>%jqk zkz(8SDgUy<&gB=A!iqURap}KgjSZv5sMViMdjk|P)V0yfnS(@J6W&cu*S5#S+YBUw zvYp(U-sL+qZGAb90xsewNzv1bYhyV@UiPifzitJp0GNe#He53) ze(y04Xmq{d+ddvP`Gl_ly!G`v(wq3LMRh*M!?9w11WZ(8X`6OXI+5;B#vq51gij3^I&yE~o!g5QSr)s<(MNF=XzsDL{wB-QWd&m)|Y z#&c`!SYBpol|hR+UC|~nY%sh?=c)yx1f7^=b8PD`&(UwApjsI?CAS@T4IR5a-eydmrom$$oOB#9+P$^YHIbCgA>j~jI`1%=Vme%r z&!zIO?UyP64;TPD8(}ODiEPG3VhlLtvCN%i+pm`2O7A8Z&$mkaKFx0@h_}??=vZmJ z>ua>;Rcuk4E0IL4ZF0^<|1=g(?)V6jh<@_6f7~mUldAwpSRo|&PgtHnKLvLXQm>1i z`|Z2`$sv%=JjMT}zrO+peyy!t8~?ene)rz(yNdt3clQpFpm#m~^UiPapT8FPuN|j) z?{(%ki2wZRxA@O8Z1uPJ&)?!dH@Ol2{P<7A5KGmV$To$;Hc{$(#nR?I4+Kq3WIjeB z<_lKV6b+%ibD?7%!Z1WRBQiqYce-y0&^2r1xWKcFy{bWIzoJ7^z?ug%N)mkX*Js}dGxEhwN=wWd{9GfGA{rl-B`1O=?Ez+2qHXI-Mn@a9zX4W)L> zI>bgbEW9>|X&ZY!o&-`e5Ss>E>}+p8lIhg_Sl$bp>Q8(7TMz#|tjuAR&MrrFXI3+g zXTO-4GDk;*oDjWoB!5|}96JrDcyU?3<%JuLkb!6H@Eyk56NM?nbXlKb4ZHgt;;(Zk zEoSXYBK0J<$ro+_z%edBnTK@PN;8>X5IIsIKvFp^@K&)5fUH2@(jE?pH5m@+h|r9; z?_BO5Jbj0~*}EQ&J=u$G7`08&wkH`l^4$^DI|ZRwcnVVJ4&G^rl~@HIg77;<{Uf2z z`Vo@gNT*%zJm25GU%Z&~bvC1vh;Yy1zgQj;KX6)>p&I>rdO`m)R3f56PFvR^mOQNr zK1#PK+<)09(-JZq)KJAVdpVv9T_t|mONn?57i)~WA32rKv)Pg37zOq}mQeMNnGp%Lr2KydyLK4q=VYk6Q zv3vUS&q3Ne5<)o#Gr|5p^ zXoRv(x|a)6R(!PqEu5pa%jkEr0Oqv7%J`-oHXaRlnA)egq{BBJ%{L!E8jNDZg5m`c zX+>$j*`b(eqkb|0D7hLc`!Gt>8jISyALD4UqS1PN*Uy#7H1owv*@3alyg-9pb4(T4moBsT)bL<)uvx0nbEK^ufk zH%J8Y-AyRi@+5|b&!4?`zO%ivzbBgV=*ccQD9?9)GS*e`NqeY_IIZ&x+>?<4BLSi^ zNMfZ#ySSL)Y=5m)4lPy(V-lq!vq#p`D~3`$w`^#n*5Gf%ZWGZf#S{J1XVHoDrraX)ie|-4#*h09+ICpP7_?F`HLI z_`e7Hv#uQe?+F2ZX=_8|KbBs-S}K-q<>)&bzdU4lrUsUMQWD$TH~oUKZ>(Sq0#D8l zkGngWcqF9&kKmI(re$BSoa@6Y^+v3BI;Nrma;^d_N9(kZd1tm14U0(e& zw-;2rgD;pr7J??4a@t&FIqgmkNu}Y=4+aELA`4z3qh6`7G&XNgS8_^AQyP}<{9XT; z{IUrDt*9Lq6h9j^keHApt)r2bA3!v8mn_jIFY>f_y!-r_k(d4FCcMmLZG|%fx?2)o zTU8bGZCi=FLiUSd8}f0qp=0`n9}JQXz%HjD|AW-0Xg|n}a`)JD$3fuRN`u$GYg_h; zBBm3mOW?6nUpQ!y?BhAz6rC?qa*N%?rBIuC81dWOT$6r-acIQEXr8R z#bbelzueis;ye)G#6qfTi^b;IP%P$Fm;vu90R)!(NL_LQZ%TmnW_V^o-t?Fpn`uR= zVjQY}upnvYseLJy(9oTMDaxC!F7;Y4_KEJnodHR?AwcKm`RBrSZn6?r7QtQQo&5TM zH!p0nxf{6SCZ~t@cW-@v zt2P`m3F`5}cvtlAFAh-Ye}W^^^7_UjOM0*jWDEWKd3|-`H3o(!WeB%~D1@~#W3A{O zjtEW=4DeBgv}X~D9tIxq$hYY=LnB5M>nX!Imm6@VCNN6!UM)BTz@<}Iwd6e=1-23? zwOp}5|35G)K$R^KUQi0N(y(B$U*5fY`|fMkW?cZA*VQW|p&fZe*hV^(S?C7!mUNKa zv+YE!jcjK6E?jI+-V;YLEtWo4W=;I#bN#bO1M;&^C!>pTXIS9tf53k@csWedr<;wR z-mZL_NE}1P$Ts@?$eWELFMMhv$EP|Fdz} z&1;6Oe77)c{;ql0+t&^4-F)`8(jZT*3Hiz&VKTx9*&T082NP zh6VBySVC;5TgDW$p!^~KE89@{jA&5_J(UAxJZOSNl`=$#hqF;9iX_Asp(B!EX2JR! zU#=8J2v!hw-d9J{DTo`Tf>S=l0kojA;8mM#YKl$DD`1X(BN4Y1*3~)m^^8Xp6og3;ui(_-c2wSgN zkWY{;0XH(J|VIo zb@41w(=z(u{P@2Aj61h3#b;kR5%lf1odRhWCKq6r@MZzj z+>tioV}k6z)R{JNl)j`fLuiD!FT_Ir(0NUf6NEgzNYdRO?1NXY)+(RTc!h?DLDf%b zX%gW5o()uKuWx9-a_j%|6JF0zEgc3lL3cq^yMd?_f>)Bdsu$g8LHqO*1`E0C>cI{_ zVX$Clu?oJYpzdZFA%ewl>ySl*$aXucV1dg@J2Z_xD6n;u5f>I%5NCf`nq z)8bH)ul_9>cPqs?QV0ti(Zb0o>Xq%p9hG??!7N)#-K5Z9-m_w0^?qhAc}9^i`8v!} zN_7|o9FQPL)QNbj6&>^H6L0%d`H%AusP0slkeR?;m!_`w2dh@@M{XK-Tkr#uu&d`d zT`*Ewy&Y;rSiy$QH~~j?R$qPP5R%zm0lF3k@gMBgx8e)-9#lZWeYGq@6M_2o!QtM- zAcG&dSb8-i;3u7DbtHXL_VOBFl$N_sH3-#dTqmmUeqXt2`2H0hOm?~sOGD*_RTm@f z84EHI&LQ9yVLBGyc49S+C*E9IgN-Y%&klOp!BcE89F$|~&Mv07KcXt6|LxynZ}C(v zML0B|!nH#hQlxSdFLs}Qzq|F!J!4zD-|r=3k4!f*#6(3Uf=qIG62l9so5+5&zqS8z zuS4NP;)QnIo?}(Cu{fA9HyM~xJ}4VQ(ikryCm%y46L=Ej7E=#||QVHnOQN#)S zZn~6L?H{Gx zzYgF|cCU1XBSA9)s49IOVzvK5m51c5rSL9=$XT#jb;?=?|HHds-zYm=-^2G^{B)>hKBVpf@O_uM}xH=}|QfZe#w$V4gEooHNQu%^>~K zKzsd*PINc;?#57ZZ+cxLr*Heu<1e(?Cp+=GRezcFNz{k%S?J_6$c*ZWglk^TlvoO$ zbVH{c_2%^)olBJ!9hch?AD0~KbK!1P6MC$jJy@(;e+as1w~n^;5V&$Bfz=Bz@?Hd2 zHsE#yMn?Yp-x5=3;%VO0;Q7eP%vo@!>T1C}ffU^{zaAt+|>Q1pp{cG67> z{y_af@fi7CM#Xn8A3xsSO`Ldl|JL>nZWc>1^9dNqLLyW?&3)VAaP^_qS})8)1QPmq zASZl;cmlfs6w^8J7&b+8Mud#aBsGUjl&72LVnOQK#vgQUVfy&)GU2^`^sR&`V`~>Jx@*PjS)< zB6~V|W6&-lv7lPBgJ9m>_bP1pPyzUp{!|&qe%Uf4eT~FdNfRI_goqf#1^MQ(GGM2c zj@Z5Z5whDPBOpVB^s#4GCj>k{dW1hm{bbzo=pX;m1rC=*Bqk{!EZDct`X_yq=fr_f z;lVNIHVqur)%1$#=10*m?sCk96>Z{*Fk~xCG+HGX@r^=Gt>$Q2dd|^v5un9@w*?Mny_3`-z%Z8xlI7fn^n4Uo^vERUEq?5 zC7R%$R2TS(G+}U{+`+eek1;?XoAoAFHRF$ttvQFfK6qCMJLOc3v&`w!afyq zQ5?K_^?Nc9hNPwZxWE0^ePRvBelIHo6H1oK%sQSBN+AB^U3Zoj+rJZ6%xzi04&;kz z+G}fT*b1#muAvwzuD*f-;>N&keJqk|9q~}43dM?7rM@mPqCO*rxR^4of+!@Td-2^@ zufDZ(ikX$tRP|N3D~j9YZ$$H7jA)i*lUlTMlrw0^BIQhE$B?8(W9k(4zlsco#!NK; zy;>sUFG(z(_#gbVK{{>d6g18%kyR5>yyhe7osF$qnuO9)&GX4qmG90tG(Cmnm)b@c0|Bin$7i^I(P6|2_}=8_{7!iuGm-y{E>s^VUH=M2!B^lhIa?F`=*OZ(4Wkc@D% z372lo&c_PFZ!MzW0oj`^G^|GcN4?u7>?XpR z5E~V%R8*duhps?rH8uQwYW45=3nZn0K3R;y%uBLM2&|m)D2;fWB2cUtlN6MMRAuR4 zdfSo%t^O9!S^YBtV?E`-*VX{N1BO50{!;o8QC@a<5(s~hoLdo6sNT3?E^F0PXfkXA zM+1aMULiv2GXHZj5j5@0j9#7j9(L*vH4J5Xhy~6g!TVe}x2Jz{(xX{$%4QaV8-@d2 zAvr9%V=qK5rqGHyv%b2Vk>R1i%&H<5rFpLpgVk1 zEaCmO-|oMDHF-6Bog+jr=9xY(RZn@XbGE*v|9!qAuJ`$m{?`afTZ*3_8MBBb6-ziE z%!t}x(TUT7ZsHmVHPua;S09ub|IczbCPpmyj6z=su=q{A#;X#XTl-dI{l&RzWHCs0Zp{u`}6b*Wgk zylseA*&i>V%C;3>WC_r)z3Q#6j4E))p;#!vg2+&tD-BuNp&VQPu^G8WbxvE}m?IuI z?C{E*ynyNITutbztwRP8-FI7xrOoD{059&36}SBdcQOf1a5pR4pDL;LPL3^*n@{0R zayXmA%c33Tj}S;!9K3MeXdFM*i%t~t^$+HD?^>8rmv#0CtNJyS#r}xMntR#ypN>p4 zg_X)JcfsR1Gh$xC;jxFk^qd=53b#0Z5Q|kT-CmtChWy==9nq3KvvG4*kOpTNHbm*Qne7QNT%z_n3dZ{8B6pzJ+$+-r`HQETk_3|Ve4tJltHASvdatf zoJtDd%G>i}k_|+zT7+Jxq23@gQjx}xCXVmnt3{U#8 zRU>~369){4b|bNoNBznMCE%j;tE(Zprl9~;0hRin*!*Q*-*{zJwpVOT40FX$b2Kd$ z76ZptwQSHTH*Hhc74LNIK$i3o+At@hDz5eK^ifSyCKGptL27oSh^suVdg)P}oR(e* zYbYD}CUuBrL@P&vuU5v&%?@&G%&IMItyp!iRT>i{{T)Uqy>Y?&Y}C(GOD%8N3!&R! zZT^lz_j!e?*p&3lJY)Jrc4hq42=6QOuizzy z1hq=p{c2O$Y%D{F)v&gQ3i7m61@D)1t4+*4$3r8}`z?ZGotvv2Hl{ZxH0hYi!$6P|8LqK6f07ZFRy$MFLgEd;0WwJkSorz-ih^!mf7e?7pfek_lU1pOxd@Jdg-AD zAEXI1Wj}tSMy#^G1x5vRIJSFDkB?7lBoHvy(7>2Q%ziB%*gD#7OaACwMFPu5hb&oP zZKFP$NVQ7LB3_iCUD2ZfR?Rp!YPhRF(&+bb!n7-P6c}Vow#qJ-_yhvnu!h*hLG)Zz zSG~ECCc35%UA3y{QavRx*OZIkj>(VpLHp-}#6s9SYwoHHR40epEJO1hsbgt4noq+k~9$g)03qvq(1o`=Y7S z+-YmsPSL9e-Gn7+@y#GjDAEYfM#Pk6(=?A?PXr#=jf<`&(`jnY)l5Da$#R7YbXTBW zRphkR#(ElM%d}trsr591lj)rnnur!WBy)I9+G9-f^Db+_@(>2Yz0@qF2&Zsr-Wdu zCVcY}VL8>s(D1#GJd$`px#gg{zQ}Tc*RGhUEJM~nY-+Q~%?w)I38Xp8#rnC629wEE-OX^Y$AL*@Ca7e4XOEXh6!z7nRx74F^mLEY z%cH~WhJ33L?G4ctnd#J>@Q|6@&7!0jW-1+E;PuD_*nQTP_MUy z>q{Uw+AIrEW%_Q00K z5=K)Spp`qAK**}4^kdpESY$sc^Xw6`ZO^J6lu(@#io@#>aVX*0*F}K3y`OPPZu`N% zAQKhWj;!DJmP495C8Vv$uqiW^f=ua> zh%8<0$S%;l9*%9Y?WjL z?fOe~vTw&g3R5*Zw_VfW39#ar$dJOf8wH~~?lj<|cv-*eNN>VOq?8$Js! z%@l->8VlPSuH-0(Q>d!0*N#HhNn7Vbo(fj}fzCw+CG{gL#d}&n#zmhR7x7KR-$Nne zoYFv-;mc1nGOBTwUu%4u*G&F%pDT9-5)d`2BA`e?@(V^{Tc*8t8#OW%DC+hvHf=8s zR~#&AEt7r2;a@vY6%lf}Aert*)XmaaIOQY6M6V35wH|W-_^Wt@BY8s!XA};hgsjtV-tE_am0WSI7+kv z(Mj!0lu27zZP{x?9PPok$IF(^IFDfm-wx6j8DKhXv-4Vb3w z0WLG0{Y^uqDQpzsUqFg0g_!82scPd8hJ~FvnThqHEtIY{ikg!yl^>I~7h_h7y}v#A z@%jD_4>qZfRcqh=e6+rCulV*`L#NM)%#e$M=8vE47sWc?>7|RZi?zQ~e2cd4o3D1+ zd;aj>i}j5=4P7>iZwF0Xp6>iR(`;-IxEp~|$Wlv2}KR$A^C-(Jm{^qw>o(Tuw| zVal@kRZM81OP%|edIOdBW=1ES+9g@lMRRMGvXxZ(66$$)sqK}OW-OI1$<(5LdQ^=_ znIq)_Y01cINFes=s*!tW%Ym8B=GVCFiBZG~;+QjLMA$i5BE_jP zp$|7z_yIBG*7L|>z^n3NIYcz~h_WdRs?#I!>xf!7ZphiQ5*Lk~A`zOB(v z8BYqULHf4{hv^k8G!C z+R7p|Z$R1rgX-Zu8rt?3=aA})j-YrDvreQfqA9;xN+)RYIoKb11+!OWf2|_|2F-G!yp~Ec+F+VbGc~kGU->eZ zs8=Ssw_t6wip9M_&5k11m(_yn5KBoM6`CzIWG8P32`3DiYB9HYUmEWIjkplk%in78 z9@cJbS8d-8a6sS|ZnjJ^69p1jgp$Psqt1ea_BZjiM{jsQR2GlwVv!N76tZENJ-C!i zFRTUXNIhjGOy}IR`&z2KI!ee!HDa7Nw(7W)aymuH`Z%C|Jag-gDh0B)b5(OuhKt_c zD!Qjo8f{4t&Qh6&3LHLqgO$mfcZ=KgaQOBv1)0AilF~{j;h&h{7O3c#ADeA=U6X8rafSS{eF&rUrAZ5hf%-L4`cI zU%F#_!oWTM#P4;A!?Xfa`ND#Ist`>uO)4Ct;;Z&agw!RwqbAB*off~_Giw{&ZKF%p zN0*$_cnH=iVAToC0)Be#^J(||yo;E{gI#1OV|2l;G1YCLls$8;tom5mjoku~YNFQ3 z)7LXgClL*sK(~lU-d!|1)m6lV{jSgBwwy)RDk|!)Tf049;NQv%6ldbJ9fA?k?G!On zu_2WD0%8^Mt374)`etMCn~1>Loc{(eeP4xu0c#G%>=zRf?~8?@B^RMID8G!P*6|4W ztJqxIG{%YJK!vy7_aTJ}v}O!}voLVy+Lbl{l%}+UosLF&hNu&h8(&PQU1L@- zqX}}nTaaH#4cowk8_YWtr*{DQz zZJLw0kD9t0(cTsnOyQnRH-981^!bnE67zlzUcElnwDYc_=OwMCxlKrjme4R0oDi1` zZ-^nYQdA75_g#=x!a%N5bgCPX+2$t_qZF}HNU0Nt`P{uS&C0oOC@`_9ye8R(DaQn> zxFmQpV?{uz{;hv)9zm&!t1*_1kbwmQSBZA&oM|6LG*U&EJh)hN?mf4Xh4qco5L!z@ z7Y%^(H6|$`jB-_76%fv|wl9VnTXOCUNLLpVIi!*7Rep1mDbjScZ3wL!*d9+-#ENmu zmsR>gzQ571S3UoQ5SB;aD}>C?_*3bk$T8D`8qjDqM?P853$ z+<1hE#R!AZ3)UaFzSdghp2;jS~4^O1XTXL&>81L zv%hVWD>?|w^K=11H$3gm#<8K8KYE{$Vy@crtrkG_T;<(@5VAE zwhDq>^sDylc4 zbaJx*nUvOaGKD#|WeVK>3-x%?K{c%H+9OL9;zX2fpiekHorOVw!2SPG2YA(fePMQZ z^Y_f~q2Z35OAVSoQA61nY*E~=x3&wlg`DzMOjgi|l(Vjj$9|0V;;TW-n)n@g`1Hxc ze|LtcwCafX@y(IqEa|!l;i{_(>QgB&A$2n$zdfPl%YlXvrJ$-_sZL;x-igcc&va0q z7}3XqYHbyX%2_-Wk8(}km|FV}%tHWKIS&$N{8189Ul27-m%9Pn=5tkCJ+fWn(fU^% z3}i!lRw%3w?Q;ZZ6G}?H_BW|Zrz0%ef0Otx!=jZNQ|Y#bTWx|H)6S1wC9g-yNaVR3 zFav`blyg+b7)0z08Z!jZbz0UY9pu>_`F;<#_P4*+ON5Y3VNzZbMnnCrjnH@%F`d%4 zl@>;l$=V?s#{_c8@Z9YKy}@jzW$h<=Pf?Hccp-l3&hwq8Kc!T=DD8qlCmL3y^&Vyd zGfw$Bv@@ju_TG~VluBt*suWdIdkFv@L+>Y3%GF?eGpen)gTs-h2;y`{DqImkQy1-J zI=YxZ>#i*Y)yM;;+=yZaE97Bng>*Q(HW?h% zoD{BinDVStVDV;%_>EOXqPt9V{ydSO4RX4S4%z{r32Er2<=X&C+5!VDNUQ6c629Qw zp~`Dw)LMoujc>|@83YKHu$rv-F>AMVRy904Q86RNfp7;sLI%~@-mq{*@CZlJ9$mq(SOe$nELg)?eMnoepD${3_=udYd0I&- z*2gPSs#`jB@1GGrP|b{WZoVAbP-GagtdcA_SvG1yMxN>i)b3as-8wZAEXr}r(<%e0 zk%JE3_9vsE%u~723-UJxyh!Q$xF*;*grT3J0{^nL`(*38r`uS=-Fj4MZl;{L)2^(w zD$OmzPtloRZCVeux0#G7Id#vdYMWk6q({kCL$c5(LDZ_4pT}vZz?>CZgmo5$DWA-0 zTYz3}h7o+9Gsn{PwvTii77Ks~qJi#)@zWVLsbh*6Ac#_YiC&qn8)Z<5%r zN8i>n6J@?H(KEOvwZ)_lGK-}=5K*R9IsHL@RP>hURIb=XgH%tXL21E$YLAf3V=@#H z@ItzdJ91BVWNO9E)7BpiWU`^HA?gzOKEeL<0Gj8fEH3)2!$zFoo_-7|js%e%#~p08I1yrJDW=H|00CA@pZ?e+3I z*G!XkVjGiJRTzSDSeo}$J*M2ducolAR}M%o+t?F!L6olUQ|ZJBnKX9I47s({G`e!s zQfF>s77@j(HAS~IORAk06_vhBS?i6hiE4v8mu)}N9AMA2E;jGPx*Ny0^H}V{;at~C zC*0KO%e##P`FM0RSHnfBHvy@gUoI>>k0QaBa@aQ@@M5!hKA-B6dW?e{C+btg&?tZm zZKgJHuyg~9gT;ZZw%!4654`Q6i66kDx(c;ehH1ABn8iN+`vvql?uIc) zm5@OGLG}gWRq*z$7+Z0RHr%l-5;lxXDR&tpHC{k~rpo!nl}k~Q5ZG?ZoeEqwlfjT9 zrx#-kj0B^&42QiS7onIkuSM9BaGtz^tph=&x6E?4sBG*3xX$dwI-;x~(nPEj7O+Em zct7$4*FU=}LLRsln(}@2r>$&NFvc-N7?~va0V$UwL{vl(BmZXcXn456&P9GtI}`_C zP}s%xhP)&Y0@Q{ti)9H!pfrwXERweh+P@rUqI zXLrD@V8zTs{v?$k>_uQZO&N0K>{ME%X&9Sdt;jrR>Jvu4&969F*@dROJvr&MKKuB; zG|(T{HrDQ}-R1o+8yNXg2`C@$f0Gu9B>nQvozM8OaqsSC{JgE7{M#gI_V#BRcX-)! z{muse_Zd;KcQ)>QR;=G-@PD)oc{(dq08jS8#H5&Vjwh{svXng6Rf$fJY#pe3v zMu-30DIW85T(LJgnPEBT6^~^yGQn%LcrrYKOw-ja5f~3`!xzMTQkQ9T`MS6qU6`+0 zj%%r3kQ%Npwc>`yx8NQY_;{&t_>9rHzv5xyI9c`he&=QJeQ&6mv&9P%F32Ew+9wzi z{sPhCbv%^4GgucjOBhGpuU>YfC`Aw(>Gh$h$AHT5&>(FX4=bO9UqiafCFUKLRYce3 z&6j($pm?o?Iys4xDcZrc7_?yC7+=*?0HpZw$^H+|U+x!MJ3kdaZtd=F?d<>bwQ*b7 zUS#36ZRth9J{#pQ2p!|Z@x?L1v+do7yl1n&_1%-FPxgP3pa1cb{hjT-J(;D7t>VSj z?*5a9FQ1~Sy?D7xZ2xuyA+bIUa1DUP;VsGm|9FHb(cvc!H9Wrd{;^_HA%t=PNaaaD zy?R{D+BNCiL<(_ig@_1;T&97A;v)c)zDZ8^ckkY{U5yiLeb5N$Ny8majgRWEmB1tu|9?gH?EwIZTO^FmsUKGzcahV3; zO$HCa9sv>W974NfCSk9J*j8WDfeiP|^hqDH)*N40r6f@WTT5R743yp}ifypmZc}BK z0l;HpTDs{8j;nwUw>j}Hr`$*`imib_pLf09P-}STFvM=uBk#mlb1s24q)%H6uy(H^aVJIBN}5ORS^5j5P!)U;R4Q~1ETc9Z>$rLml!Ub@(0RO3%pf96 zFe^{w*5MVoo2) zmAV`mR9lZ~NHL`?#}{J8SvEMo@@RSO%5Svfu>RPTc~LGCUcUk~N^JV^1(j8-0tGgSH*kC%8z@!Z*Q7och)G~yb#M)a~BV_Q6_-Ag-`RL@p) zlW@MdC%n8Iqe;xB$sw!yAJo%)XtywtsNZ>Z!olwpe4MZey5aQVcMzdhmT?$X@|FCB zN37O(!s}PVS3}ZKW2zD~wS!JPxADUgh=D8F#+wU8epSBOpcv;te(S+(#rAZUWPhj< zn0{>q$Ohap5`pVCBovh_;CBoErVNa(#zPU2VrzeYcQ2f^FLt*d?a5FXA$c%oXj;XH zB}FD(5)r5No8IL+O!3q+S4gPM5V~Z5Iqqo2bew74imRE{X7zZpaSSQx_&7bNR0C}q z#39L?w!V&%L9}MJSXsoP(NJ1f`q$Q`7y(pPsiiS-G@w^J-)6hX3q=F+6HI+;KT*=$ zrm9`JET}Z*>PifWWaIqBx6YTUuM)u0*#2uXb#slhdU%R2umk+wlvM{&x&?;#&cq2m zLc;W@G>HQxx?&7L``5!pIetNsfun-tVnZG))9EOI`Ju~GL}60T2;w&`1<2q#@xWRJ zC=2O5e!1fqgYj;lu~WTj4|{7wliI1LWiJtTaS-3cXJi8*FUP=+Gbtj`3sKYtn!gco zxy$3UW567}`e#R{tm3=F&ayXh>vj@eDp=?Pq;6HTiSs*X-?xRW`hWi%I8d&nj3b9L z?*`9JGtEf_j#tL5qD73;2vf7+2-nkSiIbDno^UvQoU}#mf+~Axt{VK(HK;UP+Pt%NCrW5& zx^lkqikAS60jRoRR&Rdf`Wu=o%RJ>Men;|h@(Lf6ORU}75}ft@3N8}ZAx7n3Nwx8G ztr8gJNu=MFe#Gs3T0xbQ0oGDUrLl*Fr5i1^^1K(kFJP8_b{Kr4mJJH!!q__PEBgV# zl{+u;HBAwh48-k=r%qkFlOAFEHq^O{86S*B3M7Qb>WvY8QOcwgdem^=2See$a8HL( zNEFEw#a_*G6neXOx8xpO**Ykw|R@@0u##qeR&*Oy;n24HxJ+3O(V<+8_{ z0Gk>WK|4=Rub7DA@swIY;`n@Q+Dj(A{|VeeHjyPIX&S{+T)B>}#z!!->1%1k5-4m< zR*Y@=_lV5mJv!I&JQhG{J-8r-%gbhBp#mtXHY3_!=?|9$7y27yeRGi*XG}iy2~p0AM(Bm6Ups|F(#p*oP|?YE+({D` ztA=Wrh?Y-vUfN6U9eya=`P?OuS|e081;H63IzI^5Ge8th9@yFbVYwu5QIUw z6v~#Pv~=r@ZPtWGS4X{ZUK6>`=0?eZeXVjFUGj{V*jGEHU-tg};>SrB&Av9V%d-b& zY?>3k%?<=&sDlTuhb%s)zec(CpWW1$EgE;(8+B1cj3vYZy7$x$z5@Z$-)AHr3 zmph8~1z}Vm!Qq86CzT%*)`y}I;Y2WyfocTBG0I?vBfr#wk_P3cly#!C)O2|D!P4o= zv(Ziz*!VF>fWSQ`m4%oTs07mzQl@S<;>#7nlbCQh8fLzmabq3W6|A=$E%D&^1e{G_ zlxfAmYNCNixI{WXW&h4OL!|*#HtP_~5>T<68J+-G>r>$l`By!O=hATQNVU3({oD^f zwf{sO$HEQ0jI(_*O9BdJ{t$vLniiZiW+wH>&#ZoVGc4@>I@`*{Cc@z0Tq?Ir#-#)V z=o|+A(jZY~My-77uV6-bRFS>~R@7=H5%a9*dJ>T}kSdAD^Fk$Z1EUh|wN2lH9%;W3 z0}q42Pu!p3EqmMVW@D(nRV13lE#zI(y;MidA1Mtv4W4YZzJPp*lYmR-+>%`WwbQlL z@`IqE?jFTZaMq(#j~0O&iH;C_C9s*3&tf0=_OORrvOk&xm_2^U52C)J)cLX}wA1+ye$97bd}Jw8nDWY!1=W7>vUiqY;SBUM5q8Bd}@qI@+Ho_oww z@l^}^&|>ovKw%OW==`M(bWXLmE6f=Tvg=DqS?2&C6FX=EVRbzmt? z!VK4sKvVM?-p1ZQM2>QXn(X;bB{KDLVzx*wj8HTQzVvrN*g4^1&zle$AK{}^#+#)p zcM}p*%DXN1jm*otK4j3BDgqc>T;5D!C{_cY2d8)4Y$! zck{&idOZR<=9N~e#5=DP;*cAZBA(J>2Tn~Q>n(SvKBO$m-way##Eo1I5%enD0$420 zyF>01@0Uvp6IID$l%@_?KyJzJHt#hwtsG%aGX1ZiX@-F^!~|_0UnR>{iP0yZy9vC} z*AO##6Y>##zMO>L>J&jt@B6W3fmjX(GuFLV94rwcm{HQP)J3jDjZ02a8)U(45k%TN z4Rca>UaBu9v8Edc+8^f-x0-Bl!b@X$RU>1$`JFb z!AX+8ud}}W50&yEsN4v*Iwb%j*(c#Y_R%2wn;|zt2%Gzf{UPhSMl8nyFMxrfO}=r(OI_aXj0xs%G+oR z+~qSTX?o%YH2;F4OhZZFoV9J{>}>zIr%333XwbRN%JR56 z|ARt;cQ%#(aqsS(^*gukZjk?Rd*j~5Z}}f{VEm{2`S~C81i*bUqCkb|ggVYCHhwlb z9<5C$YiF}tAI5LGgUAoM)mrRFRkkAcJ0*zp;&6>2Z@CZL2T$Gm)X^izo>9EK)0CW% z(b0X$c%*gN&+f(SY&6lYt=1QWU|EE>!j+dEnwxK__)CN^C)|~$|Ey1m2g|KE@BfF# zw$kN84nX=~KA89$cJ*N`ck&fxllHL^BNZv&VHuMDowzH_R}~0GV||`dYNXtiY}b)LrL>}%#{!3MUi_^ z%lokN+@hyH6bi&xD;{4=_;_LgwGJ~z^rS`4&9r1ptLkt;N}4jrt+Lyi^dZdl9xcyi zRi;>>$@orv#hZ7dl5mkC@O1_fHOnD$_W;&1+C)2^P5h^u<;x;*{Y--Qt0& zVWMrNa-Z69utuW0B4i4K@az)(tDW%W8zmcc)^B$xgumCF{R#2oM#ywW$n==kc|N>w zFm!_9Pd|{QKvskmOUXx@8b4DsC;#hBnw!-Z#;P_t8@G#Rqv4Ggc3TVcc{`hbykBe` zm%A?>C|R~cU`=nLcQ`uZ2SUp$TSqNAMCX(K>4k@=c8GN(6iz)G7Q24ulc2a+UF=u) ziznwgzFzUboV?Q>_n=F`1wzM$y$9G*r%a7vJS<4p856^rvWLmoD7V2l+VlfYk+f1Z zo$gzlU#eBY!jr)=XLP6-h-EQXM+wj+$+vhwY{j@Nyxp+<^?Mzz{@%sS&uXK({h07W zE{fgh9v)JPqGLgfMu+AOYO$$-&0oi%!~FSx<(pvnCoFKiy1;w)i$}y%mFmWaSdUD~ zMumIGAb*_XlZa(9zNP2alln;OVTjehD(>3zjuZQE8ZNgI4Qf zNE|`=s-Sh`Z+ z;qA2=)#jp!fH|J&wt#D-1?FN~^SEn`!EVD|f-LYtq;>NtkEVIuvH5OK6~}A{lPDsm z#$2U0HkkPz8+W+=Rq=wlGBN5VilsK zNX^<;O=FRP=HpbrU?^ArB6EfLQ?$d!is5fM^pQ9rz~iex$%AAh=Wcz!ZD_s!dK zzs<+H@kz&PtW5HdDt3dekmrO|pp^WK5Km#2-rt#LNnNi5f7eQZ+>;bl}t^=Z|+vbxQ~R#4#c%GZm%eT-558|2LG8 zJ73-XD%Bk^%7%q$N*yTMQle3)E!9&_S6S%oPZnMbts+V77!xHD=bS4XS!)OW?e+*7 zWCtJI3}})Omt9vsD-~9PE!|y_buXEc3gxo)f8XAI(Ls}c`h;9L8~)}4|uXyTWJId#UhZKY;r^TT)ELF_{~l{+qOQ~qWVcByleq@;L;@sv~X zlq=?$l6h-Vij`dJy~kE4e66i1Q79=DG@B9+dd2srbY-oCBu#5A($TA|Q^(&p8Ym4y zdBTki4M(Ob9es8I?LnUL>i(U6@2IhZhmGsnbfy=CK@5*GxD-!rUr0=WS6$JcH;eFO z=K502tx#FR>`SgZgI!{&9$YjJY2Y#?LrAF1$mrWRv^>)i+t_pM`*a_w4<$4<$W$>N zhJ++kGAZL)JbeCgN7*k4$rm(*4~rFZ8fP>98aj~*2NJ$#FCrY*S|=t^HI-Y@V2KOx zI3w=0FXN0%o~Q#Njg`cg9EdxI*=0Kw$>!Y)5>b$88rK!+1Cn+czn2J4tb~=CiDmlJ zTaB$1ajpW!PLAepTv}Jfyy8Kis*p}*S(G5GQp{SHI-N*g8Iqmzx=J4zvU7Acf~3b; zKYhOQea9-IHl25M08yjn;meRU%0G~lxQ?T6LkL*?`GLRLP!jzFdL&8Z>H zbb0d=G=>`8*!YPjwFU+|p3K+UE{a?_kC11z_h?R>QJ=Y!W zO>Q$Npw&UOPCCr*cDJ|w-Kt=iUiEK#ycR8@F6N)u5%J%Le}}S8QRlZ_JXyu*Z_!Vs zs=@P`3qyU-V?Nxxw|3@u0&mg>e<81Jh4boz{D)1w95SfL!L<_6gL&0ZKM{7`X2uqY zsO2{rr9gjGaXGcL^DJp07u4T6K=9KW0Zo0Hu8A8OQJG7M-T9){IT6mxFO?TyT+R!! zH8!>+(lC}#Ull7yeHUp^=W&bG%4>txP&goXF|=Ac)Q{=(PddoX=vb${1*$3OQJ@qS z6*3G!p*qr(UAo!be)jw?$gP=i6IzAfhPbOdhIy}$u@sB&=KAeQ z$ROCwqPQ9DuQq>YXqk`&@go_nx&vxGBZr-1m(tg&gk+U+5Y{MJR|8XmX@i}s)(&vG z#TsPm*&X`;L=f;f_-xdyS-)xrKybt;%-r+Cn%wIw7@_1l89(Wwc_pJL?OaMH7n zJZ@NNT2@lr_(WC2u!ZJgD3jhhfC&FGT$oznAqu5=VH8AH-bAND1%QT>po5Lx8B|W( zl&x>U8q?xP zh+yp4A^1(ll=43pXl!G*lz2QX5>U>lBqmN0D0YI0L@x3-aYhOA?%0VwG;$ubjiUyS zMQ>AN;e*FVXsig6{;;BCN!PCc64szfAZeADiyL@aRhdF!=9pnt-mVPW*1$txi?v6V zGp!fxhYVa|EmPsJ^eN%3ZoR8&a&804DNXPP85o0aFp#M>Ob|{K@9BNJ#Q)QzgUSieSq}cj2VYkv=5>CLn>QoeG(5du$m(IGEf{@$=7d(5Gkjzk2;T86zmH%xX%qOR{lmk}l%7%A&_%5pu6B8`E2y3L8X!a4hGm%4rN4Uo z)ySvT?|p&|7AK^f9b$_~wNM(Uiv|YigyE7_jpgbV0D7Y@elQCp|31*Bo{Wb$i4gl$ zv|0$Au~-K6>bJ zrBhasWDtZLXvw(={?nNp&#(`das=A4u$mz%7h^;Z?J5B;0m1<&Si!KxAaV->FTn>;+ur-G#N^@w!L6gC z4-D($8;n!GZt-EC@+FUV;9dsWz_~BM%Zz|`4b@UD8zEprmW!m34=R>0I>G|_6Yqpk zDrMz^jbqPLB*Yd{=-R|66=<rgBEA>lx z5^aLGLJa`X81|iZqqYPt7~#nUI*fMqCU`cs)D$5=MZ2SI>Ue#hCMmsXk}uOFrCiZf z?i;m`3597`)mQ!qidAd%s;Fow_R5cMQRkAc3~mazDR)z^DKt%N0H=hT?J0N5)edtenmXGZhY`_ymLc;lw%WiPi{TbkjonzFu}*Jfn!x(y_R4; z6&MYCsgKO=swl;d9npEC$n2e%4W|B8id3M3PBHL;D)79I3bjVMl4l5@DW0)4?v2Fn z@}^yASFd&*i|@RCO+jdBA@;QMinCBCYUbPBL5oX%F&vM^7nJJ@twTfkl2pVNDkEgI zg+RMSVM5c$c$)|grx+;A>T+RP;@AwMCFwqZs356IndH&*2a+EV23(nS;Zv^m^ltl7 zHq<2qteGv*vv|a%8UNw}nOSnov)Zg5f8ABc>gF*Azwt<;4S|@FAMK4Fq$o4%8IcWg z(tuv~IvZcz(1IxR+ECnvk%>g1?6C|YwEq}4?rtvja8Nc*0lzDk37s7 zW@cgCka#!%%($Qgq}MJkyi)8{#gnaS>H;1$Z1c;`#vgC!J@9Q3ys`gI)p+tPiu%PK z6>6oaVcRA&Iok?=fISbl-`##lQ0L}4&joD!af8ru-eCTUrvo_L&fj^tXZpK4>xUGN z8eI(52L0dN-hTbFp~dV>L^afEr&nHiT#Qb%0nEo~biUTw{Io669m!OZ9#VkO9Ayyd z$rSu5W^DdyZGG+T`byIBZ@$r!ufDu}_xK(UWNhBP-MMq;EAoJem+$oAvaD55Eyt|6vhvWbR?9^+ z1R08~_2$Hg&DXE7^Dl1rZif)Pn+p{?h(RivNMgQPI#`FOPjd7hS< zKMfPb=i$UKJrVC@#SQo}*tgODeXu`s662r5=$b^4Q+hjJJfL*60_k8&X*e^J%y*ve zh?igPFbhR~;sL-ORv$ECAH&P=MAXNGjkZcE{G0A^@txn|K_9;C7Ff_#fkL{m4@U2) zQf*xyaI;L|)Gdw@@t~UX!Zf_L1C?R~kvR*}Hfc?L#HZouddkxS9KkRuG9)VY;MvyB z%dMvm>|H+<(y|jj*IPb3ZJIcnp#KJ*HU^<&ZP)Fu*EaVlV_ zgcNh9#un2&;DZ}hGC+b!OMZ0r2E;8KCh{>tq%zzWD8^h6ite-aj89Gi#MbpnB`~$3 z6mBdb&K=a_H9sDylGk$lhY4_6_=L zMq;yOXE$hTMt$?TXT{?Ny(-TA8?@FjtW12HTn2>raofxS$9J2rKjr@}Il`q7zG75@UM5$Ndz3TSAzUxu){wP}@pgN*7K~ty3vQ`5LLvF`|_sXQ<+5Wlt!c zkRN3X^KK~Ay!lrwNnfA{h#>g_wTOZI!s@pRd*?nmGE!&qi0DSr=2F9>jeN;Fjj2l# z2cx;|<7*rqio`LpK;Y|S=F{X(h-}DRdQha2RJZt=WBq$Q=ZM~H5`|vMR!WwA6rbtu zl7IU*f}7%VEJCoG!grKBQqe~ej5IOC@ zP&FiYej&q*{1o~{ufp+|v(|;kO3N}*$e|A0EAa5BzqtoukuK3_BDxq0(qhJ?A1FqI zrbg3-V`^%b9%UK$7&*C2AJNfpPKslv_Zm&EJ8UeTrI2 zHMoX#HH~O*+zASOmo)Vg>axM0leMi19{Bj-*AN7}(7kWogm-O03E~Rg`#RF;*>`XP5 zt$R66Dv-c$5mX{Qb^F$C)8ZnjH!n}u@`+64X4pg;j z(kext@>1DG!(>`h0!Za+hl>No*IfBhHJ+yTfiKwBpMIRJ&W$!pI{%~u@~oo3%rt>w!@_@{-ZY;>1L+~bJNSy0(#lX zqWhc#SoV6d+XP-gXf$u`Yl|p@me*R(xtaD3UA>7t2g4)M2*Wsg&>(rE*efaSrW`PBC0@pj1%VPgN&kMsVSnJ$Iun_J^Hq9DZ(0ECyHK&V*Uy zGr$gxwV?P2?De&Qez&Itcr(h(16q2lpizSIp9gTg9pjI9uFTc(wnr}X?pMXe>n~ME z>;YT`Vm<#E2(4|>0VsCk|!X)NNIP>B}u znNxr8edoG%e-pD<;EPTjWU4lj0t2zzADC@KA=sP@hKfqc@Nhsm&vKGc?ZwU-7@vyv(bQ?drByADuG; zy=R<&Ry_kwu5z+!g(pH$lQNYok`OpQh8HFNO;0Vboa)njVhVsn?Fxp;`s%4NkTB zC~6nBp~=T?>bwkh`W9ujWNrRD!E8}0x2dew%jsKD()Q_F$sEepB0d@B!!d>^Qj$2^ zajv?0JgVNargIJmmCxyMcQ)Onq#$T9Vsn0s5f-iN(>TLa>!G!0%1dK``$g&l62Ayn zZpz}#OQj)g0uk{swJJ?ct8XGsel-SM)x4`E?W$k8{7C8Xs4`i;$$D5&7jku6@0J^6 zNHyKl)apR*gsheuPoGzR3JH9sV)eSQ=(Cy^O&?&^yzuJa{LVfks2VilS5fzXlHGUz z52NMEt$>in%XAXGTbRiv7qBcbtf&oRbY>pI5e;UInu-z$NGmAX zB`PAJqvCuMIAGD2_oLao+?-Y5uvxeqjeC7Uu9_4N7q64SKxrattsfN?y(oO_kPD z#p>BGhJ4ydiFZGkNk$m?c?3@p6<#Wf$y;f#G~xV~v2qMXr$o>V(9a+UtA%-}ZQT~> zz$GjJRF3A5N?i?776McpNFddC`0li7ugb2#WGk~r&N5l+3PLncE$lI8p4jt(_E2@qj1&-seDwy$iawwii!GLCivzE(n5W=6!COfHF7Ks=fXld)tPO^`I&_Q7RkoRcqC8{|H0x#TFsXz5FlRzj_xHm$bY#~Pp2f*n2p=kFDqVE zrD5laygeD#H-F0mWIh^<^42H^7#K(Vl< z)OX>h4oNPOj5Rm!sI#777{2|+5cQqyBczGjQvm>2C~`W+|X+>29MW_tO4Y5*vbVH;Sx#LGu z_(ZJ}c;g+yBw<6y(n`9SWXWpd9O-P)^3uvJJZEnoPsiR`%YRlwC&*jza1K8cnxkbO zRELBb$NWiA9?7T^tJf9M9_)VD%`*45eOg{D-AruB7{mMv{VuP9|CQRc-yr{c9fkZ- zPPNxZ!JAjwF_Tk!+{LTha66i}XW)Gz!B?IlB~Wu+67zQ?a(3lgHZBqa$R0 z9B0IU_>FW`O0>okQynpNx$NGe$AaW&LQWo4Jc^EPC_^5P8)O7CQA)x;xJ$^M^}AFf z0L$dhy5&p_<116b3_eLuv?Ww7w(gP=WTgAKu{^10KmTQG_sQ0GPqz!6^Lx4ddTniO zrLLLzTgHs=i_^LkTIB4oO@W&%ti|Q=rzGLPo|w4wV(ZE7o|UlhEK>Hb+P{@7r<81Q z%SaU5L_VLX3z7#Wnr))jQMnr?K*5z{zf}y*SNSDecAOhsARFdqUII zKPP0ap&#)Sx~{cSm)Ee^g|9|0i{^5{VQhQfA(t;$x>lHM zFo`cqSq3Hgq)Dnzm7)@*V?w*48w{>_V>3GS&_$9iK45pQr zQWq(i+1@H0ZHe3D<&e+srZuazQfWbe1tb9y0(;-33@$Tl?>sW|cp1T1ZI7W|ncbMw z6v?V@C_X|h5-ME>U8D1l67D2`W&g!}p4aFNilGq{#h+awnXC7Ze* z#r~)`RqnDUD9WJbr47tdCht{P-JIf3ij@3ErdxPdVi{{9P8()wO^G$0+ijw>z6hXP z{9J{~Yunx&%?)dq0>zyCx})HrZAGdp=i*e6w_f!)MG2Z>6?OpTtb#g0!nlP`DHkZV z1&m!_jj&4I=i_j5z@LU+;f+Y091Ad}?4Q|xuS8vCN>!GY*sph8AOa0=Y(`sY4wYdQ z62H%xrfXOI>Ys+BI@;<)c6%iCY8TZ_m#9!Qph8P`hYAuXL{zYj_)MJ$g7@>+;EFcvU@^Yt$< zK+iI8aiBv?Kd`p{hOXLhiq*E2qz?}ncvfvDSs7GBuq8 z#O9ETwWPt;T8~Dky0ST8+LrbFq7M?0s`*aN;IuFZVe)dg*2|f|G?a&Y+*2hL#WxaE zLRmTE?0g~v&LqnkXkn>Eb+@IkG$&`Ii8}^z!9|}`48X&Y3C#^4pYK`&l3PXO4oEJY z3W#hjyELi6{Fjx8fJ#oWqXoXKFYSeGrd)JK4+`|5; z9g^)KNr{+`l&X|6h9bC9>q(a`nWjyIcF}~a9*%9{cIleALTjhm4wX2A%sO4s%S4_S zx6&k{z=U`Z&0lO@2YRJZzTU>fZSK6;*k?2mqYo5I+e@8Opr;*$!EbJcoH25Ac{J!B zmG}-(w45kqd$ugVp| zSKV-Z>I2m-PpN}^_ww=M?cL7v-OkR|vu!kGCI3ue8at07r6~WZd)9;d_a^tLB%qt& zr(MjzY6`;+s8JwYn-Ny)4U9>-|JOalF^!tZzItkvn+SYD;_w%97|+`9wQr-)mc2c# zJmk2%nMM-5f^qKP6v_DM*6#Nd4B_}wM2B72#1S(i6~)!IV#eAcrAT&;dIPv^nKmVp z)p3t-oBkm%JHt}Ky0cf<#v**q>pnnXX&o{<8+r!q)**}w;J z^m1@xzKBUI%i2L^!w%$$nt`Kn$Acu4lm}6ZYZyK0JLsMt9(OCmB#hBc3Nff!fPKA_ zzH1+c9Xms^0-j9Ef}jTFR?|FleC0pd(zwczQY=`ljs9q7LrrcxKUeA#E&pHk-fcUs zBT3V&>v)NYg=!T*B?1(gQf4-#OeI8u5~p}j6JQ=%R0<#fk|=@zgaAacre{{(&i!0; zuT^s~b2qOskCIO^-~XGrZ{HC+KtRgsuAWXViHz8>-QC>W+|1lgUTOz7y%)2Yd1c^9 zMu^g|_wS085;576u7?HhHb|V$YwPvuFV#a{r;_|-QTI$g^4X#!k}PeNZ4E=OaTX3E zo@e!U9yB#8XW+?n=efB;$QsOq9WPh}DQy=4RFEGYoj7hZJ-!bGw!c+`U9fDKvNsQX z%SIQ%&v|30;N-vANx}aX&c)7VDH`HdAv@==xS;U`L~wk%piM_`3&t8o;sry&n-`8% z5`f6I817f3Tv?SYIL;JIT3*zxc0`TDoWFe214#YVNg7Ct!+t(MgDwD}H9fCA<(_qkADB;^oIK>g7(61YDDu0-J|(&?NP ztx`Tl-F%9`=6rnqytsM`N%9DLfNI()1)s%<4>seM1&5rh_>|vbH5z#|EBLQ@8S8$C zqlQM}f7NdAFY&o&dRM&(@Y261)Ah$>c!Z4S&Fyoqn>w~K))AF5!&Qx|JtFZbRobXw zXO!Wa`r3!Mv?n9?rN3EcbL~G&nKHt-W*OSFl+>NqWWqZX^k9!nKgIctBH1_&6D(F8 zEGmRY|J7JI2lw(S(ScON&f8o>KpH3nRG!02;9{jFhB3X1MMy_jQ!xwnSIIG6j8GD? zuZB*1>5$3x9NcAjh%V6B)Q}}&n_3Bx^5Z`5E#ae{!HV`A&AOht(_7O$?~-K`VK`)0RQl3pUhcRXxi_cO zjwh-rH1Ro#B34V;ER&bV$FImlgQvjPydHj1CNVRz1y!N5vP;G_4z?^s2(;lXryN{e zFT`E2@Rday^Rz{&=IoIRXR107I|2wQbG>GDv4N|g%>S-LERt1vPB>aq#exwXkKy+8KxOxb7Ip?# zy)N}7?5WY4;*NfFZwpT$ah^PjfL95TKHansWgPJ9UC0~SJ2wT#RHgJ)GSm*6#NO<0 zu(mUQPW4IbRJ|6v_3QU)@hme)rU~*v($eJ5zSucAk%B-6`u{@gdco9%>|YJ|(QqLa zR*)Ey;X1Q*v@tkR%x-?qeK1`r5_+Ir?LE0lvqOk2yxgvD_zVK72r`k;Wf)Dsogz31 zKAz3(uH*z);|m$jJgCpIoD`*rbQx)oIY7||q?>hkF>IH|a&0(Y0w`*$&1Rb+39$K} zPN=E7f9k3w+SNQ6P^e~KY!73Nd+_#G>yNEMQz^bHdZNZox2*}XR5p=Ek8B;1u5|iy zIqmT~Sme$R9L@B2b-cg!VvRdwh}wOD8~OMQ#bSqQ+30T+KjZ2FU17*o@8o91J>WXf zse(7VW_|Pay?dUX2hNZ!9hKk+6eri{wP;%2fL^56c-a+Ngpp>S7&Gn+ERiM6^~T_? z*N?|*TQ~Hd9P~H8j+Z`PN6-4KeEE;D3uZK9VqG%KW&(2AtQ(*OTl#v$ z(&TyQM}t~NzZggbdtD7YwfL#PDNbk}ccQexOTPypT=@1ZK0nMpKTHe2>eyF_YhmQF zcw+~E9F;DO*?HbIK$r@~OSO-x$fpR+zFYIaByKA2HEMPTp-FcfEpm3OJ887yIf^J$ zmBo~YCyx?03%H;@7qY3)xX-~=YoQyS$5kJM{5uuZZjlhpE7qwpYN<>kfvtTMmF_^- zXQQSTGzXuZqsnYhw`a z7*)b+QX0xRp1hrv15M;(Af20_D;Vv)&?TJgqV(g7doUSBWVb_WGN>C?O2Tn2c}0Q4 zxn&v=Q->ClB3uh0T136MhFWxyTn9XUF<2dm$qvI5cVmw#&Dm)xY2S<53Sq>Vdr`71 z?sz~Wk}2+uLR~OHFwiJ^M@$_}mB(v~Ou|UBxUVbkNNiy8NU9$J@OL5VylPLDMIt+j z?-eJbf~OKO-3;YjHHB~-W?1kD#rl<(}OCh5*V9kcL7{<0^sC)P4wDp zQ@Cubqgt$uBr}>&`(=%i-4ULngB4C({yEr{m09ApTl9Fx1O?G?Bso>?cSlon<`i}2 zqMf4L*g%wUmtfC??v#*Lu`u0Xl4+pH!14T2hL5WlII=XnDh8PSXC|Tj0UNrVdlq2@s`|tx*^C>)>6RqEDh&#q0S3CkVLxDpRNp1 zMi|n;(mL1qmP!y6G}jas?jx^9tExS1=HWER2uJ6!wvhO z19fGWMPqFBQOA*dq9SUKfI!DN(|4E8k>|B=Z@ko{P2xR-{6{9d4+lY3QQ?3=b?8E- zrFYC>A(~1F0MEBOkCPN4Wj~@D`R{H+`xmX&N8K|`D84~}uCqr9IvwaWX~nrjd9cnO zR&>wLL>@Pe(FNxcVA3SI;b%%8Uv;%1ZZwY?{n0J?m1rhjUcv4uqx{hem6ys4pk{to z>e8JFL1eTbRS7qF8#^b*64YkGwj~L}p^l5ZD!LZqNqew^hwb!`%nVx9YiUf(wsi4Rnx%( z)j~Fkz$9^l&iCrtLjx6w%`^ihd$s4DVq_>awL9YOCZSK!X96{7zg)rGrxiA2vtVau zm~mC;fK!qd*x`TLOuf}B;|8V;PrJCKry=)ByxhJGx*pp=Z)PQwD@tE>bM)CV60?(8 zTy^`vgkD;~na$Q@zp~>h_yg<17m;G zAHX3>b+(?F@?Ci>(5=o-bXSqt_)jEzPc@0T0^4!8?7D0XnlxEF7ONc+b)K*xI340w68aATLA?j5wGH@m+DSFK&_27glyqLRqn>m z6n~r=6D6xrn$M2IRt+B_05*RPuQogbYPKy(1GJUFF`U2@>a<55ABz0Q~6d)Tx_$& z9GJMO$Ay+sYBd`Oq2bX<0>c4b-iz%qq?(e8GOL_IomoZHV|Cimym{BxTg;y7vF=7l zIM-xMcAh;$J4Hy~_d}^5u$k$XOEd-yarL)>mL{OvHm|U>*&A##f^Y8Jd(34Ij~@Tg zhq&#zj!0RUp5>whdx#!790f-&cNC+RP&DqP?p)9c!w=DR;L$%ElaV^*fT;V#dyNeT z2R@zKbh6vNem*_D{`~m(+0H4sVI@EEr7m0qJdXIi=JoaEhM$SSiaE%;yJX9!%94J6 zt}}9-;x+xea{cMk|Mc|f^3$i+D%CKfEnpAo=bW54^L?3)-Bn#Qm+?mT!Z@vuQoVfq zFmUuhE<3JMR~IMZo3o;B0tXXudoMPL0N3hDJ!)q55XPh!vtw&LtSfv&2Jv!o4Mni` zO2^vUPaX{qQ=D~ug9W!)u-lK{Oefsp&|$W1)qGw)KYKp;)q7sEr?`X8=W6l9K2sbb zcqAxX+xOBa>h>~}?k1-3bvG8(#rC0~9VVCHE_Kn%1_`>>g514#e?zGYYmEuTR41oe zoXd6Fvo37U%TUlR#@lwlVE^H^8`n`HwRSoJ7!EbCY3MACB5|3GaS)lddS1u03SNS| z05ZYrh_nZ<`!Zs@0wE`R0nA0GC>>p!Y2u{}u_Z3#(B`sP9Q4@1@P1+lGpcPDU1t$> zCOwEsRsPcxg{-=bE*d%W<}O<9nRmU;nfcg-?p9 zw~k*3pi%}=_aB8pF6+r>qTWBDrw1uITPA@m6TOB7>N@7Q4!_N5ssW_a1K|lA>pV20 z?{rJ-E+Rce(;#6X6=YStV`>`GKNr^0@z&%aCf80t1iW#LQb#QaD8~maEj5TLi#$sb z-F~Sn(9JaUA0~94oG)AvFH?Cqejc|^$0AhVd_;_M)Ng7p-GMT|ip!`>794o%`1Sp3 zLr{lA;^lUs2HGaXqW;;L%1Y!8YBOJO6&X@I6Wb~buq+{WSYg;jlegJPH_D2Uoa~df z4+_NYmoYHsGM(ZHl~UC9!U`)URa#dkwQ@UGfPx$aTP3;zNB715kv&A$Ij^x1ddSEKV zW*!L@mMH66TaWL3@q~I$h-wuhLAoouz2D^&r9?edMY>~JUICS3UAgy=1nY-S9*pm7 z{O;cE4NQ>iXXq!sLiS3Y+Wi3;cKE{(fTiZ5=~a(JN# zI>L{jtK&3E@mj=S&5um2%2cJ@mM;!&2vyYkFuficKcqoT<1I&=wxusV$5Lz48%*2o z5bVZsRI=gOD&-AT!q`r8TZaRj38Eg&12ZaGV+frc)yi%f2f}swQTS8T{U;L~7(M5F z)Uc)UQ^T5I?p}alM%c)Pih4X*zG0vnNz&SzzA=)(^2%rhm&6Jlk^mfK1IWme%kBvF z=kbGS_!^gpSc}IkTzu~=DDAh=H&zY|*nILINyfa2o}3d)jK$|(iy%tUpBf0+tR&K%B>#ri3(|WVGp2;J{Lo;w`x^jqO*Gqr;@KT#$Wg97_7A#}D!2e8b7GU;sG2n^DE|= zVe^i;NL$SHA2u?5Jmq@@n5TzOZBAes#qN=`YSEN$^Lnsc{Q)KnEi3A=7_KGR|I zH!*UZoObXYbJK79Wag`hzvO~iN8u?#&3U*_mmW+WZ)|mt>$Rs#AvSob(cFAT1q%2p zfsEmAPaaG@Gg%4(5C3$@-`l3HyP$r{Of{cgZ=CzI3kY_Rfc2}D%J~X!GCj;v!L8B5 zC-+y!z`u&X`eJo_|L&dD@y6!uReT`)b!X!~MDQR%PNE7m-Gl`aT&CA17+a za(nU>Q`?+ONA;2kQ4^QioK0@WdQH9(GA9qf%4G8;^~kv7ng?OOE6$4dR+bEV#jNSD zi5yg@NJxz8!xkAPqwJabU}iFJfAgpDw?D&eE6z-cu+oUmJd^Z=%pwzO0jv0 z;#S(qS)xHQqLdKT9~(7%gY1=HM`5clnLs~KQlV57r`$g<(h@8&*AbSzjtXWh8N*i9 z)}K&tb4{?$K*`kXe2#h5>h%6PV;b#4RV5{C+W|vV!!N-H8fJWBZls3M=_3fb;}evM zGoBEPn4G5Jv$$923tl)u>T?GgLcfyOKOH3H5vlA$``DGeZJy1;Kvn7di zZ}?%N_Dp8}iV`u7`spsEn0crD8W+{HPnyp?_$RfHSo22tD0pE#$O1ff5@t{T6Viac z5eNK@Jm7Bx0)HbBU{qSD3os&*np;B_?cR~Ky}4B?ZeFp?^zyd7XAOql=^UpAqoABl z(3YtgU@dgOxz&3q)KH>BQHW}BI8L)@qm*jgpUPH}dQ+&SsVi9|m8@ratg5djYDib2 z(A?4o)@o&^C?$h=ML}Z@SU*1clxShPuZc_CHd zd zzf?}1R;u5kh^CRx0Gy(p3PkgRv4p6|B1+572-cLJgEGFM6S4M|zr$O{4vmUH@|{CD zkHNf+%^KP@<|r?F{HCQy)kp3;DcT|&ai`;NzO5vk!4ARJJ{#+OhMpsMwQO0)cnN*t z>Dkkxr_aCrS*R0gTf61;G+0tzYftz6DNR`_Morq01F)H~_wS0|b{t&1osPo6_kgGs zpqLfPP@2Jf-?H6I2jpZWe1jm;+(M0j>OukXsfe_NEY>KS{L*L0slDgFX=8cY$iiT= z*1tXjPoMoJG4;)#%BK&1gCY*NvrvY1pkE5U0_!+ShF%YdwP-6p>!#o5y!|2C0T!Xp zcm(tPmfcwO_x6P$y-3R7Hx`HHvmdSvO*$Zo#VIsKMd~~VHfwCGd{pov%0Ohytn={n z+)|hEG?;PQ-ULvHz0(nkr^iBsKJZfH>8!!C{q7~h)-~Q^sbn{ysPdGDO`;2F6 zoTRQ@Knw2sJxWr?WZK#i1)hyo><1u^Fejl(U%j}*2;@A0L_8xL@C&+g$*Kh5ns zKtwU9S!iFtvE?mbE!O56R+meI*c9%r)oOS=SE=bzo^?uE;nrG7bcB&9FwNg@AI(axiu006^4Z$mMQOJrQqFjA6QVm(1~t}NG2T=c&2 zHOHD}Wmh6^P>{lu{f%B!OA8E(3bGPCvta_-gg`~yImtaQGnCt8K%7HJwp8bfvI3Y%3;)LmoqoTiJ zak?^C95AD+8^8Re1kf;+yzx=@6HV49f?`x=&`%dNT|_HSpQ3jC^4p)ihhoB(ceIEf znW~B<4pX2b01IzLMRTaWAYC}IjxIUPpniQgJ{tduP_38a$;ZFCpjFUvAfY6BEW|PLlDU3M8gkK-3I{aQdKW3)uR(vosGBtI#A-M;1qOX`|! z%AY#|W%Yeiq6dPdXhU#|3%Njxr(Do|nTCS@_T+Ol6#@;$3c-SWkb46YiEr4r)|vpC z_31v&Q06zkoqUJk^4rB)Qf=Dppw!Vq)flf#inMtj0%}+OKyfB7OW2Z4mx$w^&z;Wt zt?({gFw8<2ZLiKT#7^|5=_j};+@W~}1r`qY9eT4XNfOeXS@Jhr)vq;s1e^hZOf4(r zP^d3--5Zo=f?(cQCR?Dw$aR9`vl=#rw*k{2RyhJ}vUXCK25Bx^Aa;y98+Y$LbhUjb zL=PXVf4w%+j}Q{OGx7uMS(_4kGQ|^yXOl6DZICda63WeCWQ;3V?3GZ9jA z9xlVN+n1SY7K|v@GB(7<+r|&f@)}z}CDmW6J{Fqh_`cE{tKn^We&G5-4&L&~z-2%c zD(gV}Xr7ZVOd zCSHeZF+Uf{H&FXCC}6wv8&tuDVl3PBF=t{9FgHMBMB%8XQjQaP=-m9y-BB>N)h z7u&$2-)$%^Qxz#q<;6Icf>TCbgW)J0aB*pP_+L4O) zhkYPlsJ#*Mq3!RLtx@|MnGb23K!&7miW|W|RDC8GhyQ-LgYz%AisA%?1AC(>2QbsS zu@rk3u8}F3?h1aGeJpA&sQ{zVjth@Sz+4E4Z>GKs#H5bt`)EU4J{NcqZM--nWsd?( zNg##MFD8htfvMt4BCRdacE<5boiq&L_M-<6)^*)tekg3LNRy7-HFf6wDSwd_p%8Z} z0Fjzim?|7ZNX4br$e6=-T*YwcI(C)cA``DDLLb%|Jn5$sDb z7JEkWkSk%O{*>c0cPR`B zErP{;+8YpBz9%7By64clC*eo0c-te!Ai_kI(lfi=-y;TsMt;<)G)-GQA@)VtLjQxU ziB*ug7TpHy?VH4QS+VXd@1(L|i#C!iFs_DJ&!gluBT-dZQvY;19?!Ku(~!Jydv!hL zI>s%hkM7^$m=Wyj;l}UvS&2FsV{8T%+8>hWO{k-AJNSGwqyz;e0nPGs#wXM-JA^Y5 zDWZ#1lQ(APc5x!#qymtdO`bNPZv=JXbK)4R>T9THglfBhJ0coWEOEy~rM@^v?LSlZ zI!TD}Dl%CCq)t?V2U}s%&GHE_K<1#X?IgVGoP|t>W6w`TG9Y?qv%)1Lx+DE5MtYaO zM&7xO-$k1t2EWrDu`iHSHWeJmZ4wTSm@K#G1&WJq#QHOPl%7>*ZqodK5`;2*bZTEt z;~T%5c>Ok~bZzwXXyv&9){^BsvqeW6F&`gxuz^J=;@%!ES}`Z>xAYNM35P~%a{zY2Xl z&gX!VXJCc&@(pa)1+Obz!0W9vRMQ@aS<>hss9!pu~SuQL@5xJ5@r$zg6~E3vCJHaj$A6DNZJ^jK3G4*<@ZI3WbOPCl`|F1 z$l0GeHduR#TidYGX?dfwV5L0z23^04AJRB9CfxZMxeZ*nfUZc$fND~`fc#n?WMs8? zBjy|*y1v%Di;y7iP zeB4&IS_l3u9JO@YUlyTG*jZ>VoJ8HzRF}-$yZJY4+OI7>crmf)$=q4R@QQHl7@8O} zTYUWM-!$e$)D&5M#sS9q-K~wsY{+b!xyEoy<%iq8D0DN}GUgPAB%2{h^(o_(QD_wW zd*o`s(f{m=`fhp{dt`Wbry%8asljK;l+*h_!1nA8W^UOfxnby0PR#g>=zawy05_9H z9pkjr9fd|u<0rUZ+F4Nr#kQk2fQRNFS2A3P8Vu!5MVZSit@S5_3TsN(V)2nk!efus zWC~CR+hkJDuM8ynLZ$_Ur-dKWVhvnh6b>ZGlb5OnPs#ealDFi&?Vls}G`Ibw z8K|+9#T#a&Ax@&)Ls@K3p1FwI7ke&WO%+EnAtB7X53I=m8lA-N$H(GQGmv)sdl1~Y zpAB>Ov^DZ|PlE+F@;@0%EJ53bC#_-794`^DfKA0^91B&$_*sl61dB+EL0abJ^aU}| z9i_}49a92JZxN?so+>;nJ$BIrZ}Eb7QC3lT*u+2Pu$DRv)C)nOE$L31^OK^rqEY#p6+Qg_gt=;KtuWTQgDz=A1sMG_E=POjIu;qvLvSxz=*96^a z*rwR!Db}`Un71lp$i%I>@4DsIfl!(7Si?wWi(zzsIG3i7gRlC-m>24%7aqwH3=@RJ zDTR&7Vw9IwW7~Uk%4Z>q+Ww)`w3@c2B}45z#Mks`lp_rTD_rAzsycR3>@oH2qq9o) zZf+v;Z-xTy>Xq&zJB}m#@AaDIQ9CI`&;myQC*afG)FcayJ}s=mQxN8y87*GE@t@Cc484T50Izq6xyX@j6?Gb#id zn!M(0Be)hvUm!jSVX{J-CmJi;N%QyDIICBLp#(3&7>k9YtZ2TN1<44^Q_ezEmArBd zMICFMrS=NwAqfQ~R2e4lB8;F0cC6iWCQdcyM!qQ?9QP!DP%iPvc-f>iRpo;E35|Dy}Ar#U5TP>Ta90ds32qf^`p&nqt+aH z8}Q5VPZKmWqr!|t#DN_ERRI{w?5L%iNu%+Ed8s&sWE4Zk<{KUF>HyQoA*ss?S6 zvz?|R)&}PxzYE=2#V%B1T&1F3Ka=z(;Zya-wN(hLwChdgLysEB(C#92QI*j1lrbvv zV+y`tvO{(=unX7vILiuN=vD}st+|#F8`cfm&-|0yJIZ0vy_tg5d87HhnidR4BA`rg z@uMXNorHN3FD16|_6Dfzq7m3U!B^RnD&-7fDMZ&1CoKDS5jR!2*DK{uy%WmAwx=oH zSaADOyc#8d+D_Xp3~Z5O7eihFtO#0sVhB4>(3*w%bfK7Cd{}5ZNo$Vk9hsmC$TOmM zQ|nozWxEfpJYHEaZ?R#@NLQD9Z1(w$J|`wRIo?Txlm??B^dcWh+qb><4}rcNG*?Bc z(D7Pjaq{BY5ng^mf}|OSS4A5nD#7YR+L>Ad*E}lpDO%ub_AFgpyMTQ=N6o`$#SBNDy@T~oRp7>@D0#-bWkEdKhJYhbpo0>r8l4tyrsrbkd8&LUb;z z=z4b?M~G0&PO_Lc3$H-Zi*q#XVIDbx(l0^dB{c%$=M$c zS3Scah1bjFXBsyeK|_=kxW!TTFr)_jr|MM^(So$r`8|T2s3<}tC1={%h?bccKXs!> ztAn-uwNRhHgbqj;D{8mlT`TBPrztH{fNjtF;Fd+ew z6EK$tX;xErMOyk_;NB)g24m;h6a~mfb3!ciHS9!T&=*3an%&AM2->!Y6YK0*ND?gc zKN*i%HNhHf`{O5+SiGW%!eg-b#I&=QnZX*J$C#BO9=B}8`+@$Tmw3`2vfv6SL2*%C zWvxnkS)m{buydHuWL_KRf z_3`buy7`rxGxbO@fVaXfDTpq_eg?z=5F1C@VDucV`v8>#POL-hUW~_h9^RH*H`c=A zdETtE<{-!;EY9fBUxAhg#cOtQs~fAOWF*A3fB2jU_4jRI6S9j2v|XXK#fOPzg`b%&#&@6Gzd!zZ{qe@bt?}lQuW=G=ZjM#3xcCczROh!SC;yG$?jZPfZ7svk z^2TpDqJ4o7D{&r4w4q6xVU^9CJ1GMAk|qK@p}}IZ`U=!>%HpZ{UfS0Yi#)V8?WGQe z-=_$cQfiHwN%g_b-@H1bQ)}I$m;)BTxp&82fTAnaGeLGvS{4_~^!o1f@8V8rer}*F zCzJ;V945mKA#g)aunAMQrsg;e$o)gf1;Ya?G5>7J?4uc*pF(=DV{q?yUD~sPP@+F8 zw`eP3kN5J+@SDk0>2m6d;`u zV1`2+Ciw&qUdW^%-z?-Y)QQ(E_sf$bS(&O4$hMN}U84|uVM!@#mN4+rdtd;H1Kt6R zf;U)hMO5u=e@2K|Tg@e)GLXdY)V8(sw)2J5(}0Ag8*;s~Ivq=wkK4 zC>9cXR2%P)HwRh3?NHYE=&5TCW<3E|{9;zb)+-fF|kbM_CxK7+%3aHfR-^{&-TCvBL*qtWJgXLb~YoEkZ$-g{EJ0D@?*Tpp| zsJXuk*QdVD0RjXIfQBv`T-u_(2LYff{$5cYveGu*2AnO$NM8-SlGZ*Pl)`0lE5v|1 zC(EN$E7Lqn$O6UeP*oKHtcrmkqF%N=CcvFYpD^!6LJhz2a`Y$n%BO;HWI5ZLzOrC! z|HY@Eqo?IcV-nuJ1={zydIbe<#23mOac+}IE9)*s)x7aIk&GhZ2B>ER5GkEwx14#1 zG_2j1Wr$@hzOh1JogYQN<2!eVehgHRrd2BF*Mhhc(6s;h8ctNuH~y zMH%jQ(?MVFeEg&AEx3XZ>Li0Mk$Efw%g^{aR(QRf;FNHerS52pr7kCkvJ$IK8I1TR zSLzMm7$UK>%l6@_fU+LpYMBGch;m)3MJS4CbSR>3cQ>J+YU4w-t@?e8T5JTSFT{rL zGA#t*IfbvPnUjucG4phs%Kl#P)eYa_tK68Vt>FVwpC|gbbm!6SOCOmYZ+yM6zID}; z6RC@czZ9R?{p>96M2KSBVi$U&rP`9pKsgz;VN)SZ=8r`4S%-`&6iF19vk)TWa)%%% zA{L3-s1+?FMGqa)CaOML`+zTXms+TDy%hw#Hx^efff~X`If?avm}wF?E3FcP3mSq0 z-(`|=Lk9k;9ws{dd)GW(snWY3VCA3pbqndr2U<@Dcp{1NEQ;`gx_N(;B;#iIbCF?0RUH52nNs)I2(U zU6A>$ITQ(QWvH8m)~&8;gBP!vX9j9uv_Tm;)lP|>NAI|=bk;OqmVtr9JSPgKO-c(P z);oCwG~4`hHeOy;Gp=_@Rb%zgtr94k$Ocv^ zm0!0Nhr|!#K0@19)CVtR{-Pw*>Tg$$xFV0-CHwK&va7%4jUHAtH1FAKASPsaDbCbP zFI3IrlWwyKAms9F*-g!Q2zn1uL6Jaj`F7LDgDxsszPs3mVq=-6ti3~jjSH7B67;|U zcO9=KuI!}yDr|N0KB~x=v`-RsEh>NKK%Z7pRsHqf*#dErQdSFkC-WJJd*_zdE2f_eR2ik;m&&CgQ+86Jks>{%DAWr_B zT3h2)yR9@*_s4Ss1vfFz*FrkXzlZ-4o!IR7Uj9NT?~LW zinPpE1q-#i`oSnNhCub5eS`~EX%y73#KHr8T@J~tYT*fmSo^hb(-!vt!EUU_{12K6 zTy%Wjp<%jhSaj$8)4lEZBx}1KL_otuDXSN64~1fs5(!*Ve#Y&P&Y;zHC_~amQ?QgV zPjc+;1zt&0$3v*H9(8prNz-84Td>PGPYw2E(`tb@-ka12w0PbFFR=v~CG-Wr9F={+ zr@7tza{qAXLIMVeSo;0Alah?}IKK{T<+*pgqc>|0#e=j?LkQBWyh5e-UAH1$AB%3>Z*0! zov5+q2#1Ywc6>B&mqq+ADOZ9khKr-X;o&p#99)=|A~lC6lYQ>w8z81=4QCFqz?)bR z3f%|x&4HGmV{=dz=c~zc-9oV(4eKFYl{8fV2)9tzqE~bMtn4>^zNl2xCrCkyLSnwo-Hx@g}7e^yW ztR$xTZX<0yW}Vr4vHL)o*rhT?+*6)2_cn?6WnBaNdug@15$m`*zP*0`{uk@F|1k_j zI35Ib@jJ-Kac6yNT`ojEkIK?vcZifVb|Q{jzjELT(P>7rAB0UW0UGY>fMpb((mI%Z zxyApT7Pt9996|Zj#{I7+8@IoD$M6u^k&9|H@dWepVuDNri7ts4(S6E4 zjWn}A?|)OM<{gW`{LbE}?#kxY`r|D#^Nojh(5=7-cY+5;`X8EFlT5Yd?Q)9=cgu4n z8iWW|7LZWaV{%_^ry7tHr>}Ph#Dt^OeSZQMKa$yUtm`Z1b25fr6KMbyq>L4nKqWP8 z<3_*p=7w;`-(j|UGtpP#nGxbT*o9nP$`i-^M-RW8+Fp5i#cTD5z30)SD-l+(O1_*XPY1m#7BW`@O!0G|5;0emSl+qi}{o zHx<3`_shFGoTn306eGUAppqozCTD(G)P)?aI)o=QNO<4W;!=?n!fMditz*~Uz+LFT zQ9C#AB27Vn)_!AtQ(c*Z2MP_%x!2OG3ewX$ua}Nx3 zLdls1>uDKUiPW2k4>ss=?4LIzHB>A%E$B0jEOnZy=UTDVkxE^79b2cb4o(clbRzvw zopZB}YSlxE4W5R2pA7fj4mrS$b`R0wo(<8*A<{^#9fO`C%{vw+8&bdyBF}AO(6`!K;pY8(mdjM9;3!tTT zl#&i*0NO&yt8MGFag?f)kk3$){M-1dReiyyi``=iz)Vj#jthXU-=sh^`n${`y~0Ex z>;6M!5Ia!Q$VgYQta*XE*VNf^g}%3iTZ*RYGL2X2b1v({P$3CD9k0ISKhcN-x~0$~ zd`nNVr(-vaBaA2Mv@qQi2p|CNtZuT{rO%v(Uz;VcTtK4u3FPsJ#>( zQu>iaqC6{X1+#t&l>q7hV+oX2IzQ%YFQqsBRU2fsIKmjo$+u9J)HB>rdY(TPQfZpj zk99^&X*B^@UCmxAi}UNN?`5bzW)c;E6dna2RzJom)fD*M|d8sV|8rIYWOua!yf{6Vr)*ALJ@fwU! zc7Rt^#1SD1KjcQ!{XJ!dWTa(UhI(=KPKC@Lzy}Zc2%g$Is{Xz_ymA4w8|dP6n8g3(Bn)KMqn$kV0$a-_0j?RRySE2h{%r zNCUiOqPSvd$_&kw=cZYlwPMp8uU0eh9BqIE`k3f50fFbhGwE4#eUp%@Go=L3k6L4&)bH}%D ztM*RbicR2-G`Z!uIY{A@iZISTbH~O&Eag9{zNzRjp3JwuU_&}Zs%%K0M?=$gy8Y;m zsu}k-i9y(o7bpxwiM-dcJWQmE5wr(X(Q0mdABf|=CG~Cok@a7piI+@|x>>TtR-^6i z;W_@vxsJp<);Dk8yXQ16jLUeTrO#>ZGfc>3@*UDtuU##Bp}0eK(I#@b^nrnIa{OGT zaIqEY31A>@bcQ|~4R0S7^3Wct)~~nT0(Ph2S8ZO;LfN+#Ui5v5HAz%oPdITk91}b; zl*-rs)moGI8wMEM%WuoBsX#DysG9#zcjT0Hz);eHG;VMn!n2%y^PMJv;Y|qII06(& z8`_JW@hj$wJRk%bn=FdDOe=-OM$3fOx?aC$rcCI*d0f0&zDS#!eHGQrX8=gWClJZ! zIFg|;NK16`e@`}6%yfH+UbKzqL>xNu3*}}fD#3NqROzD-$jYtSL7x7b??WK21Ye1{ zcrg}CI(b84HZuMfyqc65ua+-^gOj{bW3pzbKyfA=N-Gtx)%w(uQ!k$0k`7UbDWa>a z!J=7Xm;d|apwBo<_WQ!nLA-m86&imPawYX3a>2PEbEyRs|&{|B_dDqf>)qRNf^%x!ws}$eYBnz>W|lQdnM;yn3>^`F{au- zD!Y|V9n^3O?*nF5Hgnt+W@FYuu=+EZ{kHOgTjkl!Kl>bRel{WRR$OdW?VN43@H_Ad z&hgUZbK6TkpRt`TY&iEr&JEUDc*@)>7d+rO);U2MopB8Wn8vU)&sAL7Q2Ju@e$ z#;aP>+)xYNnM2*mY9-Y%Dn5Eo#}Sq*RZMoKljeD`S6?{y=xyb>mERM5?2*ra#jDjo zFYF>vxS-{D`6@lF=5|}{rm>p)3k80`u$f0*&qJXXd&W zu6A&)%`SaG90mPjAU z<@6T)S;6kwvWN#%T--nXbc93a$q@7NjRsG7Qe&~663;+HMb@{JQfMWt?{4^!`gm$BJ2(Tqr)^z`ee+%&ntN5)V`Y=2 zN~oUDp;4ma=iE1q5{*V2iem=b&8T2DlhqXiVbQ$ z0P4-b-eiA|@RZd1KDmj^2%nX9PcO~SZ!;)vUfaEItF?OAV2_nP)kJt~!PcqS{g z!jQW+#b|LgtKake2^QotMI*tzJfEen@&TrygChlr%Vx8nH77JJhQ?52ZoD;K2I}#$ z=BbRWa8pL#>kil~h15(=K;6PKUe2Myx!L~YE^fuGbmgszpGgEp-HOxYci z{#SJCAHw;4i`gC!>qOL7Gneu(x~4On-z}yJq&4vBR*eA~FWsJee+v{%ArIA6SRHTt zj%o(0Ix}-t-}rRQ-H^99`pRHuTqh)FJ1@pd|G59IqK;6lS(p$fE8itL-8MH!GvDgd z3;YjsqS>xQC)vEjqw2JlF<}gpgL|BWr0%gs@F|z4T&=aBQQeu@rN}2|aVz$o+`%m2 zc&XWg%5WVHQD{c5ABVv$Bj=`z&=afPck|z0Q%EBEsnc3-{(H8eOcR(%IJJ)c z?r(ptj)4xBH`YF0`{a80{tIw>_1=bCe8it$eEjj>@z0H4esVMa{ki_-&nG{>apUIS z-T3&EPd@rNe|~x6?>@Tm^IzWl#ovuT8t(3|`;;3a;jZJq!+pAQQP1!D{i3%2hJS1K zlh%AZnM}rav+5}* zHOIG)Pu@k$^~!DX);_vqYZO>xs z6V!uuj=*s>nP!tuT{lQ!kKA^EpgTPvrXq?69qnC*b6cN(r+(10bmd<$fSwZnklvCn zDS7i{7I^*o{24K>M20MM|O8HR$&(F&k*`cI~fLVcM zQt^nSZ6}rJk~-qsVep8SXgWOkZjLoG_$6u5=$sJ%THHgJ`pEfR)ldpD`Tf1EuO2yUt!e|r5lhDY@Knkq_ghIcr}==;4pf@8!!w=Fn{fTH@W zYwVU>T@?}2R=Z7%2R*LHUpUbbSJkFYEDpX!SDuKu!hk!My9+e(F}@6)hz4}n zrk#}7S_OMYw;{YNYe&cg(FVGy@%i`*9BvzrCyySxYD8A(_sd$gCR)L(e!BCfvuUUL zkpvM#ua4Lz*yT{eOQ~KJO=k#!-xCxoX3ZZ=##vcn9?L9Wwkt`R7oE1JewXLBB2K`HQ$4= zwlFJn{fo^<_n&NK%p@m#G=FU>SdMJYWh5U3d9*lIwoe)|e$Gv$z@Kz>@hb!T)i-SK z!@xV99Ap&Hxf9!?z}NsE^V7$II`kezQ;5k79`q}sSrnGgy z+U~xuBMMn6MyiW>5#?M;OWZQ_#E$O@!I&AKXJG!q>svdhbwx^khWVW27&k(`IH!*K z>d4tNIT75_-A@yWjOgsTZLiqw8GK`x;9C+!6D66;z=l)(yjUlw+P#iEkJ7Gc!5#~ck~eh;dXjOZ}es*7TXlWdrvBN9+f zA$Z!P-t&TMv5bv-r%;i`SCnA+Nj_93IR}cMuDRwp{c1bsAFC8$}Pef_Q@h7?C77+LDVsYe6kZX2vppihdM1ZH(5WYd~V!o6*f*ct&zcXnbfGRsofMSqUqk- zRSC+Ytlt`g0E3ggB|)#0hbaGz6eV1 z)J}O<>2Ka->Nc&Y&}!Bq0$UrKTX!FZp}Q|5;6ldlirv0tk z3IKjRNY>R39EDB_s59|h8^A#BfB#*miL#k~XIq?Ca|L*#MxAFEt1$!v*8 z_LPahb{8nh4#HGTJmp*pPEEj(q7h>jvJ%0u4Xnh75hVs3mC4(_H@8Ct$RH|!D-X$p z)je@~BmT@vHIL%5C9*NjEhTI1J9DCHLq#mSwV!%WyJ=Uqu54kz-{ca{j`R-CK|{pR#MTOBhQ*L2rFYk;`GTZ1s2+4ui7+ z($*dj9w#q;5Dr#e{E)?WClNxg{a)QLJ?4BN%m~WKH z{HCL~JpjQbo!<{ql>ZrYFMclvsUXuJpr#tlLSV9y|5>dFi)DH5P;Fzc4k-u0 z`i(N@JFrGS%t@18!-ZdRam_w1f@OQC$jw}@#F6)^e9gTO4^~!8C8ZusV*UO2E8O{T z{6i(Ni-7Ki59xty5lfv+cb*gBOl=64qj>+gO+URZ3Z4GAj^tcbf+c{&htgePSlzG# zp9%@i5)?For~7+tsAg^7i=~y8GYkx;N86WMUEXAY5!6(|IINr5BsyNb^jfPI&EY5Q zmwkylI3NT*w_On$r!%PlgXP*Rit8-7RmFLY43Y>_Pejzj`gEI}6- zIx;K`1Ho%}JmphL?TJn`!#<4qBu&~q-=5_ zO46y3om>$oZv11B5~AavkC}9vivabf!PEPZ^z`YnUD(l7e-{J<)+8@AoQnFinEPma zP|3wkwTGvG>&=@T^dJ*Xd#aFu1ZHeAclzQLL3$JT!5p!tJ2Y8h%SSp4x+_4x+)#@ zAnJWdguNd}1*~B@+Pf+p^?(@qa!H65;s}0p(PsNm9O;T%PvdLrb+k1nZpAi**+qQw zCxDqqy%3KDxCFBxa|p}}N$zT3_LloH1lWPuqryQj|9aT$fq6A-8q5o@c`=iFFE)FS zU76(>n+1^<(&ojS?4|2UWCn`Mw>ls_P$)2HVtNhb=awj@V}5pUJaW)Ft3Kq z0_y@WAx#m;RTxqam?3h!Jl`xhd@tVwOuhTB0On<|IRhqN7h?0uw7D3YL%&?e-a9bM zr^R6EjlT{ydthD-n+B7=7J+$nzBvR;j~shn-jB_K$cy;q-qrc$CBeKjHajpc!sbt- z&G*85No*Ea7cTmh`Q~EChJLvUn8Vmq&O%fhAvAy@YIq1fnm-4}8$Jj>jH|*OWQ1!N ziw}ox?y0n6*=gSy@^FM-uaujLJ4PLKw;g>srD4XV6~FWXRGch6a4>HG%9DB2{1~V+ zw1wZ%aN!((P4y}V>~IHt6edb=%H&-FAWCAajj}X`qcy^^5HVS4nZ=iWJbt?Lf~+8g z)u;c8U>kyzh>N8Vkju!Fu5Fc&`6mB_+5X~p;#E~^>%V7Pq&vhuMv`T^>TA^h2h$Js z!uD2jk6&PG12i@4SsU#2A$~xPA~H@P2RnFSE!-ffh^_Jd;dCf2D4UojzEEL@QX_-d zmR*WJImQnJam&j6dz)eRspVI;zS?-Gc-4*j#H2<{A6Rw*)^>Ev1xkd#?j7&D5G~;? zx-W94_LelJ=ol_@$(j|VNkNBNL5hL|j6aI+X3jbKdVu4=Fn6aMBO zfUJ0u>!s})3e&v*`L@@*ENGYXpQPZMgk-_&R%mJ3W`0LSa2&eAd0O-Kj=ca{38A$6 z1qySDJo%q?t;y5G3w9UeCl5Fao1Go2u01~AaIO#0N12fR?TsI9X|QAGTn-Kckhdu1ADvR@I%6hGEowPx9YDb+9|e}fkXW7)0mg57KsgvMRqUuXGVHb0PKw_0N^% zWU~;cC2c6{8fAd`yka!;mUaNldMW5%-nkPGH46AFE_cl>5LAslG;-3z|; zIPub-u3!J=PuIWw+4U}kFWpETzh3$iLO;B169RIW_|o;A=hN#{Gr3M(!GoRnbabW% zNBd{~diMI{yVpm2h7EYgrMpUWC{uO{%keoC`lap=y_{24O6rXM<{}cV!xS}DTqa>a zd7F999Ha5CxQ9^LHQ6>q2=DYxZN>z1-(k> z_Dk-i7>Qws8>q_TF;%;WOXhxyxUN~x_0lStQ{3Q4RCl=rP@!_tb9c^0Q5S>c*=dZL z)u(vX{pMJKLn%07JSr%w$b4bf3Tc*ww<;ZZ(Fhd6Tx$pwc|P0FvLJJn7Fgi6i>yS= zmi&pQh~#Z@p{RTS#$GeK4A7ORHnsyGB2GQ}vcZi!5j~jU|G1Y`rNXuIVx*e2Js1nU z=w&ag=H4|&eHxd#r?r`ZQdDW)Ij`k#U>M6q^r)kMx^fFl*Lg!$%X>YKW_pa(z#rW# zedL%-&+%4s2ZS%YQChYXO^Onz0u`!QAas6YN>FzDKsba!Xpo&zZi+x}EIV#hQmwX! zIK*;Zqfp3r6vQzdn(dnFq;k4Jl;u1`z4}@PU4WyeY@cWvrgoq(vGI0A)6C*3>!i}D zRnAFBqou^d_pCo*wU=?}EuT zcac6)i!Lui$hFKI8BXNM=uFV7*Dk)xpSgt_FE5&Ma6HD+P7z+~XuTRCU0_@J{k=U3 z5LmrbC<0(|DE`htD{ugw^E*cY%2NtHQK(J$zu1GB5bb%7de&B^maQuwoUk1O{K$lqRxAv2t> zOc!0Kapk6}a%ifaxj`US$c!=0YMP0+MycF2158<^2-6j0iHJy(a`Q-lstBSYwhI_| z8r@Tu9b{W8EdeCdYVY+kX-?)TWyoP}ZEPOx@0>n+*A?S@KT-!o^I%U#>unHx{~*W3Npa;>W&DuSN^^{%Xf>pj3wVmb z3XZghp%U-EnAJA0T$-89vuACv2}R{2Z$C{hX4x9aSIc5AOfDChN;sFcGgNs9Ti z{gLv*SlV;${dNn|=yp+Sp$<$#Bq-y%KpRzERkD7f-{++3qs~)QxiIx^?QdQMb1qN$ zlTr8O|4&f&^Q)jPj@Fa21Iq0GB*L>PIeHtrw=1#|h;L_npUW-Xnu9b-a|(Z7A*r3X zU6Ri=K%Szt%|5=$j7g%ST47|+{}txlcK2f^&fKMy7;Q{bpN&rL89I7lg-wsd@Rrex zvIvB=vC&UK+b35+8!K{lKGmH=fb>_uTl9xc*saE234M!E*D=;xp=I z$}|s-nCDN{G`85#?y1~zbtQI5kpYt=hNdHGww&phmJ8pB&>L6MpZ4~Dpn%U`WnNyz zw6jIGKV_0tX=rA4CB3TLozhTdde23wJ^@`qY0h4(G^d&fwYkofYmr=9IUMOt7wy@^ zm@Heq3i0Q)Q{wY+O9Q%8-AT4ctNalaDo_w-o~suMhrK&N(`vL}C-_UDDjw`b=0GNanByz#1h2`>0s|IK8_V1F4p>Y()rM(f*@66 zUu$nL&oddNT0$XS8}~y3d<-jdqVUGgXKi6HAv5z3Q)}ao=U-^$PI>K<`Pb(0!FazI zcu(ARZT!pmUxF*Hjej-&QxBJ5pm%Ry;HZaP2DriXRb5BnFOBb6H?||`JZz?+R?lU) zqNqYg^i5ZGd^yeDZ?bsZFvhpT8&;%kIldtByz9IDmvaI2kKS;7x#`d=?F6Dxxgp^9 zrz%8NTZd0(`GZX^+}6X+N^o>aDYjEgT-le-%d>t&EUMGFN;2t3oD!hpYf;kDhvA+J zBDvbB79w%F^;~>{i#Kr$m!W?ew{CAde@-bDDXP(3h17Z|X(4$hSBm81}$M6tp z!%)=e)!OJGmV23#rsRt6QxZZ(M<#5QuoTpZci|6NA4TOmuYKyO%^aVzxn_~g?HPP= z5QVk?E&#HNa28PquXkR|AqtO^pO>qbxvj(BRUD2&V`oD*Aod%yOj8^#&W?cc_2Wlh zZ#~{wzIS_Lb5*du_HBlC3W4+oUvFh(SFwaD zL<0x6`r4sit&;oR8P5zorfk<#)yEJ=IY22pSgU7g3#bRhV~tm+3mo@weM6y}+mG*k zy>;)=!*3~-q#vxt=ss0nw$qK9rPVAwy7{S7PmrCGWyul3FS&s$7=%2S^&QOxkf>`% zk%f1xG-bWnX@PJyBC%-%tMK}%j#ZQ|Iz2vq?b_n6b?3d>x@)Rj?g8znINJc(P|M*= z7@I3Vx+GckXm1a#nO&oJr*~e9vmI+(1_utx0%auKF$_dzl6Ms2J%m#uUc=Mn^WmtL z@o4ZE#-3L0##_zZMwlA&j^h*w;C|R?Yiend%;5 zC@*eQ9cLF3MB3f`^2_KBFx^stgxZq(e=EQIZ@9GYr9jKHXKHD;^`F-cj-DT1s{LhN zQkCb0^`Ae!@zE#Le7^DVFFv|?^XA7lKlugqpFjTS;~Rgg|9lzT{?vE2R4JGLxa#W< zySq=GeBq_H*p1i;T$S=oi6Ba;;_)xmeq|{=3hG(I`FtReT8lpazyFVFKHt1t&1du0 zulG-1AK>K0T@5tUepZZ}q@aortx6E4E-uN^6?&EwN^wyxE4!%b^Ig@FzLU3_g~%&~ znuBrwF6%hoSfi42h_ham=3aGZi)(jTDq2S;cU3OSy+_Su$!C?zCrk!ry@|5JG_lcH zqmjtJ%Yor+avPQIbi9SKoilm{)?bYO^5=h9cSYp~J4e(ATm8$Q|4&QT+WHI1=e3Q~ zPPDRp(=H(Byn*e1M2R|?h&(^Pf!ZS;tigR?{5%FP-9hw3jcM&TNe zNzdgEWBYdJyPJ3a^5_3D9nXNsptsbY%03r&Gz3MBTr|I2bImb2^Kn-Wb3hdYgrR|l z813G}yN@RFa&KU3SG%(OQ6o}@{k0JlGG{v+{ZFI2Xl1wNHV>DbQ|5WRb&Nx4{E!8`#rmH)8cB%d z;gb`fz*CEyU5fFS#<9Mm5Bqxwx_~c`w6H~{7*2ccrp%C9wFu_ z^u4D@J|h^Ml>9a-p7>N)vMe& z7l-d(W0EVJTTB`rJ{sNV5!wug){0hwyc{jOM{bl(DtIQUlo~DkK*n7h<@M38n?G>{ z`0=xUk}SuZhL8xllE8y$#GOz+m4Ob_;u}Z^dZn3BlZa55dCxxyqjoQpy ztT0M3T8jfK9WfjG$=W zi{3bg6lKN4Ef~tny`F+r97XsEo~q{q0)c(^M8(h)D7Biy-{0feaz&Ms;TcBjj+Txx)qC=si#Z}E8g?* zb7EpC?G@W7>DgJ%iM}H)Na3`EeAyoz5Qn5?+J8&j2}VMtoN^O3jd}Rwo@{(ra%|<3 zAC3v!BeQ^E?dv2Bj>+t{n_sN~H_PxmYFn2b+fYd*?lBoeOzz8A|B5pp)wsgvUaaal;|#C*V=tvH;xF zlQEiFfmm`@4dq*zIo`P-HT$L|LDOddLrY#)N(k4#oE6q0N_7`Z9Dq~3Zoo8VIFTyp zK^{$GPv%(#!BG8PIUpXBb6<6F=d~s~;xMcTCgVJM>0%4+*VekMk6(j|*Vtv7t&mWc zn|_$|R5m}hTFkS&`g2_79GOf@%slBE->ff$45o&yxlE4xkubcI5QCw%R9Zjdpg-E# z`+;Ms_&Be|;G70v@;isqV-bWX9P4A;LQ^P#1@d}}2)g!W`aWKo>(e1N`J7@|jWQ3G z#Yk)CoAG(wa=;#*~D`sl-x2h|qeNyu0>eC;@wK?GAEG3|>H?618bsz_TB zw}tj3Smc{4Vpj^7c7g3GjDSO62L&X8ubdrsKox6tmV_b!VX|P$?6U&*$n@u$V?nO% zi#YFI(@+aKd$unTdz=DCf-E(ChUa>>UW6xHQg_f`*LWB97r@^&^Q-qTN-AOEsZE+@Iu@ivSjkR6;?F>t3`v#uMX-WC#pA&_!f*Bo#@` z>nxE5gMPm{euF}ITC^hTSRrn6`Ra}}cSWn@4j`2Ez+d5PVw$){l|HDYEFjvZQ@;iM zy@X28MP3|7531fzUZHkqV8ZAt`xJirk_(wACDPL#lol^Sc8?4gT{ZjEOuRrr7n~d% zS7{kGw_x=}K15f|N=4}Og=>`sNTlDeY#^0u=AxguS$P`Bmt*TQ;w!!tD~LiWnv*hF zF|A;kR&(a5$Shh?5r1O!QiM90i@sUU2x$63mxr0oZGs+&)ALh0Et0rc`T5b!}GJEeV0LIKgy(#ka&1R@b| zOqWm_%CEE&r0+ZJ2}(1dP^LG+d17K3q%Y1KAk3%?j_w$Mr!X}sI>2aU+xo|w{gHP1 z>m3eA{6Bt)L!H0@3rcME)~1-{F13l|K^JA_-0N?Md}bw&#cp~rNGl)-ZHn5bV=Cn* z%*<1zC0-=f_FiZ=aWd0RmbRrU5QnH#_(d1ejSwu(fz(L`!v-_mn=H*7FBc9zxD3T@#!mR z+Wjti>Z^2r#AvJpFEW`pSxa0V9O+&uCQJgxsZDCUy^iE5yUCY5%30l>ksx_Uf+dcp z8oZk~9mEt_N?n)6=z=XV?F?lUF;3NSz1ajsX@AH70p zBm`i+Fxy6o7KJ#DZ%O|gb56Y%q?sqXju%w7DljkYev|?Uj5MZ<-nf?l*iAV;_h#XT zzilQt`t0K0f1|LK_n1b8j3LEbBIDh?o~2eky{1KH4+*nbSVeLxm$RnCTS+1k$2shb zx}Z%j6EMn=8)JfeE7Qt@!kNV0De8X&Q=m7Ha+b6(#kbjp0$rV9g+;G_wic@lxe9umlQZDLChNWKd(vDZmrpb8g90>sCviP^K)VIvo}E zC$SWy-P^8X88;*sv0`u>mCI3kM>n5Bn?-f$*u|W@35%i73vYqJ5G&-&mXz{o=fG`v$kyX?(&J$AagpYJtP^)f%B%qU#R%b|BG2Ij9biaNBneiwX#NbL z{rrHN&ZGo=A@A_`jl>dDYiT`+k8V+!h?SHuEDSe}jg`C$<52GL!_MTwx^h)%shNh% z&^>WcYe^n(ix!xei|5I9QF@PG(05_+{2pc{Hcb++5sD>%#eK)Mt7iw(=gy}_CL_`C zvX2}>sI}LmfazWq0FgXXn8D0ELT*`B64dr8V**EVoU<&Fgs39p5828J)unz34&6H< znD8#GRlIWERQ-<=j5H_*$o|0bU;g}m+t%EiaLZH!krqO%D(5-#>K(7wlxs5JnD=SMpZGA;oHkpwR*9vp-hD~IYMt-z!%#7yblP6AMp z6h7WLr`itW%_F^G_GlF-4?@rot*jfAO;PXY^>)cV6IHO0=UinOng)3|Ec99+ZOiNg z-BM~3F?qxSN|6V$DEy*xnvJmC6u#z4M%6dqS~dquJC|pV9O7!DkZLZlMOOk6ej<`B z{5nR?7!3x}jylOsYbQQ~P~(TL6oO4>D6}v^u@o;Aq9QsvmxHncPcw@B#hdjYU;1VD zX$$yNsilR8nmc?0HH!K$S%`Kuk9@JmS%c8-7OQqu6l3DWjvM`O)jJF`H(!d%1p|X8 z-Ljh*kp=lyZK5v}8r>t$4q_XzWlSUS!D{#&&V<;t?mfP3>HFo;xr8maXA;=4V#B$e zP+Rfi*c)kE1yOMbl(s&wnC7$M0qX;&#rwy(FyK}Y9K6)A;h+d>ifVVKmAxxt`y%&t3 zJ0{3NaDjc$6cTP6`U9ZoslXy2YDmA%X|1@ev{T;pK9EWyC$LmP&(cLz@TqcuE+l)B4(BRe{w<-1mp<{+xB;pj!jN=wWbmPOJM zxRw0{?^FzcEqlZ;ud(%U%$Gu7?M27UvOzg;nSo}&GG}47l546B$doUtdrW)P=Wx28 zVZZ|&ut{LOa`B|eu`sv}-FU)KrJVg!sZAVse-9&rwVy&PD9wpT>qNOZD3lz<58&@d zy9aXSbtkgn%@Dl=KaHL1QU-%7$l#lj)OA@Jvdn%GfX3Y7gFq^n&(1kqaKtF0i4NC; z-@BHKa1;G#8Ou6#%Fz_h*5UXCDueb5$BvwK;R)5$6rEIiQv+sSaCJYVxUitYUJ+Vz zYo#m332@|-uD3!LRcbCk)!?t!*XE!q46`gnIqU_LGt0tix5J8aO1BY0&QAysgWnXb zMTQlMPZ*NU!&R-6)?ovpjZiAYm>sFZMyg~;JpOkhR>`s{~h(;Y-@Kd1gVatN14n4UTD#^b=EHT2IS4*9yz4JVy z*w)V3Ay!*E0E>3?Ayi835lMvxg&rI6mn>BAd4*6zPb9-c#vJL_$2$;m3H?P$$Y_!@ zcr*w?j_$(>z&yL0xhaBFSk(@fw82Q^6)3C10{WMxRXGbYbZwolYX?-NFFFEnt@gq6 z_*cL9=th~(MVR+#Q=*=GsfXK!&ZT9#wTUsbgK-udX73^bZ>V}8OUnVLcS;bJ zg(y8oAJzIWYhcV35Vt4{Pjt}fIzpoXjE<}zGhpDiXqzOhpE;$S67Djt7CPainNWrR zPQvA;aNS*vnzmyN*i?a^JPUsT%v~|k3~`VbneYeR2H3Z+ank4Eh_7eF5fd2!g33Us z|NCTu&7a4n`$%kr6-3W@oU`Id)giPCFoKcC82W#Id}beWFDE*6@a*7)b#2v%Ny~?p zGI-HV0_7Tbk)$h9o|IgjRBfRYFf?=PFFoVo-awXV73*G{JL;slUZV zmWaEXf5o`S_n1;U+*ex4N5qLH)g-ZvMmM45uEC5U)j#vX`m9rXG#UOAhS%rEO5)U( z?)0fn&-b|scF^+34g$Etmd|TVQ-oL8m8x@%BULIK?6zo}hD!E7hD&Lm#AV%t{=}_u zWj>K5!SXJZC5cB}8@=()U)RL8bT zVloRk-*fZ=_`xDlFbadC!P3=X%OCLYP4<(kEUxL87(bHfnz2o zzn1jXUE*mtoa0a6kfn?rgg$b-yOjeYDt0++&2mn}yh3r4i7&U+-86mOLTONafypx^ zOy#MnDP!lyMCh|uAa$*1rvHz}6cdTkee&I5x8`@sc~>={!~kGOrgn90u8E8MsadOn z+iPgKd)HVW(VO!0Ih`0GY96qQBe-X|7Tj59>d4{NRjG;-|nayzobImD= z;{NSXM6`s#p4!Qw-FP7_2>6&dAQ46We2D9P?0lPR+gV4_h*mPqW&Ynsz}5D`67 zHZs>cpT3&VGk04uqvF-oDCNtzg^?6A z!FWo*;nIWD_P0NnQbZ+69I&$IhcfmDob??R>G|<75H3O8A3l`NW@TTx{WYalj{ieQ z@bRW3_^n$JIxV|Rv5#?zg}2xtA=~*&%#X5hKz;>mh^O$`@CjxAcfH1r(%SU;lK3;X zh2wlrY4|8#8-?=~91_#$FMkP;(xzr!DNWegx{<(DB;a7lx0|Z-(N%6vStLr03NYdU zG#T~-%OS7zn6;QXOzb*nla$agm+cY#M7f8rA8p?I1C>gNCCM8cJ{>>Uocum6moRnl z_0A(zUftya7|DodG}S=SPk%J+KEWUpKpp?7#)lLulFH#7?@mQ|yQH$;@3PC^jUOt$ zW&q!@(CiSypvQ;>NN*tk8 z5?;KvzsBE+&{23Y{-8s4rA|lMI6xCXu)w95y6mYZCyTpz% zkc?JRJqq%N<3oS@dY&OJTW-<{0`YA)+O$by!bs6^U>81wBN*%YhMfQ}G|jV=@By4b zRdAT?gGUtq7*p3mc@Lo?ff1IQ5h@CJw*l7l@C;{za&>>N&_pE*Pz?`{0thWE-jsyL z+!o^Ks(|SvhX&TrpvuP5gaVS(FkFC9Gp>w9I4HDNHc#qPycfm3#ks0aPSfvYWR%uD zW^`N53bj)T@etu+mCMpSuW)nw+qfxXpPFV=0*Wd3c?YPS60M>@1#k~>scZ=N1d%8^ zy7M5Jz`>~;|I_KQT-NZmx)iQ{vz8T!v=W@4x?P|OI93?feGMC_Ix=4|b9Oe1^E|qF zF4b<8h0-M@G2rjk??2fHjSd@dGq50gjxyCgt2VyuNDEe_W9jwI#@+QN_bCh1;6aq7 z%k8A?z>B(Tco==D9VI{zQ{CMNRqu*^E_Y9_@( zq~a%wUEO2!xc|LRB1Ov0psa_I^`_(d5Mf81$x+Jf6Xi-Q7>Q8D?hJ)SSE*IzvYxyH z7>Jen5YSi0U)+0m2d!f>bZ)d4h3$wWc&S{vXEK@Gef0Q&7+)X(0`qae%VLkLrYJL- z1&|eI5KWmUN0K_<3X+1$=Guz%+-G;c8i?(SS7Qx2_Jn+)p!jw-P_jD`{=%*waosRk zmKVL(N^>_^Y?g{}kZOww)NgUjpznalRUtTiP2tGwl-L5&;=1YAvDrS}$e{+T`jW%C zKPrGRiXzh0O`xQXQntu?xy}roSj+_-vXCVFdXHncKpe;Yhru#oCfF<$X_JmbtyKr5 z7DiC&3)6YMU_Hcp3dJd{f;|6`9KK#iPOrtM&L6nOTg$n!Yc#DDH!_jwBfx zyPkKQoLDHi*EaC`ue>;g3n}wzM@rsvL!wH$;}Pf5!{*(;{O|u)^Q~(+)3j;ZCM?J! z9(T0#c1dgyopGY?W_PHCcZabk4?-ZbV6XEGG?@gIwCk1%gNP4-P*c4;qT6$N!F@uP z+ByHE92t_5j3AZ;df*LsJqw(-`~jBM4wG1Sq9Jzg<>X98i3y^fMBVN|bangXK54hi ziat-rU#)L`HQ8GKf}6Coo!nwY@qZt25om}GMSM0XAEYm+#8pJy2Hqr?6HxRW5KFlM z7{sRLgV-qcscy@~H2x*JLfR6C66r5FWGaMDvwO(7-P>`9Q|BR&|Wg;)u$- za_*t__2|4sUc8>0;FI3Bm7h_lrzLvwcCK@R?LV5#w)yy@T4GGy&g_(*jaMe>hR#1x z_v-K3m^KNa6?h-H!~>Wx7EWl56V87jcSUjeKFylmy{txMp0O*I)&eLiJ;g7CUWQyG z3h<>o^}AJ5fSc>qR|aYEqVb?bT>sIPw8&G7@!QT8tG<)--G4z_b-Wg!?-;gUM{OtJnaPos~Bk#8|#)VZvw5+CaQo{}3bP;FvBB-Sx1TG{t%Mk`9 z`{;}RnL1pXeT*lXzfgC-GemKqz>BzcbG$K-E4#>3i2u;eLOI83_-v;5+LnUI03#*8 zcQ6R57w<-<*K8V7FI2D>lG*w4<)NNcheC6W1XX26=P`uXA82IHzWzX2|CFv*r{m3y z|L|nv;cbXW3e`LGHp@s;)0s?;m>wjU%U0Ud;J0p-Aa6tvTw^ffs~uN{lN!hnF7 zF2oeQ#7UWZ%kJE}y}q>p2G#=^unA!kn8Y$zgNSu2t>Z}Lr2pv1Z(}hLZ$c~Nf9ZDU za0IBZZBz=!88WF(2&5eXj`KjmP&utlRw9yk7+QfSBtT%alagN4L3I}ECxzLFW|zAA zfTaT2bCH2d0-0c}HD6(Hzij##3;h&+l*e!>Tw83r-chM!mSnVU*2~d~=MwAe1U1Bp zWXuh0HI#o-1wd#={(M}F?RUE5^~D=fRUZ|kTO&Jb#77B%5fEY%orC|r0&Pk($!Nx_ zFYigHvL$Px(7yOzVfQ$Wh{9*Dvwm=WuUUEDKlNV5?r>9vhu&*#x7FTk)8kaWIC-N) zl%T&R>!>%v*6-n5Lx!XOahHU0(Su-#l6F&7F|bbvzMW;^Tg`&$YXSfvF>A z5fr1|`|TuQs4qsPcNKe?sP`5mQ9?5!kf*n}H$V@-GIF4ZD+KZX>U7g~o1q@&8Ctwz zKv0|Xi4!t0e;5DJg~O?erwXNB^itZT zDZ0$dZw~g(US9Ne+}#~7!^I-5qTr+b@v=R=-g&EgkWQx)R54vVr}bHgKpEpXWs_XZ zRRMboU-jYz?y%M90s>jx`AFXd*nHyK3C_g;FVVYMqlQ7Jwq!|`%SmTd7D#~-%RDsz`B0fT)F1&;ut0#u zK@@$_qwZ?-ZFODLMb<^XT0TjC|Nqa36>IMeh?WmoZdxXRz1NyCW5$db^BkN`Y%(bh zJ-`qlG}ll#{#x9f7k@Xrr)8=@v^5o!4jbSsqxUA?2Owup9sB*%Uqi~i9!vn-s=ZA= zyrbXC<1Y;K}XBd%xP*-IM!?Y-L82TZa*y(xZ`u1h=s3Fz?7_ zj2`~1o7e*5KfGNqS~ zcJ}tT-lBPL(15^Zd<72Cl(fXIhV~+qUFW4~q`{G#8ArPQd_EXp@ZkAE1fkdvcWJ=@ zp*%(b)R#?a`(QM|(LcB38D}6RQivKqacp4Z4uEGYoW5)q;uiklKvsq@g1&ACVP1uQ z2EJVyd2tg1&0-44CkyMuLgi6X4glzP0?n|Mu2Exm)~apb3F#dZl`+tNu&2RF~o)`ze6>NuToW zZ(J$E4)rZKu47a9Qj=)+gmj7*Zh_PKwv?s?{n?TSi_hq_7lbgKzmN`MilV+GT|;V-NY^~;UKiNkoCL-enG{E{OULyNpjufI299Z? zn57!ZpX*ia#$MT=;bNi9tkJm?Vek7wdVKalBKu)McqL<-9ypkXEwi|FpTNt?%^VBe zy>;vD(Z#LB;_u%sZk5x>E2EO*v(tWmMS;{mTenWncwzDPr;A$`Ump{|O4|AzfU7HTLp2GdwUd4ax!7#h>OjjzT(c4TZ_N{ilMc!{p)expuR@@e?c48OqhTNG^IKrT;mTc4Lwc==|Kk@}y%--PXf|KW81x$0| z*b==BMx2``*jB4JaIcy^0)3@ZJ_q}sr!GHW%GI<>3rbSVUOKEq7$2GOP>D-MYd-Zx zoEGG7XR>0azQCsx99Nf~C*IZeiI-Fz%uwak(T&-uv3(k!oiF|kxz2p}!!2BCi|30& zVj%zWU;i6hgy#5L&(vRZV7jbdsa~Ugq~`@ z{`Id(yqPhdKGTQ#GXsdxeo2i?B6S;pk!nG&uEB#GJ2sH!s@uRbzCW-;ix&oSG%df( zm{Dhx9fv$BPN8GXT&?20433RPliwrpv^GD^r27wkZ4r(VNAiS{0<4C$lfRzllEEu{ zs=Vq87p%*Yg>v~O30mj{k|g7{(+LoCl3_xw7aJgxsRcy%3T@%u^g0{%Eq_W-0|NYS zy}Sj|r4#?Mnv&m9`U*hXwDR;Gv8O)c>7{8^%Z61nc36%2^elHq2GnjjvK_hA?!5cO z{rfw+%O|JM8`V|v-y+}Wga7LTGLKLLr5Xg+g2_L(KDaYIQir_1{hs4}aB=a~9g=S^2e2!; zIpC$E+?pQUfWtoAkv(3Oye-TGW60}|bVfxLOPUA}44Z}aJl^Zv(s}%RWqBz^m?a=? zSjVPw&2*B!(l@fbuCH32q7puPyHr-P*GEw~_ULdz5(8MiI62^uz&+V^ujSc`(xGG% z%~NU(y2JFrraTiIVLhapskUrrqHv-MejB&p&=1|phk6K$<{$1M37zqBKLk)IHDID- z!-{BiT1T2+1H~pCFG+%~EP%Wx5&J%F)kIaA6m6n~ZJnBtwey(-4%lSA-LclOXFSQz z$_vk7DGuB}&Xp3vF5P1_rzU;`J12nNS?pCrLLj1Mkfy_Tr> z=;M$6(@(#-6_@U$Db{+0gtcA)zRe0GClzOmb|&UCyZ7?BuJ82v;;Z81&_S(qY*Zn9 z1kc<#fG10+Z{UYg3Z@hFx`hlL5*mGN9amB+r!}jp6v*D7P_^~rX-lxWcWXyfPd83{ zZ3XKE*KmHtJFRni=gbl5h!;7ma6*d$sx=&TVD+h(K=k${Zdl@e4cFkYP&;NPgSWmP z-?iyG^@F39bEhj|6iHIsv@ zHqC7<2U?*6g&BPh0Q>pj&i;=qthXP3sv}KKUNr#D3E43G5p#<^hs_c^){>xEqbiz{ z>`y9{H^yYsVtI)K@k-6CNnUO<5VbKAU&VQkzGJG%=aYSqG<%MXhC%1-A3gOQHcsfd z^P?ZxAKX(OnoU%Z9lT!m=GhdPR%`o%!-wWoUNyO}&z?CmfcrZX`f4#ui;TSYcdfbb z;n6eRymDBvZ=Py5a{_^+(K1eUi_xbJb`R(6-{Nc}j@?-bio%@8O1T3xA+w8NH z)RB=v<|~#@`*0CSAC<;m(DeXNoE*I=J z((gI<576fNf=ku>oXXsyq0Y4Zz3It-7#`<*)5Zk{k(%)+GIk6V7ff>u)vQ;J=|%(z zN%P@#)KJuV)vBAFN9)$en(E_-#_DDBU`vFW$nu%`8)j<#-g{JRoa%1zmIr#w$%?CP zxgWeeI2WCd5+D%()f{&ULQOaVs{L!R0>oEh2e`A}if*O+ z^(dkQF#N$*#A`-XpFrSII%9=83>hYgM47)3ntCjV^CEx)WWpO3gd0OGZJvI!y=B1K z#r6T^A*k*d$; zbcvP*t9VbNxE!?MZ^97vIzAzBafhaNx_LI0=xg7saTK>ey>w<4N9$z;LO*p{m2E6( zEPA4~>(6ZFqF7~%uIy@CT06OyM9Q<;)V8(JEuBbiqjSA{8dGd}H!kDw4$T=CU+O!G z5V*71wU#m(ImW*eXyt7lxeXBoSW)9}b& z6g9EO^0+kVb+qd1zbHyCxdP_(4LG&$|Ds6cpj=MjmGfk?+qbLzK)|C-OS$`OCj3I`ns^y6k3| zHa_zgMUI<0&|kg-yV*m3QS>TVff~y?tuET#V0>VEr52il2DQaHi%yjBh3)*UjFh+)Gc(^hFH1Tq!CTe%Hc+DISx^!XY`Y;JXUpL z5TGtG?lDohz1wN<>cWgcTGe~&l0juJzh?|HH!+!k<8nTH1}78pqxbpWUuEqQ3Bj^NXBw8H+?T0cnmfI( z50hkLm~aW0IHfP2uIQmyM+)m7@Pz7UboXm^Aa=ufl?!njsrLug&k+4?Q50+UvzI6T zfKr9HITFJG!=l0?Zk4wYPq$q&)pK|9(@nwZutZCx z71On*gnCag78MQz#B1JAfQwy{sU@2I>~B6cspGft6(K413W)CtCJB_YOra!t{lQD7 zbuslq>&v>xp_h?&nhh5`yYoTxY@p4039ASZ<4`e_ zyyXY{S3;U=5fpv*E7fzMqs`e~?!My8Sh?m`vNbS&dApTAL<$$31?~ijyiZ%7uHf=m0jDI9X%U!X zh2`FHhGrhP6FLi>B-881fcI(%vw?`&iuO*}l~UEi1m!ySXw{*O8|ir$bs97{`)X*c z3oqQH;01-ItlhyY-4ZlOlT@zARkYrP5=Unzo6R&sbBMQd>FspXQEsg7j`>#M)Z zi_=hQelE46M>9^75kpB6EJrBS;tDj}SE)2B3p1ys>dTUdrf_^~u|nLF)QwlRd>}4g9iOUS z0p>{LlEY_>k*EqpKg$>WX!O~FuH_CqzFb1J*ow;Lo4gEoout%*ppQ|oc+TC++_DwI zgcidDR*z9`H|oAzrHclD<{-`whgYO!#)El z;KEuN=0O0g_)wb#eSDIQvw3aWVHLD%n_p}-B|~Kq;0Q$MJ2e>;OK#{d*m#_h@-Ad8 zwytS1h?w5oUoe2oA)Kg#U=J^>$|s^^0i@m3rWhDazO{~di^OQ%YEvvYo&$3%bC3fu zfEw6}C~|A7Jmrydq%5bQqj-)T0CkQ`$4y+fX?5zk>zOZEc!{Gm?Rtvpm9&)tSWCb` zupT@}T;sE$t71z*y10|~D!`Fo0MRBUd2N(oG&`%_8rAF-DFZJEyIM;dxR^91!Z#UZ z*PLt_NpwAuP+QSTsFRz}pmujY+xhj=9Ajyv%G&3{Kq-=2M`9J*) z0&3&Ib5EA6R%p-2z0;<@neIX7#=SjKh7PCX54|gCC`!qN!M)%yiC_P}q|G{2Ixl6l zBk{yvrs5yKNH_-{Eg$aO-}h-SqiUec*v?PyKKShO{T0@`i*1h#78lduxcawdum3~f z{Kfh2V$tUo40PO06M!$}==xW02~s|*smDearEFXMyj0J(S*s2S!ttr0@PU%|m-Gsz zN8GIC#p)a6%3PjYEDzfKA#U2=+ZBurGSu*zyPr!}(9eD7d25gnAAedL#QB2#qf|DGczkN=j?6oMrAdiHkChXwl z@0V`ba}%O@8om3HB^w{yrj$9eRZ^q6me}G3vC3~)Mgtw+S*2dJ@wj+%@H$OiZe+_k z4$6(zd!)=J%JAPF((7{QqA{#A-(Gw(e!JLu&DFH-s!civOu{rO@n>-db|yK4X_Ia6 z4p*|rT-R+^tM>?Tm(glB4)*nWH;#{6CL1s|bmRE=FW8L(3{V+ZuRm2c4gp+sWB5L2 zw^yO>49R9K6L;>cP@}J@5eJ*~5dI~<^rSUrQA{WL4>teTBnKgnT3TdyFe!Xp9i#NC zY89;KH6eD1;GC|Q251h3w7x8}4UL*Xk}syx7bV2x&UO3Db?b(V#@P&}^F_P%;I~vF zDgZ;nBHz*>W~I3uw~`(|V-Klln=30|Jq*xZji(#BPmfwH4o4^Tg18gP`h_OM_w4$2 z&K^dQ`hIJaxK%`+7}hwJf=}3$3@lpG%Ld@a2OwV2uN6g4Zj-m6QZ7z6q1+6GvE~2o zDbM_Vf$YJZC!yYK*w&FgI7m92-?aQiib3Z+_~wkv$3s)9JR~!sB6D)biB;iY*|M6< zzBN&p6TKKsQpwIE%%S0B<(4)8*MuI#UVy}EM(R&2D277qf|L*hYSO(7dMf>y-$P|i zNp)Q4@pt*xe);6Vr#qi6eLsN8|H8-3K_yFb=JsBrp-e?F66?LN*{GkgzA5YMoIPwq0N|z}!SUw7bhGhEb z^;X4*(gDOc>3DvRCfozZ6IkbOY+QHkin27otQ^znUI?jF7mZy4b1pF)Tv%BS_5+qJ z0lx0+V)ALs91w!adLh1uO4ii{5{+!`xXz^fk}_&uH3xXlN;pSB3mEp_z0bFIm1SX0 zw!;~}-fO8%eGEk?_n~ibbazq&#{B-gabL6od}hNW>4Djt?&a%b(E~z)%lERa2)F41 zO9Aj27ksUV{va-N2B}g*;!kOx^kzGMrPGUHi3iUv>dBr?eya`>#8k&X1mC!(z$)33 zaxHY=Hk3%Dp`IUj6`_9XA<(-2_eUF6TGCQQJt3qddB)|kuj!Blh<%COMRA5J;O)_oG-T~A6XG|hZjJfLf%+a?B7mOkAFK8L48-^r< zVJE{vQE|{H;M#|+VFzIUy=W41pl-QH0LikQ>;SX~9YH+VeQL8n64jo6?Br(%f8WXP z&1T&un21wTdTijV%w&%)P`aD>KAd>RCJ>rr%5c#gxZz2;2rOOuVRpmX12y@P1jVCX z`+?;|qnp=9Z7KJj@)li&!)+gCQlIwCyD2m=RnMnJpxoKT8PP|DBh7(r1FID*K@5$n z12wB__{iby-N(KkM4W{r9s)i#?hqsew>G^VtHZ1rvL7@Hwm$;n!VRxqW7}MwA!MKz z17p*1*_YK0?QL=|swWK_TANk~P-%1K)@BX3zd>lR5>#SzYP?a4Sl(;o*Mu{%kSFy% zg*u1H!P|LNh*o#w+HGx3D4d5R19#~f_JU>2v?u%B?K;gJnS7#xuzr?P?%&P}ab>R6pOX7z&KKTCPc|FS9O7zd- zBM;>_X!iaXj9n9~Wv(mAEjBAJWxsxG(Dk3ay~K~Cz(=vn{M;Uw=*x!^a2H=)(yzt= zbbiyl`iO4txFTztuy)|&s-!c5c)J@6c5pod55@k?$T8UeF4ZR7n< z1dN;R=)IzS-vpc%5rwqGQ6pUJNM5HEwf*to&`TLhI-#ORRjgH`v>;8;=x6QS-|X+) zqdaJPSCQDc!ePCk`yM5uv`LhE2f)>J#J1rEH)zHEJFuQ)a8C!5GO}F`lPnP8togL*C7#ylhg&ys7th2B> zR5{-iS?kP-ji6pk&P7oLvQ$-Uv6u*u&QO z=2-Qrjh(z(FR=lOR2cASqf4|qAfiKj#}alvW_vLEvm#fm;xEK^xj`c-Zb{gKhR`0@Kc{%|wLJuNostaiWqqv&uW-d|qo4kP`Q zd2H4-4$#K_x!yRU&;I{}6JL{~Ot#tX&W=tSd4jReV$K&;$!V?hXKBx3gSl0#V+T2Z z!pAU#`(+Ts=hkL^%mF*$#Rn2-b&WT*pz;(jsK#e0ANoB`4G$B6!2`Ate5lqY7ZPS{ zM|xmwy7OqN)VFw8wI$=$(hfN(?-+$nabC3l2ovHp|iAt>qd&p6mQ%uvnej>;& zyPG+#^4jj?#K#}649UNWd7!;viENt1=TE-a{T_30zF0^yt2n@*<45%GIz;2wwqbHB|KmQ)H_*d8yZ|SbLc@n?bA=mp4-w*!^yhIPyudn5&U+wJt z;`{N`zrt!95~tkAQJ?PZW4Ii=JxLnjfxF@8Nz@^G`OnU|&9)2Sp#F}M*efOloP!A! zO{bnWet>LF8Qrk^dwRmnLiqRc@NAspe}3}#`v~B_48v6(jWV^loR1r(+HLE@8PW>m zCJ&qRy?Rr6YqTSqB`5_V_ZqE4nWZZ%Xlr}<;o@@CCkJ(}1!OdW+FhF$tVfU;Xbp0) z|B=-v1KX$v?2CWym8(g=GuJDcazr6NNSvS# z-ACD-JoopHo}HsnmG1P>kGa58btcpz>-~2|dL>~6rHvp6Hn-H(z7ZT6pf*9Wr@z`;lqv+1H1Gjl#*wD`KiSe(+?M3(~hA+84^=2o=^vg=eE$ zBzG9D9y)8kA1LiL7(;8iLP_j$Y)^gR(u`(q37NFL3y}YGa4K>xK17Z@b4zq*Ey*gK zX@Hwn{!dE8FVt=)@#Z^rNRh#KS<#t&W)Y3o<^vOM3a?bG= zf&<*3tf6{J!DIM}_*9Lp$RDH}Sw(uSCJNAH7@55Id_pqotZk^bIE&stdS7O76nT0S z!LWnLlr5}~0BNJL<$)GJO_?8Hb!$jN_9nODZo+L$2>r@LBkDUw@ z995@>z5mcdwfL5qb05zYc6XrwZ(*^J+S6`9u#gGK+BTZe9V9e_mqi-_@>u+*1CXL; z!{u_AH{}!OH8bG&Wls}%y8Gm_-R(z{H3(2yNf^7Wl!`N|^_m>3JnN8AVuB)hRV=A@ z$v4u?^{eQHaK3NzL7d;^Wg@u3o?=z7jR57fd%C^q4&H-*wMq|m?%~fQaP+Nmh*!pQU9Kyu=kDJjn5QTnb=?_wPNLQ7 zY%{Z~Uk}F)iQDxu3-0m8O)k$HwCq%GATufv!N>!NyyMn4nt0LU{XH$gY;ELRm^w3} z1&MgX2fmfVYGs_v<^o4J2DkR3egA9ZQFKc?oA~SHTkL)bTsbTWdmlQUJJ8AEEO0Hh zrgRgw?XptH^8H?y+-3=7h{~^m#*uxhBYzC6y`?Dn$x{-x?C|*I_QNlB0%+E?g@Wk1 zah6rqpZQ`zF7djmXLC!eMAvLa5(x-47#gQK(wqZCVk5u^Z>i)?O}+_O#&OPPO#}1! zkn=lpXby5_0@vFaQ-G$}TwWHF0*TSgAY`5{d(>kEr6vk0iZiDXLWIY4&a)F-N~e)K zXEjV2Bw_wKUVwF+8#L-IPf*x`BWBYu6Xk!GPw$w6oHhZf+dXn^68O`+y5;bo4Ph>$eQRQQhYn6ze5*2UpJP zKkGcHr~hXTc4aB_EGZos?Se2JfN+k&2sK@FY~|PES3~>1<21LFo`aKFG!wVXi^GgB z^jkak8`(IVv;o>Sgg|I9zWj*h9^Ms+z0_c?I(Yb`Mz_6QlA* z#^9Y0STAaqSr+=u_CVfk1OJMR5z#m!;%su-est@i5BlZr!jo#YeZvX!Lt(f0p-HiJ z_1x<#ZbjOvRAvQ*oyw7Pp$M6Yy)xPuZIz7K{KsiT1wfn?mfG~LiQ2<3h;ik*&XGDm_yos)=N=MInQizvLns5Qgn^&73J6 z4Y@I=V}@n!$_VRwN!|ijU=@#B#8po z@u145CieLw7K}xMgC?mzcB}TMB9<7+@h{TAtmZ|mdQ}m`Lye?j!p!i(9 zW&tsLa%xF%O-o)GXl2V)qlsGe9CFn=*~_jS#S+&)5ma8+wp#bm5I7E12ZwSjenFh} z6afR2x%tXY)ofJ>Q%tf8igzj+*JOMa1;at}0(=r|w(fMz?Y4Qe3b>lgp0<1sM)M%36w^w+;eC9&ojf0h01KGo* zu?<=uqmD{bh)_{w2#C|?dUDs@n@`RJZ8?Nx<`k6n2=Yfqw(b;pXZlgh{a4zzkkU!% z1vA6nX^7Rg5R;evg1>O6g;66K_EOb9WCwX|yM05wAqhB9v8_;-%F^q3s_4i<1TD#$ za{#f11z5%*=l0OoL!Mlf5AuAq(vb~Nu&dfAP#)mWJ;OkZmx^r?-Q1Z+hydyG%?B*} zRu($l#P|7v@cm-XlR@>*R*?YkY(u8+Y``ZW+LBJY_bph;NZxz;aQinJ|Jo&xqDr%A zwV>hu6u7$^O<#|IvRIOW{~E_-njma+lQhdySj{td)o=w;^3S8~r}1C#P@tDShs6HI zE>U-9VWmXg0~t{?WEFlr5o_iQ=wRad(UTlWs;(d?=9;h!>SUI7W$h)&_r8MJDuZk% zmgBq{eJM{{l`DGq4F5b+j8{36KPr2XoK5=;NC<`p&i(iwz6-DSy`UCD8njrFsi9*XNt0Js4JRi zLzS0Uudv`~1#84^om`U_rbJVd|5SEeD8#T3Uk5q2l+?v+#Q;MtAiK?$-nHlCb@2*E z0+y~r-*jk_o%)T|hg1MeODbA2>D{ECAu;32F!yqMiJ~1cSR4wOEQ*w%L04Unvve7J zZ9}a>KmRLTucu^kf8sBn?8>OQL}Lp1g@Y(!7hsSnjYwWqcuI66f!S^Hr><6J&3 z5B7utQKEdOK<^xfkA6}mB{j)+!QmIjciWhcrXN3>e%!Agk2c^EC%d*`Z_v1bG}oe) zMKDFulw0KcLr4<<*YC%1_jO)jL*Vb*+IzZvZ-=HG%~pj6aPMt>@%X{MT6w&@a-mn9 zH0}3=-)iEwGqHDG+mK}{jpc2n^R#6VC$0k4xW$z6wv)D5QbP~xC_pUo&fT|CFBL~u zJf(T=yZFeaDp$hQDU#=6VrVgN;~r#A0`(S311O#mL)>_ZxWE;4*d--0jZu1V1R}Zy zb4J}7+xiS5QO0DmQGxTj0Sv>Uj7rsxC+||y7-~Op6{e>+_V$YO|d+5jj%T|z)GTq*oLhe7d*S2YX+Y=5uf;OQe{9CAJ(*v_j04UJ(ju|1@1hZ_W}d{6cp&xExGTbt&uA z97F|fA#NXO%EgfJqXuO-zj#pngx`uOrkH2v2z{$D^{RLn6D>$NqR318`mzdFN-JRp z>ufI%`7F*|YzX)YDv>x+k|Upg$kU8@%=w8@do(lTOrrqdrUB9MP=yasO*n@f?Na(>>4$Enk1 zNLmRX$+cs{^Io+kH)^LPR=Jg_PtqQqm&fT}y@6mkkJh{0xr@;=bi$wh1m7i5LX{jW ze`I-G8p4bXy?j$y!V(I-^Rt)}-Q#i7OHMIF(UKEs^|@H5VDSu8zj}3W{ES;r_t`e=_z^nmzEAB z6A29oAMg`dhVMoY+fzS79*j=8ls`tVm-Ig;Kc`k|?8(?#4B!@1jjRpMUYi-S@SY z>dXh5-)W8Z(wXluYtuja+x2FB?4k8$eLopsY-rZ^lfPiIK4y_a<4@JBPXH}Q#Q|ZM z=L7`K((OlA2=O78NurBteemly1BEjxMerX1L!$i^nZZBkokZoCxu*i}^*%goW+k5L zl0%ANtK^u>0`7U6bOMAviro3jjeb)`n}scA5WzEXitckxHdj{%ozbO}xt%$1A2wdu zlseCTe?-l&K2_EQXsxL=vjLYrGO(B3*>adfUe4EY+G|ePF1B8#+3i^I&_+K0U811Q9JomxtehiUzq>kwsXqpd7Vk zmU1gY40ueYNy^aul5OXrUF(&t)79Zyeobpe8zZgt8?}MTdgK~i;}3U&f4d@hmPi<5 zkb{A3sxh>S-3|;Z6iiwU9zh)!sUrero}5^OmDgxD8smILp@DkC4(1xapZh+DOuXDQ z8n6e2igc9$M$(j9#PI3kogFw-2!6i(8@Tq~FUkUSVJi>B)Rbu-bd;!$>dB+K`^&oY zVMY5iKEY*j@zqt>j%>9{A5Lp{Ko-&yh2S#xiXF9$XZxRDGu+?(VZ)6B;F3RA zfYW~M8l;@ODeevC_I*!YWj`%a(&4KJy9xi6KwaGg0r4Nc|77Yy=5a2o zV{|)B3KzCZ&ue~LJs>qJgmcfczURm2#6f+yTI5g(rj%!vgd4PW?x{Vdj8|RoBvRmP zq6Y~bTaVBz?a|1*C$L)Z{;HgE-o1)6+xqPfg4toxXcgg;$ZZt#2+<;Zu92y;HmzG9 za5EVhD0K0vixvHeZh%*3O#RuOqtOS}wt_I_;+vd@!ZYsghzC6pNKq*;TK+rF;A3Lq zAOu@+ged#0E<$1&6{9?$cWE`e$zNM`kG!dYb5MbST0ea>R2zi&Ail(zS?Vb0L5Q(CVRr z-$1V(p3&}F3$8He^y1o%eR**HOt)~?(M)>ZTct@;EZ+{`y(^M%G}jal)eG$`#|dH| z$d^Z_O49g}aj^UZ-oJN3=kFH5QhB6D9G7>QN zx5eIalfe#YR*WW47v(UvfH+igTY2t%& zG#<5&6oKrX|DlA+4$Z3zwqWXszd628no9?2_1=n4)*)x2)q3g5Q7c=YUY7mv(uq4P+YaDCMN|v(>A>0M+zicsL!PA;dl+_ZjkpL}IJ~G0p@D5hY2|vK6WXprlX- z5}wPKTgDf8l4;!*u!F;Iq@$w$(?bL&e2&CU7Y(aHg1j)$^4Q5{3PO+%U@V^JC&BBQ zWl~u<%c)%|)C;!}>sp~{RutE`rMeEu8H+y1F~70Etpr7h64suIG1n0#mdpOO3{tpi zV%aRb#$n^4*{?9eW+^1xS0qv0N;D}6yE`OS-P_R)Y#dVPK+VRITFVyyvXYNXJzVTO z{`BX;E1#8T+0m8J$nbh*X?bN9gb{}7#1=3qyr@go${wgS4JO58F(AA@Yd$SjH`lo{ z=4^)A2GYRbA<;lOhlQo&2~sq85C?;|?69m6M;LV&C}pGVaVr|m)Ht zx=mpi?jOZ_9K!tAIKlZ3`dCL;jo4N+7>(z_lyFt=bBd~mUXGi2t*=zb6%t7WnM|f3 z#^IdbbCru}Rn``;iDL8MRztn=4<;s-9;vIW{^@U?9h^t4)DIPnnr)K3l+=lpcEj@t z3(O?s`anhh`lr7Ce!C@tvF?#WOF^s*%d$E*R>zZvm<_o4}ZIZS2SrY5#BvWowi#_?fEcUiVC@+QAvk`by)ZO{QhFG_1=u^#x?q^e=u-xdSAh@8!x%B#67JLvukO(}Lau{c^{s93 z7441E3~R~5dc;Kym_lKv-k3bi`26c&gf@|f4C=R?WRjIk#uwyWB|i$GXYY=Dhj=;U zG8D!w43B)L8CNVG@^&iqXy`z}FNV)qj(#Niiy{yxjIU({-QysPoOcSm(pw1hljU1pjffyzVUu*|Bhy=5{5Okq=vEZefNIsB23? zvJRPA`fuY`%<4BBrw6L5!}Nj39VoJ+6HuyacEf-?%6x7w27CkjQtnRvdn0>|XEX7@ z@)=Y%wZP=NmLikcg^SPvkIu12wJJW{RQ{*vWb8olC@>d`WM++4XfjQ~hz~B+?7Mly z`_p_M?C!QKU`i}=e~`D%@vGOmA;P9O8FWOY%u=(R7+p4!%1UFj*_R%q+WnW5XwLqcO?bEk+P-( zk{DPYX?_-h^8(LwPgO4{by*&Xizc&H=Y~gs>MI`Ybw;y8>pMGcMr&DeIUJoLaHEq| z<%G(7p2^&EYfVbbMB?EYPS^4w5W$mIh}?DUJ>Dgm4=(TCl;I^=N-e!VLv@!Ev_b^X z8ejkVc3z(eE!}qj((S<59yuXWP=J0Oq}qzk=+O`Hy*SysyuM$YbEo_eYGr|K9goXs zo^PXylS!+y_*c~RF5jF}*imF?WiiAA9BQ*aad7R1oZVM_pCL3u*v*O7t~&24;Jwba z6B#L}M6h#^vjWX^DN!ZiLvs?j0fzcmeKU+&9(eB}1dpYQl2KT!PFYc=zvqI*qL>VK_`!RUmCMVTu3o!%0qXAR9Um>E>jMpc&DFke@e@(&$JaqodQ+}w!js{YR`?vwq97-+gZG} z>dmetpfZo(yvMS&vD&_9uWs++5g2?VxDJC(C0OQ|XtU-?d}p{;;++vnUC>A{6&);} z1BP`dopObBhWQHt)Zz(lX5=$}0{=&f9+cGA%f|m%-C(#o-SeD)Ks{u)I$mXt72WX_ zoc=H*N?%oM`H)*QxuuSutZ~9Z6mcja!B$K#*{HTELWZjgma~)BUH3WTUgN_WTz7k% z46RSt32xyl9GY5Ud*`Mr)F(CYj=qzk8cuA7udLW0bN2yFo*yd(q7DLVBn9J%Bc$6u z;6Fcmqr!!>Gij65E=;prK@TtoU?P;P`SjiY#_c~Bq|ETC1sZ{kW2Vd)Y2M7Q0FY)i^Rgy)>Dl?VMI?7^_*-T zQBO!%qRJ4&4-sJ;Fy_)aZw2AMs#@CHSqf!G0g`{?yval?WWnq<^o$wM&x{&0L6y&4 z3V`}jjnuji=i*BMWyTUuK!0h1dag6j#*O>kYWNgLEhj`J9y_DhyIEGXbCE-Fb6;og zrZKAy#w=qz70BtY5o)mBv-9iyoyU7LRWH+JyXkXkzb4*1M7s$8Ec&yd6i1E(8iKi{ z=)joGbAXl(neRi7s->DM#@9~pmcGHYcXZO+dN!^7*T4MN|3)a8@89R!%-f$2apx6s zHE4Qa-c^DZzbCl&pT84&!S6ct!>rvi()v0vgN`##S+TRcM|ey%_xUON!5tjO~XPp39)B4 zC+#;l5eTRudfO~yHEQk>(d}IU!_vID z{Kgeih`wL|HbA?jTX9lK^=~h3-_q*)u)_YQZj%#7+mdck^^jUj(Y{AZ9bB7|tQ#N& zE=Pd)!P2|~S7R7`di&18gXpXW1LbCzkjkcLIVl)hQa3y_`T%#M@?_F@Icf_lrpSN2 zn}d3{ln*V_EA%=LJ2Tu)fC~oJIKhZ@t+u{mU^*5uv^|rGl@=yP<HAPBxYvL+l1nv4tA6Wr26P24m1$PS@DoTq?w(O$T)^3o7I|A2YcZE-E5;$VE zcr617Z_@F_>)ZFk5IUpx3>$x5m)!5T0m_>%jug8g3}>Z&r*)NCWBt-Hg=goe#*Wat zZDq&`#H4K5kh5MOYj6grtN4T_l%>{2o3>HZT5YDY9}H7 z8l?U1bi<5K!rFv-Y7#~Q?n6ZLbuz-G3~2Rl52npUKy(uox(gp^jGV&kgZN01Kgf6# zikzaG5!4m7OGGo7GjyLP+H&#IhwrdOmDTu?rsNsi2bP?Jnwe9>QGfDP&Ca+F8<({L zM@e^f896F6&13~_q}2tw`ksOLx+qybo!B?0-yG8bM?$N&N11xWt2O4~+f4y#Og<^6 z@~w9Olc_q_z*=B^FB2IfSdQ|pMW*+G%TCQLbHhO)XA{!BvdjFgP|)PXu&Ad!Ev30V zXcxQtVEgVvgvivZr8|~`kQ>`b0_J4st`Eb7cw4|o+u2;`oDHN8;UOU)ZVRYlxlKf| zByjWEyvF8lswPV#n&Wn9Cqit|8&+s*>(;~V$Dd(7-dnduqxP)-op-+2{%l8P=joSU z?AhAONUw@xl@xyhZnUuKRut@do7aIa@y{%*&uf3>!Si3*YJc<92+ z#rex;f&8nlxJZlhVE41TqEYK5nARK)MRJ@v+ymJOn>`~3_G?6BJzqh+kLMx1t=+|E zi@Stj-62AI%gONl%vT2L;vate-iQCB!;XM+F$H`^m==juN1wGj7nZ;mefR6UC2n<< zgaAVNLaBS8jqB|wZ)y?aX=if^XKXO_zEfc+a!@Lq3_CbHpIk$=Y>1^)1m#>qM@4HL z^9}0v>W&PtrjAC1fikkgy1Iwg*Zl51rF}c{L&>**QT;cKt^z}BPdl^4XWSt87(!ni ziT9zhsW>6DO(k~iJyoy2 zhZMP>9ix1sy6ag&O**_3GlT^$W$E-CUZA-0WKO;!*Ikps-nC8X7|t|D3;lW`mSIn{ zDh&g1W*g9Ai}a6(IK4TM$4cx#m}f`2rXv_43g*Lbi)vj{uFCJVbsTT%Sz@akQ*I&h z1lj{7@1P8DnJG6KN$c{QXeMu)BI5AzBv-ARdU zEs+x*3nt9hb#B6F6S(EQXGz8Pe=d0fWfRK{NY0bp5V5RXIsGv9t%)B0h_? znbagi&SfXl@^vV>Sz67!M6yF9B3gc9QVW{8hJ!7T^vE#OG4~n6`n^j@TTPwQXY8IC zn|bL2MwfNzY^PVk%jeI3>p1v4O%I#Mp;Cz#A_)CHpz5vw$pfj+)ofNLd));lBln_i z#`YysV!hUR@9R_il2=6_lpaJP3OMsS-!wrMYZ|FE{@B9}5oAdeyIPvj8cNUEr3g-b zfYm?QQm2QYuo~*#kya!9&=Fd1%3l$1e)21Ht}#WAqjCmsV77W*3^rEctaR1og_0jy z)aX*NGir^bS?W-g=yL>F?PPxPSK7dsRuDHfFwWV;1?ZG1wS#JMc-e2(qO6;DlZcqN zAs348)hm25Ww+JAmqZCXnB}#F$KU9r?~Y1QT5wvKQ1c@pK=twJ0%o0_ysp!y*`~Be zBb4F&u>Yj=UnHRb^eY!Ytde8etQ5dj!}KVf(lexM`-nuGaXx}uj)KfaD)|PN?|Q~t zclhpuL7l7NHf<^bO}e$~TNu>qsZ>;2KAVztG}S7-1UO$!uxnLWz|`*rO0C}LDD;VkL}5I) z8O(z-ckG_xAFRTsNT`#8x6DQk+BjroVVlV$P9e)mS5CCr-h*>3D{Wf*47&eh?WhRU zB#Aj_#|$NkioAnRm`ZIqhW$L^IB!e1y?gapeCYOW01WGTS1!Q zXMs_o5&l?pr5oQi6#o3}hYy$cxvvgCjKm!RmPKoB{X`@+K*Jm!mS%2*1>6_qWK6eh zxoKL!{~qfR;1inQE+P7_-jl23VGP}|brbaS##cJ|H#WTEu0)-tZpR;RwA;>M=i*>i z#ez+@&8?t-cV`7)6I5~%@HG(^+@16OPm*3C-Sj}a3r}XE6@J)lL&vQ<{Unw=7{*$K z_np0`MzV-AG?j}h*Ix*9_94AO9pgZL&KrH%78ic|yxR0%!I+wdNk<)LGIZS9IXrrH z^-@h9ooPD*h7R;wEFW5PEdJBplgEqgrw=4)-hcVN^UZ$C7i4O38%ot!jzb8UR|hz{ zg~{ux7j%#6*$^YXiPx&x+LBbkW8W6C@nOf*4sjo0d7RoJks#Cp@>vpMi7a6R5*nDT z;3|a+ztF;~3@7l-f;f=J=v=N`p;mK`UH=!Rp0t1ahilgqvXR9oUh!HWtn}9;P z%Y$#pwI@MOju{=4H$AW0U1`{&8`5xx-}I!?tI=e?xq%lnfAg6FDlrSN7u|a9T6+0z znN}B!)15(hQw3Q!Ayb;$iHzOrp7XLF(>Ly1E)a>Dmaoxz;dS9Kd^f#NXux7;n26Vn zk~x2C@O7s4^y_OU-F+HBway0N=ycm0Dyw6Oth50+hF8_*Q!kZC#L4n*4d*s=m*X^8 z?=JUAYoo>s8@kJV@)ztb2L_lch>1T{cewzZQr`|qCE+x>dJQ)n9lT0Wv{SE$rTS_i za@a`2M)8R+jXYB4Taos{MvPS{-IGm*;~#O1->m|sgiV{Ms$Oo-IY^_#Nb<3y9D(c2 z?U4OiUWCGWXFKqh!lTZ1lVSRU)y{UmtTL)Qc1KM0p<#Bbm@QUBF3t%V&)rFf`%Vrz z&8_8gQO2jcyz*o~*h>JCSUPWP9{h+jMj>5-U%$Ftq`i$^LMe4qy_4X(+&}OV0!wR{ z?x0MJ8x6Uvqqrtq%|s25{SPeKQRNL(m*{$k!XB=bw)rMsp1f1^)@fOWf|xn!AjI?t z7WRW<1*B&K%OhuwW`+GUdsoM=~SeuQ=6-?Br%f2zK`z)#(66eoD zKJME9x#zUH>1$A4F%Lbvv>*UwS`4fWp&TZuX>;$P*S{-EsuP>E^711pv5H)m_C8s9 zI#S4X={bo-vU0qrf|Y6HCcRj#>4NWol=8_%vQWgn89}Cp#N;STEF5CH3=d9eTc=D% zOY$iAKc8afEB?SxE_R}L+WH6#L?uNMHNrJp-yGK+r^l!20cuNiwl+4Hu_7;z&_^iK zQ9lDU#mNA3M?i|{TP^MIW;;C8u$xhmRUsRk|k z>>zdxk!@w_BB~3zWa|Y^LyBHRvKP*6^SFEyI4EQU5cnElQJD|km28b=|ZF6kS{2kZk6>C+=WvNDzN z(gCdagu-AjJZhY`C}8Xbl7OikEs04_BS-aK6|EEWdvym_5>}GffktqNeYAc=i+{M| zJgH<${nL%*YZu&9rj0)MdX=ovJ;qa|K$xj|$u%k1qN)@a# zdg@#xdCL4z6>u9AfrE{M4s!a-RmxMv_x>`N2y=qZ##WRsmADir$)6!Ok8?S1*)_PO zcSqU{-_e@!^nl*FZ)B;KkPrGGJIkUrU0c$S1)#{d?s9y%eRt;}v#9PYXe_R!lqO}V zn87osR-CU&1+o)#A7W*&L^wHJ1GvTlSZo=63XQd z`t&M&^}xC@9LrS)P+x#59h{Q{{?@V#bVCLZbvUviI7&%xW$`MzMD&>!Up=U7EceXV z@}up`t@k3C0waSD1_%nA2b^z#b*$9h!Izzo>IEMr9k&>>^LS%o4*%5&xii(sDH=kv z@m$<)%FEl`do0%0HX&ldcLS`FFn<6x;sOw!qePGb2f2-gIQeQbYz4uUg-s^OYmDs_-Q-1#gdJ z6Akmp3qtEG3p8&OC-Y@H;@YNZ$98ErG3kWUFt_Wm)<#DDS2!drA0pn{mb+HyXYQ+#AKUQHCG?Hl z_m}UhLVaT1DA;9VT_dzQZ*Gt%f-D}`U>=d3dT7`3>ZR*Vx7jaSzC3~~ z24L&X%H5`Nyljc)45{SQ0P0Po*UeHPaP&?Hu$%t0Lp$2a6(>jMjvc(Vy&8nerkW{S z+AH@yPf9c5uD>{$xwD+^0Q%zeSbK7Vx`~plSm~04JU1rYQbL!S7}f{3=ZWQe7*Bcv zn@O%=+)mt`q*4I3u8s<}F^U7{v=t`%^Argv3G$#BAy z%^q^evK3rc>{RlwWwa8XaY49`I0x3C9FHfr#hiL0(*Y9A7gcUNs5$4vwYp|(PKl4` zZH?ZBT6gO+9QmZOAw1pkSZ>LGOS&}z_5AH>R<>-gJht`HuaP}Xo8)@79bylpM8KW> z(n*$;F-&bm%c1ae>F4DeG*4eiq_eb`Irm7EIa;gN9H>42LHeZ2AzEayC*j4M+`4+5yMDwRT$F{KTz;+SfWGG(qSLhcqTTd7HCQ^DAp1hC*p*ZiP{^ zsJ7?DkeWk4&qO8MzN0gVx~}`whCMzwMG40S}rDavX#k<4aAoPp%EO;TE#>gQqz>m z*Y-EOb=nbWBL~s8T%f0H#7^Edx~B9=86L64tzFnPF{Kox8#z9ckZJ~V zd*n{s) zYi}!jkAA?F$W4FqYlG%Drek|G>J4fpGoVlTzJ=9WzcmdU4}Eqq!;1gqY@(E=NtXa= zXt-Y$XS`XrbQsYN{kAYh5cioyj(&r;_e^Ph0TnNyy5Q6NNN%_W+uMZ|;lCEd5G7RU z)5##{W76Nww+a(V(|FqJfqR|BrZ3;GzLjwaw?1Y!OhFcJli=K}^S}oQDC(8IChh!9 z?_C(H#ID7eh(r>zH+8c)M?7{ktU-!kYp410OmV;v>IOpF00T)enk|dQjMgW{lPRF8GwLSJ z`VO{R6S+!fYP)>K<1mlBrt(7woAMI(gFA?LtB-r1)UCE<`;Jmek*D#gq(C#Pp1Sf4 zQCkCL@q6ug@pTFEbh*%~E9iFJ(mHq^B#W3T-_|O^(zxX&R}&*mj{Z3_$*nQ+(K`0tLKMGby{3}ef+xHS$rd`^mzl}*WmPSkw{H^+%)$KV$qu~ z+09Bkmca4deTan9BP=a)E1OvJvTvCTA1{7LJ8`m+7l$i`8V{)%RK6YkC3*3~AHUn1 zz#SMk?|$d2tnO;k0%-tf8EdP?ENRSwdt3nsH1~m!CV9j@UStA6)+ix^8V>6!>8a97 z0>j5to>A>b*)gEY#Y5VOP;wGi!!40xoVZ1&HH{LU za*Bw3cI^4BY#qCNQPyz|V+XjqiFF<JPvy|0d*)8MOl$u^SRkbDYg>c_+j2PU5Bn(P~w1l##VU}wkM``^6AdLa)KjZT#IrWREK0XviyCbQ33@5X$wT-Jr_#q3np7 z;{$}nlbzk&CvE|;awJPC>LY=d#;pUb$I?tWW|@ga3`u6iW?DOQbDl3L{7@vt4oUtMCTpW{8B$>d*YbK2jahBRAR06{_Sixl40d#>t zyCBXXnI5B;74W2^zFr?ySRg07z3|Cb33D@$bGDO7F z2kU_Fhoyr(Uk`9XPYwi?S4r~dPq194f!x5#H!ZzAGGJ~b76KjAqEK(cN1O}APS|9N z$+L!WS4(42-(-z(8a+d~`eylLcbQheQQh0SEormCEfHfV?HNR+aj>r6J`^vkf<|G{ zgYnW1v*J;4k6cXf+oXvVOH~7J;%XkY(-g+MSU%T<0KB!OE@6xJX?FT_S3OE3pE&dV zDe%Ki9e|eQf1q`Ao^Oz8fLY974N}nD>NC!;Fp85Z<{Q-m;%6HMFhoyR@}pQoIvXQH zByFbQX+Crf$MBH>tYgDus&9pWeTnh;<*<9$Si*S~%*5dgw1zsY*nEVJtWL?Z+Z>3v4|znva_`Cdj*a`xEg*g64&(N700eG2yye0#lqzEhPUxq!_GXu6O4MS;(@>?aXteP{ zraxd2OJRF?^7b`VZN3B~9|fhvJ6f(ii%f5YT)BuiM9za*@BQk~vX6xJ;^#X2bC07{`%P z!2R3KtFzx8>!-l6`Yb%SNGl#jwcXY>p5J386G>L@f)at_*O$ub7A*qAteHwD2APIO zjc`f?5wlm^KLc@k_@HI+rjEU+lJ*BN!P>0IS5R1zI%1O1IB9c6IP8~&VZ;Oh%Zh1k z2c~d&cE;lf$^K*!UOmP#<=>t?V=A+K7-W){4$1^_v>qaf$Keff=CE4L9XL#w7#3Am zI1-%|n-)1jdj zyd*-mUNxmz2Zj6a?#u_Ei>}(;a*fZRxMRrXFL9hkw(BgDwnU&qQ{9n8+0)zspsMz_ zi+}S&1|sse*up`)*b2zyx-_}XpUUW%38$O-J8;;HIrJr6Cs*V0_{Ear3=cDxSPuhpRXI#L^mDguq*#^sgN*H(UnO%enmH1e9H0?r6guD(?L z+Jqsz%2p~etVs7}zprEW7xhUF#PDG?3)6N`&sT~GB~G=;c?Am@!SQ4R`UNx;PZWWJ)0ZmP9Kl zY2Ds)Hy`RPnRft9g|Xes1}8|8m*(?ciBH=f!#yd{*=~Js`RertTt)c)<+qn>EotqE zy+zkq9^&s!k2TRPO!4lD0S3~ofM4ADVB;9`$2M}6f#ODloKvB-seawkG%G>Gov7p` zLMG_bfE&JS2X||c7%|;`lZyn?LT{d*IYCXwI8C4>WAv$JpZ&TXBV|p?-!AtV;U7mK zX7{+{MpK8Q*pdB_mg5P8GDao>ST3~Poe5V!-EtteVGXca(^1PZd4e-TPbvE{u{>tF z;vVO~AY^Z8i3UK=3!0&B*j=CY+V98gm}_?zv34aS=m5rR>b*00NG}g=wEAXMN zWOQdqcCaCi;y-U|c`nk9s_p%-N~A(#)A_V)ZnTlu%BX575XDL6leaj`&y{v1IE6&6W?NsnxVhw~l8QHmVpo*NuBfv65k%v7MC+{sTGwq7h^6Ot*>y%tI&wd8wJ;iazHkj3oup9sFT&}1Cwvu zK=*Zy+;}{7S++cQ_Kc!pTozxuo*bX=B+D{=M=&%UeB$~VjBNTZru&n`H4-gt<2I-m zHNGBVE#$HOq&0eN;z<;5`!mrkNOEA=DCLm2X)qz!IJZ1rlUCVzJ(`?iqOvl|A+HK5 zKyM0fweFh3;W=#cCbPwF+5Qs-D|Pg=7>Kop&{DgB5P1*p3b--Z^TjWAezWIuLSh)J zkf&hlZT*{%%XpG?27NV?cIepUEoUVhq{n&kjPwidirFXTu{!O+80}}Yfmd$o?Ap0I zfKPCR&6H0E9g#)YAYNXJ6LTx!x1J`^V`1t{Z4uD`2J+_KrDMF;PpU{aaPVPAKA} zK?b|IM%#3sBS<+tu{7kYXt{@uyZi_8BoN1+iyd~(RG13YnSQdYlui-pvOn_xX! z?(Xcb1G<&N<(8*ptZBVKQr!j!_m+>Ae`ikLfY2>}F@UinUG#1guoxd9_qAq1=-?9K ze!gu8ot&tC=B-tZx}z23o?<3sBQA5RlY_7Jx6^o@A0OWTv^}-6cdz}p_q6}`>5jlO z9(_~`RZNX+(TAy z-d8F_-n!tYD=iT}tt#CxL7KOjNyBwO@r-5g``tqO<#kB?Xj~iubkY2b7v!;jsjyHm51CJ;MLnuLH~a>&A8! z!Y~ho<-|m5=Ny>gj3rCrQY4UHR+F1DPC}CvbFkg7q6=;%+;zHgCTOkr=h604CbEU= z$DfVmi0~J4CkmV^p;X0_Fl{s#A0E$jCj{2G+fo7X00Uo#PS6WJXhUvJVw1Usyq$8W zuJF`Vwk=X232p5vsb0moif6=+!;-7s{1QHjexqDCCP#9zW^rqFiEL{T38J4-21j|W-4 zsJ$(a(^h6&kzYyl&CWQsx=f&UWwN+ZCOxTmS(R`*il5r&B_#>ElVVYci3E1LJ%T6{#FBuM<#`|J{J#e<-*F`@yk9uC=RGZD%L}1v$}pB z{yd&6r9m>m%8wFRlv$$8C_5s32KIq7f#T2($@8}MmZ@oDXfZMnhM@~V)C|h_3(t$H zyRz(*uux2_&8#8}g{K_yU9t_UD*je06Ze*I{~Y*#SeLc^mF3|=-prz}Hk^AXRig~} z-mj?8$+BoUO+GH8$p!qgP zeTM`qMw9^137WgN76Aw)rrVKMwxsont@ZK6SM}(a`1ddW_x~GoX75S(Zq=A=5{!A< zd!s|ii!(3ZKo(`LX_wwW9=A6A6H>?UW@4FLdJ}yn1in9c0^`p>onjuY&CXFc1nSdu zb`u}vn~2`F35%6{G_5mJpn!kd{!cg!5vtmY7Uex@3i$;)B50geD5XI=%92D|LMftn zmP#dp19_coZ}}|pB21c?`j`Lm-zz%j5FrPYt$*bXO2LW@?oOmMVJiVwlR|*rXYev0 z=Ao`R^M`)7?k!I?`atmxZrA|B#?_msHz&6iKjbb1!YQr3X52>FJ@VQL+Mj4!{Q1`1 z9J{vcf5bj^h}8WAYx6D0bA>Oc33;P`CLbf8!)XJ-v#R-E_sA^+7f&93%7vz}XCCkT zNoCUgZ-X|{X6)1UI2P}%6&N1cj9OASnuy8RPOf(8kv$a!0;7Y)& z!{g0|h1){c-2e&ozGd^s9ELx;H;aoH&*k81>75KB#F{&;pRIVhbPfmENiEf;sPPlx zfp^R3!5GSfrU~{boa!f?og2&t=&NLEG`zML+KST>C=G)voPl~k$6ZK>CvRmLV$K3@%EhSMZ4(gVsQ}4BG)W@eOqRzpZT7OJ(6?XfkhaWlygn1KKQcfj zg*5Y%^{`lcE=@(2W66TCFBNKX34@}@#zT!A{w+KF#YXu6bLFDs;4nKT-v#L6!^KC7 zj~72#e6kVzN1MdO{mS}E=Kn8PzK2a^zjnd#cn-^caqWU<8V@U1eMTMV8Re#v#jo+>;YgPd8mjaO5A;n-!^l81qTh+P*gKL8?Cjs^z;#VFB!H zam>%rGqMg+Kx!cbR;cq)0L@V^xdH^eV*~No!3Fi}8x@Y(r~zj=u~^(Xy+zIq8%G%% z?wUUT`h~vc477>0;rnu}=&9dvYi6}yZ>c$R(LBwOV;8GVS0&VRb$6al$>@bzn>e|H z3?lgH_JiHMd0p4;;0){HJRk z-Sr5xS?Z(lY)=l`o^N?;T5XxwlB{lSn6DN5U?6es5pE)?+-TYofb>KHsT{Sc&XXyO zrmNKIy+CRRwpA9JYEgkO&>UCVEEHY6t#x)l8?GSrg*4eF)JrF!es~|K4oKz+VBbc< zlfy)iR(o_V4{ibw!BOl7oE0v2XjA0<#pEeLLmn`hNgjy`Eq+nCi4oBtX&~Q41-+c5 zseq$5ZP9`AM}i+}@YsMtK(Ao_;1ms?n}?N&An`v;pp-CS)#*2YTVI=-u(I@Olpmm2 zK8teLvt6!+2fLY2(pFTfaM8HAYe7dMUeJ3Ebxmnk!%&+ME}}U_MG=j9m0c>eWOyAq zv@fscgUy~W3B9`|1}$%D;W>eFRj?>8>XER_=`Me0DetO>@SYtIX;?tDa_c4ICk#=$ zl4y-CA%+4XCLgx9n)@$!Ov8KVrH6KsY559c4aQQ@mPW zIZRgSwJ1}|gq11fM*Q7;ygGh@B`fqT9S~o0nXf*aBQ@6!pPs#5e(`#tGih{ddI#EP zJb!sJ>ui{j6E&ps!%7zQIx~mql)t9=>Q|78x0)?zoGjbDb`ci?7$p|8b9<+5%wVdGezV|q>%=BD_HJ4q|HN)AvwXAaC_X7O>$ z4`wk*5@aD!#(W!c-Lk4^$}~c~B8u^}YpzIQX*U;WT1Fs^&6%qe##Ze(fq5^*s}~dv zsxEcU!Zdnen-wGE{YiX8xr9-29zaidA0oNi=}>~cHbMy9$x|}A4GNjOd27c+^FZm_ zICP4H`l)>y&`rT4!I`)2(1BMJWVr;4mBoC)=16;Y4KuaqwJFR;fs?6HZ`4+@P@U8S zB0$mmnhszd+TumtX)GrVOWDczG7hvZ0q!Rki#jcxzT8)C_QWj$;5Qo^M8x&L#a9$Z zGmgxSuPXrE68FDSA}J02tzV0|PpMg_ZFcJET)zOvP zH7zkLP6iQ>sMTD`9KuB>`*eW)zn?-@zfUD}H@!q^G1mQD)BCjcb$;eKvGEeXbVU9ZDMZ_hq;KPM zS_4Ar`umf=BYUjN=)PRmA*bc!SjkZ^UpP$xIT12!#XUfV1l6}k-^wX&vCgKtBQd;5 zpgh;reOOfoc7b^p2iR`1xQG5dK0ee=3Ymt{I=PoR&P{Go`*>PT5%*J@?^uQuF_)BG z;O1bXusjmSZc?1Vhf|wmQ+UamhFkj>i|PSO_6AIqrEy(f%+|nYXO{`5?Y1}Z2eab1 z{tDTitRoDDO4ChB&p!t@-Jca;(EeW3p=1vqoS(?*f}iiR>#Sc$hr|EiekxAA1XCyC z1VSLD*(GSQCM$Gcb*gC@$-mn7p04&0r3&O_6+!VB@}wq3$xc#X-74FcbAh7_#kDgz z3eYRuJ-$(t2GnQsSl^aT$gQnswA|hbri)wra%;W08Pbc#em_3;`)eNi-PP5%({uWb z2j}B17XN1r{hr7En+1%p{t$!K=OLy=sFaQGp4bYNg*-XqOhEYj(a1~!m2u=5<}X~y zTV2`K27Gl}$ZuwEq!vXu6iprT8Yh#AV7>a8nMws|f%}T1WvHwR5v;~o;-9Es5U-=S z27S~w-RkGSa3o1P+a*qJ8YWD6p?I6a%Dc?X;C%9?sL1e2t%YUlJhU|ca1C!?NV(+C zo)acPt0!(%obUbdQ2obzlUCV0L5g|X#A3>sSa9*~-aN52f~?OgFf=(y9Q|>w@-Me{ zA8g-!xC1@hP_7M(#isCF)Parjy#HkPQ4#4hYJmHPjBBlJ8@Ib9P#+7h>ry7L%DH=Z z-SskcQtqRWI^A$}xHpukmo(5SW7VMVPb-M5ill_Jwd@PyrrB7t5x!d7pi?t1I=H6z zf^suw9j*yT+jLhPYf_gd7+D-eTcGF^JdYsrsp6BUQ^%!kLcXINT=RY_~~VjN>(Lr=O4WW0Kn=vc+@Bvb2ju03_F6z>4vTa)x2myzG=;NDxk za>0*m8U6PrNYnG{pM|IediZBcV*GUR-hZ@Y8!FLJIB|x{8)BUi(r>3#PMs954E{I%XW&r^q`lW>&B4rdvu1e{*(RF6YMAzRhiPNOpacx`+{q~vZJXM=K}MU?2S=rd6%s*4z$oV+Q9A9 z|1947cJbcZ{JohCS7zyU2p_%w$@?FX505ITl-5t>Axjg)A1tr^^MfDkySAY@6>#iV zv0EH?5|Jmz&yN*TuZvs7GY52zX6d0W0;&~H9-sd}A4+;5R51FJxsQf+L^%E!p`(ml zY?mO&(UyGakGNM^B4_6B97a*L_7Yx1PN~ip-&1o!PiaHqS3>a3CXZ3Tw3ps%Sg=Q}+H@5UjZ5ud=oCWck@kzj{_>OwP%$&^aedYE zzuGwi)Saixr@K3!N?gG5@r>zNkrR`V)D|KNv@Kx%nVE0nff#zoifB^(g>_=)Fd1tY zs{Op{5W};9?l;C?Gn)!&MPx)Zv{rpM@;D{Q3Y78JCccwzLE2>~78Gg3mX*xN|s zjA3HroqaH$Cf;cw7gei)o=jq-CKB9eHm82+RBdkvui6!MAdu?aV4_2z`3nV#Pp?kh zoL_2M@Q`MJ?Pu;mo)w<~EfjYf>k1#1>ueTl#dj7s^SD9dv zVTv6jT=ek7Gw`pL?!|QZjV2Q}njJPR*-@|Q%!UsKC)+S`jv~23*dYJ*YaJa8Lv_G0 zZv>@!pB0?vY6_Uo0gf;4lOE)s;~Oa3$`;_2xD**P{^#;G1ESLjj}G!FEZ&3FO|Au4 z%?v2ysmj&u1@kQ0Vq7a6h8uuZ_yg%V#I-Y0`HIXSjXrpp(GFm8$~)>3D)2u(lpnmV z`gwBzu(5+pXBVw$xX1=c3#bD?^layR24{Y2r@^uWyHBIzwL}Gp<#ROL(SB(ukNmQQ0z@b3xwZIbFMCWzg@r`F9>CF!xJjS2MW}Y*DV4+sCOzITx0R@`9 zd~7YJQDR#E(zI*hjiF|_>peqd&m`Yee=_PmsF=)7LAc;EXHJlA#zwfJ)nCoy*<%W3 zz$k(O$1r;QyejBIYqy-S*Gm&U;m(BTRj2JTX;!U#F&vdGK3T4yeoFdiO=x#_=h2g2 z?(|bKavCwvMDm>A-tL}nvbI}9C8SqTnkTPwGht-mI0dy(RS*rmq0<0}6J=l>MO#y= zMTzNDs)gVHUZ_)NS|*Wfog5B)Ax5Fe-{BcSw8}rm&S~~r&WQq(H^KW?Iy^%R&fKeK zX?As4T8x_VVKvs#uSvi-bc!=DNV4v)_H{+hN1n z)RzAI;NJHBj+r4aU^C_Cfr6Rr=yym9@j3RlGc=`)pxNtqMqlRktHEU-q_2y5E6=%O zz&Cgq%rfMwy?Ta5{{OXiuG?`ON1oTmS-iy7V2&3}n+>8HHxi;~Qxqt1%-e$C$QsjY z;?lU7AON8Ok?@%_+Sk~x`)KkW>s}(7paS9J!ge;NHn@CBO@atBO@c@(%_$3 zC+dRGVAd-^6PASaBoyUXnB~Po)7k#aSjk3WTUB zmzT1KqeY&*8=JeHpRKeG;%Y};Ff|BfCx{mtaM#;Pap%M3qha>lbLyFIQ zg9M9sTw+qT3>wB;rFRqLKCUKK?cFjknOto^_lpQR*Nl@Db^olt!zOmff0w$ko(bL+ z-)6?vk9gZ0FFxYE^W*m;xf_rIm$&U828xj#Q$7)We?vLBy1;P?JC0T-JAb|I9~~ba zk>&a5Qp)pHC#lb=`3Z5xGV7n?Y+-5PXHsMLc>lN`B+)Omuw*+{c8>*u<}`_DWMxnD zWUIuZ>-&4>S9a8$Y?9tJ!`}OSvV)RtcGN#2g%PjXt)sq80rTu1Z!Z1nfc2dPhc!fD zAaJ@bOW$NKnI|9;>fPjHX5ws*>RxOzJYw4+^V5Z!B@-X=ie?D|LB|`Ic@MiF1UXR! zg~F_UzO#6Qju)I$@VJo;dkV@6_q?)&>=(SDZzpFrG&482KrME#|2p>&-<=J)VE$om z@8!$S7xnyRRXv46HZaAbPoJIR3eARDJ%t7v)x!v)#OSy`de7I-3A5U$qG$eodA?C? zpPkRmEjQcG{kr*+MG+E|6shV2k-~BN%j3(hu6BKI{rpPV$mK~UD`k{Ckz_}W7fNgp zx;@c#o~lGik(J0sB%*zB)+K+}#a!>`^74GVaqAYo>_=3GQanS+?50;Mvj^;n~+91A^Cz9BxZtHCPENHKhD^P0^Yq1Vq5 zs4kxtXGYFN@G2W1?1-D1lSc|+n#N1`ki{m41k)=eFhvpzwwg<#jx)!lQs_M}=Cias zgQcZk;a&+B@5$&UOLBP8*q3AC6Lm8R+i=jB6REj4Pza~4Ve!kc%PNR&C6}bYrKN(_ zhZ~NJP0F{4>NJ6cSwtF3Ja7LYaG?zXSuVkT-Bq8LiQq>wjH3h`OUh})AEC@)`i#-B zm~8=5DS@=Ij6q!FEK5cQSb-i?-3}8m(>>>bmgFqN9tM3Ow?n1jQw{`43rTlbTKu{{ zzUJJm8({NXqN*TnN=k(89+LEsy*wI-at@|-EqNG0ib?Z6WiWc^*RA19G?Hv;49DgU zhio6Xi*fcNK`i#r*@ug(!7I>b0(i8i!#7q(QLI2f~?Rs%e`Y08k8$RiO| z6F%Q%+S#eaZOITWT8TW|m2(}9n6attokrS3U6R@==d!9XQFx5ip@uq_8On1tFEC3Z zcwVNRvr|Ai(;&n+TjKz_c(^EHn^i!fz^v-oZKusxd9YPt1!u8LCfmgshD&5R77-2Rfu>i)zI*xuOMo7^Y=@J9?{qxYZu!=L7U zT`5$E>*8O_0GX1Vvm8m!(nK;!Y*93+xj*NgP(z-TaZ8hOn~HKtp3lyn9e9bcw=_JOiEXp{*@D zMh*Al*>JjMZOw@dMs|V^52{(PI)=8kd`HCIJ3FDikMH zXxx{E0?7ng7s(CuIsutr^W%egAvM|wiPq0cxfC?yzCCo|A4{YGsB0q#j7qD2d1b|E zl>MTlo1Znu)4-_<(7-smh0$#J9e_%rc4goUS$k?RgT%hU4cKztP3%w|#9kT1tkpxa zdo{a|oT7UGD#@dD7yiwmdEAGzCnCL0_nXkVhBgd_tT9sD4Dr%`#pH3tDF=D5voEkf z>;SZUXNpGB3Ht8d;O4ZNPywho{mUk5n?v)2LAtbUj>?#z3nLe7O+c12AqT^?fOTLr zT%TMbZqF{OI02|QYwLqa&UX8V*kNq|aAWO-$3t$8;5KE|ynP+hZjdunLfXtTqQ96f4yT|*m8@ezQ&^;p9iLWq(s zjMk?CaonB);gAAQQPH|RwbsJ?W+(j#fL%kICUyYgaQ}7!PMWpiT3eqcHX!6(10W)8 z51PXajEaC(XAR8&nT4gss|(QDlo*MjnW4Id(Omt-4YIXF($RCGeM~;q!LEHRuYLq0 z+2O*{Sl+oiW^Fi(C8C`}ra`MDJ`1A`K$UB?dw=-DA4;n<530qGrCF=i93udI1f-5x zt9;h>WhdX~$ksnTOs(r96Hje}qbpKd<# zeXRxPulTb6U_isA7+P{$$mw?st)xV(4otA2fZ}EuX(`Is2v`@O+a^ZBDx$q? zHkeL8RZRa>l^oT%nS$AY(X!i*ayWV_w`2OB>m?0vU4YhYU#kI`hh$mw=b@E2d$?kX zRt-pz-Lv2%VwaSN%C%Zt!)D2#H1&;HD`7MUjLytjXXjcj4GuAnj8XY5Q$mBwgGMEp zJs22`JfzA3v>LQ+B4eKeP!S_PWw)dFGsPx>cFfw^(1_gzq+`t_j3!2uRKRXfC z6&K}+29ablZUGF8N;~b)F`59xS^b3M9W5Y@St|lsv(-NV2x~rpYH`+Tfa}7@PmmLU z?9aNGWCqni>}5A;OaMB>wtn6}p4tGqFmib`0my1rPLC`tks^)UST_L(x%8$0rxVbU zNyrI6mYgY5re^T2p{%ui?$?2me*<;xe;2p6rljAgV+PtvkfC-!(?7TM|Q`GwSJ-Pn zR?M{srJl9o&>E}XhIVz@F}O~Q>P2gSY}dyBg}ry$WZyAsLq9><&k>}ynPWi*%{MlR*zRqH##E`>fTeOEe z7rU7Ab!8wm1k2j>Zh`6@d{nI&Mt&bMn>Z4W{B&`)pJ}->&R5V-+#(GMV7q9v?nBev z+#X&iT(8A7qF>&(p>+VNmThRd#q?~vcq$Sc)o$cGTE8xR)l0u-?70JP#_qz%aG&y+xQ0rJ%!fLyWhtC}AOXw^;iEkH9z z);TmkLAC(R99btIzZc1Xtc2bK_tbxO(a5jG0?_P1Ij}B3!{+qYX@p9o?w)?Xg`ke` z;pNfYa`UO771x@pH4f;rp9ILZYzK7jrvUPUpBXgpWATfFnLA_$v8y%1$b7WP*ioM! zipT|^5{;IZOh3wix`k2zDhdC=x=nuu)S*B!d7XgT7~#@-+Q)qvn~zcxP7JVfXm+Me z7zxB+qIJziof!G)={9eQWh3_U* zpJlV{z^J-yTQ$4k~DvBx;@VfQU^vuS0db}%9Xwkt2L#)Km6FZ zQ%P|xc>T&(SgOF)Bp(DurRlHyETW^Dr7i9zQ`gXzOo{Md7W8#yKKsf}9{@T4Ew7u# zMiAYb$#G1~nEnhvMH=}M3Ez8pLg0jGfy<>qcF<_JWMahJ7iJx0YScO~s{A$!XIy** zXE|nONF9JeVHcp$PXpveUle1r7uzL&-@>|e!|t}Wnp%NTX&0${(Y67F`aTIJ&sq_X zdqlaaZl?Na?#U!d(_iFl=OX|qQjc@2#5O4ph{!Cc%3ag%Ha7Vfu?vmg75z>!N&%=O z*eW;G8z6H*G;g?i4cCMgv<{3aKldA;lrs&}-x&t>HVnh61EYc8_$BwL4={HCDy_B3 zjlS-e>5i};7HlOYq78@;TMOGbRMSw+ys1Gv?;)#(0qatTJv6Y)0g9v+)6dMvoUloO0Cynfs9o{Q2 z&^dYdEo;lfsG59KqS4TYmH-iyaoQ)#4PiJ)-zlE#BDTAq1t5|_;aWQl;A3dy7rp_A za4Y(mE)myi1CYu7A}%eAO4C1R5&L{+>Pjt5nEmde(H%351xB%UXX4ZWsPce; z0OaK&XTnLMwluWL#Yh4)bG}+MD)P~)o!2-OQ|yA!GuE!_IhKe3k%8_l z`CouFYyP_c#q^V$=F)2A_U*(rLv9D4($IoNAE&UTp_Kp)?wCBv6%+g{Qog6jG%D@1 zgJn}jJwiN;e)zMgXlP&eZ7P0PvqKE6Bx;vkJuU*m?B0fxhE@VJ^veSSBr>~tbv@(A z)-h|bL3Ref)XS{{(9rk(%Og`GEQ4!Jk{UvM3P2?>GVn8P06N*3>3L?Lx&XPmhnyBt zQIrp3R3x@vigPg=n{jY%Bkp`eH)ut1W*+vux z_z}kXE@Hc_HUav8u|5D5*P36ivfX>7o&tq~hdw}~H8<6Jz=Cdjr)%UiV0X}H%|jUM z<}q$ou1-<^@nk^t=W>GEDHV$wnGEOvwCn~=12p?OERC)M&|ukTO}RBjrxemUvnXy3 z>leq0TWHJN094wJmX}OI#-*wUC(pRZt{JifDDEH)klnG`U44VTX;k(*FejdIqEp8pTDO1FG#BV*(jTo3S5(ED3G^Dy{yw7BfIH(_I{sSN6w5i&cJ6 zXMj}7MSf>O1al8v`gzN*kP%Ec$9LEO0#Hd*hE|I4|9tkMoy4fPFZr32;8#%bYVbJMP~!9LBv%Sr z2Sx+GW{=Y`Qc~B^5lNL1$samsGzj%}G7iQ(wBXgWZXH$tDh+LY+01`GOi?NdCYk@l zn%g^v)wYdk&Xco{W99qX!#nZUBjxAWCw&?+;(NSKZpZh}FcyAyJbL&2aAi1%?<@x6 z`{A9H_4wkk^luY*i++JU5e+@mqU%zu_HKE&x$p0QQ#`1D{zrFH!c4ujAC1AA|-1h@w9q+6` z#!d3aWKxQ*j}`Qs*C%=MiDN)6Zd6y72mRZ6e`D^GzmJ~?9 zs-t5v&2Ww+Z@+w{$d}Rs#tmf3q6#ZSDGW>2p=>tCr{A4XPR^R@Z_UFc01qq^EX$=L zSrCUgr6S&^?orOj0bc(li>v?A8~~_gjGjJxM#84iNEvDDot}pHMT$-^pf@UIAyhsh zG6!9$sa-#k_38Y0k8CZpygnBu^WRccUBwS?R`XxYliNpmc^IQ(&l)1sc3%(o=gC@P zRpKakbum)ep82Es+>NXfBve$+7CTnj9&j@W0V~Fb)$y%TNKvkm(p6S`NKtIy zNU9&#CMoI%?3`D`I_A#@7OT2Z^;}#irB&m{pQh|pk3HL&4bl;R%&xxp_agBAb1l!m zIkrBb{Ve~}-|LH`lb@?|3Hh1we=t~DUX%P^U0qt{XMKSDUtV8c`bGZ#T=egP^XaFb zk`BT02Mjgm6rSnlY($n>OsR#C<)lsW)y!*ys{iXg8PVT!NF%um2@6cSx%^b$&&>fc zXHu(r4{mS+Ivn)*Z>4&OM6O;$GB#>&k+sHj#G6P`Ni~|9u;y-3MMWk(MwrsVBk>QePHw6z2vU9hX#1;YFSo1vPrs?YzQ46~|LOKORHgC5 zF~#$|!ktpjS%pQ;?W7zdFF2(jfbe8<>%mtvx&Qg2$B(wZ(U$S>(KdAfUQ`r&BU{z; z`&-+O9=v>ff2(@_a_jlC7n^Jdsdz5{ItZ+-&$6FI4JjY?H`@O>xS#BMo$Gsa{X%bKN^BGlb8&gNN zO7tg4t0ak}AHBFd17AzE`;}7tkpuXQx{yavXrrP^G3(0e>Y;`@^}xt~dIac@%y41v znxeM(+gu^=NB|SXd$=okUz1QWw6{C&t06f$zvcflg6PQ1&C=9P$LFjj>TET-(>pz7 z(y%AIwSVF7OugiTL8@B4uHLQBs- z0EO8aDf9stA~1WwV`{Sy8kx$YDtD~~lPV{&_HWtvLMmQT9>_9_`n>O(lw)erHa0q4 ze0Tijm|VxlJC^EJf8A=#$Dh++V5z7_Iv$vuY|Qr5GU{t3q&&b54hR%v)xUVH zZvI})xi!$**H5AO5<(vc?{n)ssoDoO;e=tzT>P zXLGM-Q~H))ZB46ODP339s|#yT^9$Pyix#gsNHisdQyUY%Um+1u9!n*LzIgQF`Q!WF zn7r7qFjJ|gs`%MUAdoA@BcZuSy5^qwfssuDm9Rj*-`5wv%aFt$5;U(q~Ny#t+YV?D6O^`eAHyDxFBkzYOw2&@BXkahR* zdel#<`);yn(nqYn>fjU(bT;l$zxz-n)EC}ytxj4n;`;l!q?}=1EjZ>GDQi2$zY=f7 zGegrOrRrS{s!O!eDVA-%)zlhv)vLn*I=fzpQYza6ZO!JGX!-XWPtY6g+wsAC`EBy! z0G@{>-J?+-GV+cae9mA`pKWhaN$CI`FWXYRZsNb- z$BdeT&=`H(!4O(Sp?zPnPo$4&cR|C%irZ@9Rd}J=7b-GvhQ{NF?Ag{2IN?G84v%O=>bdIub4N&BanGa5Aih7g&1?X)I>1?LP#{k4k!R>g(| zIVvAPj>WB$D@Sss>)_ae*clTE>~Z=maACZ1uc;n;yb!@>?*c4J) zLWH=SD#(>j&F=z``pu^3PG0ItlS=<8VNeuVSEn{Jy}yn)EF(wG;4(HG9V?>4n!|La zsOF#Ff3)?&OITa+1WvPN`Kh(+}e%shiHx(>aYyvo} zU%$0zkzbAHS!Wmu0)ZMv@BE5GacQe0}4%5vQ>eTe@)6*}QT~9fjQlcyLWHKf*e9wE;S0tjRj_9U#Cr~5*<1^hT zGS^A1a1}}Y=y+!NwnRjm6`}-mP}^CY+thl}KB8&!;*|e_t<EjRYP0m?mC*1^3?;B|(fvyw8#j$!M8ryg zTWJ&RU_L*oRx>Q99f>W9+9ZjZb4oo91Cf+qny^)M|3D^lksBrHTw6DZ^vqg+bB@c^ zkLgK`sbjofy_#1w%UAQ@{mS$%&5tyXlk@^1K-wTN+X%hDK0xD((P!mXO`Q=Uw#P&pdf3EujB7u6wW&HZXtV^4YA zZjdz6QfJnqUU#YZI`F$`2;NLn*;r~V>8Ul10~Uwm*eVRg-c9TACZZa%qtXb-Z-dc65NGs)V2PN0y94eP#J^$r@zcF?@J zBrB*K&6k>r_YTY-&qn*N=1~^?l4W~qa}(nhc5rPUwoK}Mr9N3{2_@ZK<}qb@Rup_AmMeX| z#&@=uP36I>_U+g?FLa)y!i&ZY)rZrX-gVu^CLk7zxUJn(@1>^p)YKB2WqV|vDy5k*4LK9=c?=~MPy8DYkzcbpyWt)hiod0eKbOvYQh| zvHL5uY$_+Hk!))gKiTjQt>$07dcDKH`DqPr`fVfN3!PdCy$NX5;T5)KjxwwP;m5vf z{-<_r9D6Z~hZCJ!H$YD3UR@Nn@M9LO`T~Xnc-xBJnDvF0vPRTb0_9YF|A=+i|G+dg3U{RWsLGEHUxzs zo!-xHIo1*l1;q2)4cudx^qzXG`!*zNf#Jgx5W~!$TF*YP7E_368)-KBk!M4!M*At_ zP2<&g(>d0&bI{6t<`gC2lW!+1)hWbb4NY!}Bc}mYy#nt|zh?bz;)Yixt4`gu#Dt$; zbxOpGEeUkp0-&^f7dO8`({3~y;A;xxrJpCO^&y{EvH8^Ps|6->pPdP<^~8ed>xpSK zZ~RfWt_AiRtSu@_ZalOyR)1Q zpk%*jv~DuORzR}H^1tDLx^sbF2?|!`5%bD;Jq=;O_5mpyDCKPFr#c)>Hm)Jxl_wULXjaSosyuhD(s=A6Pm4sigvxDIQOr4q2%N@!Neq;bgQ*-{Pt=KnT#dnbuzBbp^#esRk}vc!Y1`hVnWy9yC7P(<#%rLf_uO$^V?`mu>0^#&}ZhVEI3DA<84Lle`p8cAh;HLT4 zbjJnXcutDES(C${_A}48{6-UZI8J>-BEG9}%LcvquXU9LlFBxNYnw3!j`Dpu7TVRq zw0=%@8e55w0Jom&vYOL9|J%?uncET=^(dOZp4oIEUotH=-SYtC2Zw~%M7A4LmGfym zY-TT#3$a6}VOWnWSzQ!{ND@=>J-#CDPpO{S*3KJ(Ayf~(OtUgdg(~dLKcZyOfLMV8 zCd%cQFVsJw$VtDtt5UaT^O6w zwO`dp%7)yu;^tgRihMKim3dHV$q&M!f7?&dCm_0DT{Ml0K#j=4&j7 z0BwxpPj|jMKIE!c$DmWkZ7wy!Aj8*KCZ6=q7mQ-|`4sY+hR$DGo7)SbX~Y9HNNKkf z36$yW3fq$Rzlp(&%j+XzTm{4)gj7&u{ z!m%1fRna1#xX5d0?~@;AE>i6;Ccb8Y_|R@*yY6In>F?g#@u4Ft7Kk%Wdf5CuV>jwV zdltqfh_NW*=HPeT)&4b^8)KGy=IV)Bm^lEfA3BQ$Biio%pntT$9Gcs?gD{5d#3YN3 z6xBeSw{$C)5!mV^u)=MlNs{y#GNMbF8)qd+PmvHasocV?NH@4;zkA1dw1)?!We4oYk{DS)^&UZQ`rK4v!JR#=UGE9SEUrG=ANBHPfJ)9o? zX=QauWq32GaK~!^Y5viC$u}ptVsZ$W(Emd@>g!WBV-x&*I8P|okGvF0BJ7*YR>LA2%D0Ir5ichRxNN@8qTCN>Wy$OiE&Nz zMqk^F6O<_e+EP(A+E7A{#X-%luI67|;NIgJktgv@l$c!EzkPL)zM@BL9CD#Gq%oF} z$_@9M2oJ+f4u?6NZ;cu>iZok`RPdd>vyBM+EdMYRb*N6PFx#c_vo?Z zi?EHamr$xB=!|dNizE@g) zlSAEREsB8~15ME`OQu(^-jOR*T!9jr79dqK^v3}lE*Ak(6Z40{^IO$2JJ8hL{l_iR@FZ%WdRl!C^g75PzBCn51FW(V<6N1@u6nY`6#!JLZC$e4HQ!> zQ9{sXW$mU-&XN#%p-Y?V40oT)$o^nzqHB<#LR`|$wfoMJV%Cl?a&Z+aU?;PyW{+;< zO=m)x@EXr_M4^)cAf?>Eplk~Xcw*|DBpj#b6J)TWS6?@md>@=SZUk$o9_fdua+=7= zZ_BV{V?Z*$++rUo^=7ih!BGS94^y^HER7z*pTnaCx_L>4x;`u)}h$A;uin%Y|% z_Id88S_$fsfAkFB*AKKd9!T!6BAC{TEj)9wESQ+Y$%+#uNS73s7=R)QXU?qUW>Vt~ zdAaAi5`RO1;=7n)h~!p!j;D6Hmxos!U`0Po$A%mL^P zW==E&PS0|lLbugGY%y-*d))BcGjm$OJ65@@N%|n??0atGF|acRtEI(WsF$Tp`rjtD z3R<4GO|Ka>ULc4ug+AZhBL4!Tee~r1mz$DN|8oEF%S{Bc4%gyoS^cEcxcN7Sd3j0z zrhMaoB-nC^vQGHItWg%5?C1nrz;Kp@K&~eCs_!Z$HrTEd7wvAF{xA=!e$8HG{27A0 zRrWKu=8JyMY>@1w)TNlR00vW?++Se{^SznCz|D%@G=_v)UVU}{!S6SU)1qa%dqM80 zI1Qk~vGw(-%Ay_Aue&%Ycq|S6OMfZ9o!TR3qJBw;g2q*>HFi;7cT*@zSwlKMH_#M})=WH@?I8L)@ zo6)rrX6EhGj}f$OCu1;TWR?@}FvT(^el{!NK^#ToPQK7?T`e4bN49)N#R)piKD-hZ zB<30>3=${qtrYITzUPyxZ+%K%_ih^J&CJ7)kZCWW1z*Ym^kXD_k}Yp9^ne) zxEd9uo6?&psm7#=Vo6@DBBy2frUV8glA~$EJ&s{aU-e8kw6#0iKknJ{S<5smU^T=e z_yLiJKM=fMCC4KrIGgcKy>{%d3QJ(;d(T+rZ*6dVwpQ+zVq?>PG?~)ZYUoCcA4iFw zL_>2+&C-sa3sSRQ^7PBZNaw(&b^sGMx-f#w25gM&WWK^i{;(snH_)oUs} z8j#d&kt7KDiM%YeHIA!=-u!$u|Av20`1d9Mw)yv*f1CXKSN?sI151+Lp_6g%-e)e| zjFg5sNJ^hbpmTT56q$Zi@77eyjF>P^04Bi&OT=xRzr{NkC{l~ur42j{a-U4;obYW+ zo1kK=-$bqYcgTznyycLci$4ooL{d2X_p(ol(lzO8=&etN-6RZwUVWSQUyKW4W1gc8 zmA*@j=6|&~I52ze6hs~^`J>5(8KH?b1yW*nb?P`pMJ-?K%6T%;JebK_m?X-29m*8P zhum-f=nB6#+;eyKm6%z@yb)-KmRb5oL42r@i;xzq6vW4DR4*R=>n34$c%LZ9PonbC z=7Y@hen?HK*DTyBTc(m66%56gC&rZ1ix=G-^Halg%kLLV*}<+u-dN7x9NgvfAj_BX z%7UIH1+=d?rAh_&Jy$t_c&I{c%B~}2JsupLp*_hyr=V1;a-d*CtZ0^GqE0owfHCDK9xc(60ZDiB@|Vpe==tquF`DmXlekG)QjJ^7Cl~wCZh@8U9(9n!>%kv_Sje zqdz<$r^7$XE)&;OX5hC(5W0F%hI`J)X>Ob1g`Mga4lJusX>4&w;kL##Ym{A0)`YARePc|8CwM1PbF_J1ndhUobAEnerN1wajIJo>q&2YAUhk5R;gRsZ)l&jorYyZp>aH zlAcdUPV*A1Yu7qhQ)BBRsrzr1<1`59ty>$HJYYk?3@LYBobFiFGWI-t^my~>{U=0x zHhtuj-E;HlcFRFdb_&u<8xS(2y6Y%H8ap*|&k|qyp5ZhRb2%yS-$0UbCQ^rfE^-|- zq3^tv6V({ScTTkZU1N$+V|YzR;Q5C_*}s~v!5MLbF%704`?-jN_Om0FVCrMtvL3j0GE^wceK1PQXH3+9UY zVLG#p8?wU?DTt1`Ra0tYen=ay=gRX4nL3IuOAR|dxJH@X*s@fp!ybdcwn}vSswhik zLv-XO*IiN`iJI*F7Fz{YGCpz(o8*q*=!b}UJJ!XFOg@$RJukMwws=>LFLMOmK1MqGG;-8BkYXs`v={j+4J)5D_#iBG>GU<~L zWqzHN;^v{BwT_dw{UcTzYQj8BaNd7=ddbm1t_8ZGsNb_Io>n<^@$Z!AD%2D%s(*#| znu?%Ky~&1OUp?yEXei~+uk__%BmGJjI^2|w$G9Z$g{ggZ$zxYTaQddk=aSC~mdb{! zAYn7YVp-$5rN>(!$Cx9Jy%i=sYz#)T|JYSwaiVf&t2@x$HWPm z0$gCFJtO2l0wu8uZsZ<%t#!$`zJgH17mY41?JYM)NcOpXdYYeZr2A9b1X_|>LIkK< znqk5Kjs^CI5TL*3J~5CB0pihF_zl*x$Cb%ho?IO))B_op@1DOoyw|@gZo47=L=j8lIzsILI`yZWMBK>~DKL_yY)q-MK?MIYJVvj9|-jZ+9z~&=~Oz_uf zCU}*63!Ijaojb>eM=+q0&S+ec{!~3#l$*sK$+O+<^fABZaNB}vf)OPe|F7si=>`_X8Uwu02(JiO0=e;Sf|t|eVMPVdCl z1n4wt>J+CxPvO0gByQ`e-9{?mRb14k$v1rQud%QjTO%wBi=Qj@LO(i{HROA+L#p*& zumCmR4)JTdj&`1co??0NlnSMwrnrLCa8R4RKbd+(J3#ZN_uu5D93E#})9WVPJgDUL zBb%iMXQwPH#gS@4 zZav%j;?dLlk7eQ#6G|#T8HMGw{Ja`$%TX=bpckmk#GKa(1WJpQQ?j>2G7s^YOD=f> zSkhw*@XXT;_y!%*mu4@#0`4j4j``5^ruok%q%oH^0bCA$TQ}<0A3v_WVBpwolc#*g z!6`Ajc3RQ8O8*je-oVyRdc^Ot?7~+f-*fV8b8BeiEm~JGqw)xAD;SIZNtB;+mVdG3w*HMv? z5(u_AT)tC2zeDqIriPzEvKu$8yD3yKAaC=1*4qO!w@A*VY+0DzXsxVz05=$}uQFO< z51V7~A%OWdZ(0Z--agU7D3EB_%-1N&ZA^`or1ECUeoI~^olzY3w=$T%w(L6#PA18r z-BF8@35!YcX?5x94plD%Q5fJHI6(uc8wE?w*>uS5m6v5;N!`++PY~Aks|(GtYQw0p zI>-|?=a}0dM0^Jqyah;)sCU}N=y1@djPcmb`ncfA8oZZ|OAT-|QWT?Q6V$A$TwJ=7 zG#RFaKz_A?v`~nn%A6BV`Pst3&K~|0G_F)Zk8NI1El^aa2${Z#smH|~uR_Or>`ayz zj%sdfl4uw#o89RfsQ9;l}Jakf51 z<+=r8W%M4ueB5KAIdO~)e$VrOCgCw%zM!5y+wMPjxg~3%PizF!3Z1+T49mE{(an=R z#Bz8^3NMolamz9qpDu88l}9>j-kfr6u@MoOd8x7T-ILZB0jwLVoD4GQTdfRMmF30G ze!5~TsW~LzqrbO!VtHqFbrY}F-{!5E77}8NEYKE=xNg{)vx)KDAx{s7<~_<&B@b&u zNq?lHT~N3cFw1}HM6=FTsShlB{Kj^WGxM3q<@ zs2e{uQFe!717ojE$u?<__0Y4rAWt2`xPNYpFXopb5fOs6i5V_*4>m_RP08aCKj zsM>p|DJcp=6YgddGhuBIEk=e1Ny<0kX{}`}Ev!!8)Oo$oG4Kp0+vQkr$y%Tr1Y7RN z6C$(Na$zxUaY;}A5g31j4{T;ruQdrz=8(pGZScvHCAgD^Q{a_B=Sy}o~l zO(rSpZ>J4G>|pPfUjv?P8N34PXvfgaIyVUkUxjb8E5R>{Ilzh#?jcXJib(xt{i!h=$GEcxZ|AgB!Lk}o`%|yRgqJ(sOQ+J1 z7cLz+04h-4s?TJ?emq5mN*)$$r&!7YuJG+9vuMUu?4nu=9`|ULWBWTzp27m9YRuxY z+}jW13Dba_QGBMR2{l2;$rt>NN@)5@n4FM}`eb3kX6m{4n155>+j1(+f}c4PVT`hl zR71E!SuEbRG^K7hZPin3fxd@j`{3woH#~^(yv%YOATEuh)+vcl+M!b4_*qW~E0a8C zd(V;zD0G~=GULZj<~7?hShD$Ol%^$Zdv*aJsh^lOv0u;YKxC=paVz<))-+I_NlBz? zio_CQ>refe75kws8%&bx_=Q5Wf2;#|EektEo3k!3e;Dhg0U1uVH%WxGP;+#3$}EVj z246c3>LJB2Czo)WHnn`2>eI0K$#d)jOR}DM_~6_oymc0TLA5SS^PIj+ykJ4Js6Y5W zZ(8zV%1s)*{j{kNpyWvoq=_DQRuk?c>E0Bbrhp`kDUM`Q+kmlc2!;tuTk(jcERS}^ zf^O}=htAi3HX{a#o#)02tzSaQ4H;(~;Urc78@e zCZ;jpB>NhA5tooIE+ysy4bHrARPp^Nj!P~~T?&Ud^#;GF?2{to7nR-ay{BY=?Hd1| zs_bd8|DCJt$5i+#%v-%*G&Ih?joPmIuSoI^G}1PcH^#6Nl(fc=J`LIaRqs*4po0#X z3jfhs{s-8o1LdUa<%41C5TJRV9&lsRE=|b~I0Vpq61NS$vwD-Zr%ZLeG4mv#c1ahX zc@A(DK8^sbUCR#vpP~=}C3fK0W3#%x`yY2Q_!xzeB@v8VIM;`rp;M!`@s%{m5@bJY zia5#cR8Ni$M{?K1DML;_GGRutLLCHB;sI5Cz-XeY0p2AE3l5_CoLK-cVYWpD2?vj4 zGPSF5o!BreUXRGKkc3aRL^V!8`C)oVfk(1Lf6O_4x^X}$G!F7M_^#EuY{XC6R%X{J z(l&tD9OYH`H$AXrzW)h_wglrxZD{{b4{q9Fba<<|+JwLCoFB6oKWtxX><*3j%^b>y z+)=X9=Jnl1$~2~*dZznHGi9U+W7*%sxo*+{RiT{+e#uNy^5iw+)o>J);voqq0k~ztCWGG;No{RL@ zk$WYF!0S1@g2e=Zi6531nzT-wBSOkYV5$uXpHeH)sJ1O;#y@v_AIJup6*&rAROmiH!ZHY0+p@ zpo*>9M{%Raa4T0?+)yYpNE1+7KITmAZ6Iw`AsJiA&0; zm9UdklX4!ymi8Ae`WbDEqX7rz+F>9G-Rdv=O{k~1HF(8|lZf>ftMqMiB_*o?$#^3s z+e($|@l>3F^1xo7p10O!^1AlQJ#BwA0@2VENLc8}^ORo`AEO2NCr>+>G+wk-3$f#} z+X?)-@j{&_-7ZJZiTa4E6J1zX9zrf{mGT|Km}ZSj9xE5E{HmZp{D>}E(%FLwc;zt(^-(6qWp@Qx4WUgKurV-G@%7h3n<>6Sa? zi&o7uDNC=fM`KmYBh$?J!kpxi1Q{8G$vnche967~Q*sEvfToP`N{;e=ms`0+@ z-4+bX>(Qc0^`eETT)2>q=*g^BT+|>A1gSZ0cUOlGOUkCmf;WmW(6|~{0NNR`?jieE zo0}T*&YnCzCYU!N%`E@-m(E-0m*RE9eoO&n0skDW*R0%wGmcvP{2#9 z$mCP#h^m`UUwpkusb8813+vWAn6l^!$>aq$CDgD&qRVb*JA3@>>6d*6QN8%;+17UY z<16AF`cX;r2Kf`YWaa(l>*}y3V$3t|w7>*biI$wkaniMm$mY71<9)M38%&C(m~^GU zk4b}o6yhiE7dlLKtJQK!75njc=i*o_A3~)$#hwL}WpB45a(~)UuD#klOftpOM?D-M z5*lqSI~AAK_&{+D{heu=i>nxj+%d>bKXF zW#nR8lj9?^>3Y*9fuxb=ll_jU{dH{*l)bdm@5}_rx1rloNMo z$1*>l#LeIkdd#bNrDI8| zLY6I$01xrubL#b~VP1=&5$D@l(+FJd!%1ui(XXWPK9QqmhyC5N_tq)pP>Lw-_{93ejH z1gN`g-r{a%7C=siRNfngKRd?5E1CUWoQE(u+}l2>@Q46OGv3e`h~hcO#OOg+3zelV5%s6bLWo;T=NxR~A=q#oxrYK()quKjkBtVwTocRzBg^V10EMznAsf ze%6=QJ{hd6Ee(gmmEr38Crg8s;o8b4)zVMt^kdti)Wf#bCyIpXYV)DzU2Xr4pRrE9 z9Gxl^GNLO2gzyZN>!Q_7@n|vU3az86|LZ=YiEMeqt>|mQrZy^4=l;5{piyikxI1|y zUN!H{$gYssi;te$?y*8KAD9PyO4<1Mc=v)_!SkdK>akJnjcK;BxI(ZMhna8rV{y35 zE>n@!`mG!q=Wp+?jBf8NudS`!9?ug^Se;-BPAPRHzRs9Eb3<>b5i2x3v(L8`abrt?o`Q(0ezl_VTRmme=Mzfqs(L=0*sf=E|(@ zb}vf<@h_Mkz;~lct3_~MZav^P4z> zAZ3hP=BkVOb9k}yRtF|4b3G0Ue`>RrWN6lx?awxmWz_gtD z2!M8W0+}AB4}vGC9AF!eEIAy&*q<&^MgZrP$-&oSw4=0eIl@htF#(VfS|9@90I2*2 zS0^_$ZUxJK{b>8EXD_#_`%k}7e&MbAPq)AMG#`+3#xtr&)jwvS@U9ATQ?~p-5T0yq zQ7r1|_WjQvJ$^*0Uda0J(e~5L7cZ)Z&$g=jN~S`wgO`u*Z&lA za}4wYT7-7^u)iU$B#Z_Y`$53-h_eWG2o&CP;br*FhO1sVB*iI^mtmaXAvb?cGU5z9F z7$L(VKtdP@Y@C+vL|Z_O7y?rdNSh`4j?Q?HTa=gAxl08R-rBqWxlhC7NF?~+Q)PBz z!rJ*3pt(#5#;)9Z=OP$j5HIo}jTXXdhE;Z70)` zEJmu}y?23=r?3MlVr@B@DT(F?b67nPy49`i1T=d8e8k(dG>GSIu{ohT z@kJ2TW0I6&Sr4&&>c~&Qh=gA{b~k1|&3PucXpsglC_3eV)eBYm*ymoG9+@xLhTH^} z3tNIRQXo8lS_(L`nnyB*p2GbCf}qF)Wv&<+UU+VILV5uAkll=4o{L%@xks_N#?S<$ z5pk8mv+&0wWK0$eQQ>M#Dv`8ggCwyiG%&$PMmz#@5 z-C8uZIG6+%`s|*RzUH;kN0u|Cr*eUPUE8G-i)<$d3*-CfvX7Je^{#R?`L#e4IE&l9 zV7ipOI<0v-*o4&#kg099MC7_q3#Df=UvTkd3V6GB%o=uy`otHeW)uq_oD3_rDU=tA z+3~T4Ob#o*p!;kYd{Sn-0J) zuqo-4lC#EPz28>>EU}8XQ`WZE|Fqz#+f(r>2XG_$igyc~X9$VwqACQj)E1mD|6n$z zQlNZQicjnk#-FDHICi zOMX*1z+0L{gW|4Fw3bMo(WI_~)mD545)J4nX#A%36J05s?V449=>20VC&mmW2B|Xk zQgxk4NBeWlSB;YSHE*CLNEksH$PKuZI}8xf29|G}*tgmY()$1ynXjD=a*@zR!VtuS zs3#~KzBt{{3OY6^pT;wnZz}N&LpoSdEo1tOY^FL$v*(A7&a$6-VA3!thDdPMV6K*+ zIprXu6`VSg^?zz@q=&XX6t9T=zcrPO=8e=B{>l$n7*W!oV_i^4#JIj>r*0alb@oQO X|6-y387vyVfcS+1zfj;~De!**^+m2n diff --git a/localelpa/csv-mode-1.10.el b/localelpa/csv-mode-1.10.el deleted file mode 100644 index f837f6adf7..0000000000 --- a/localelpa/csv-mode-1.10.el +++ /dev/null @@ -1,1949 +0,0 @@ -;;; csv-mode.el --- Major mode for editing comma/char separated values -*- lexical-binding: t -*- - -;; Copyright (C) 2003, 2004, 2012-2019 Free Software Foundation, Inc - -;; Author: "Francis J. Wright" -;; Maintainer: emacs-devel@gnu.org -;; Version: 1.10 -;; Package-Requires: ((emacs "24.1") (cl-lib "0.5")) -;; Keywords: convenience - -;; This package is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 3, or (at your option) -;; any later version. - -;; This package is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see . - -;;; Commentary: - -;; This package implements CSV mode, a major mode for editing records -;; in a generalized CSV (character-separated values) format. It binds -;; files with prefix ".csv" to `csv-mode' (and ".tsv" to `tsv-mode') in -;; `auto-mode-alist'. - -;; In CSV mode, the following commands are available: - -;; - C-c C-s (`csv-sort-fields') and C-c C-n (`csv-sort-numeric-fields') -;; respectively sort lexicographically and numerically on a -;; specified field or column. - -;; - C-c C-r (`csv-reverse-region') reverses the order. (These -;; commands are based closely on, and use, code in `sort.el'.) - -;; - C-c C-k (`csv-kill-fields') and C-c C-y (`csv-yank-fields') kill -;; and yank fields or columns, although they do not use the normal -;; kill ring. C-c C-k can kill more than one field at once, but -;; multiple killed fields can be yanked only as a fixed group -;; equivalent to a single field. - -;; - `csv-align-mode' keeps fields visually aligned, on-the-fly. -;; It truncates fields to a maximum width that can be changed per-column -;; with `csv-align-set-column-width'. -;; Alternatively, C-c C-a (`csv-align-fields') aligns fields into columns -;; and C-c C-u (`csv-unalign-fields') undoes such alignment; -;; separators can be hidden within aligned records (controlled by -;; `csv-invisibility-default' and `csv-toggle-invisibility'). - -;; - C-c C-t (`csv-transpose') interchanges rows and columns. For -;; details, see the documentation for the individual commands. - -;; CSV mode can recognize fields separated by any of several single -;; characters, specified by the value of the customizable user option -;; `csv-separators'. CSV data fields can be delimited by quote -;; characters (and must if they contain separator characters). This -;; implementation supports quoted fields, where the quote characters -;; allowed are specified by the value of the customizable user option -;; `csv-field-quotes'. By default, the both commas and tabs are considered -;; as separators and the only field quote is a double quote. -;; These user options can be changed ONLY by customizing them, e.g. via M-x -;; customize-variable. - -;; CSV mode commands ignore blank lines and comment lines beginning -;; with the value of the buffer local variable `csv-comment-start', -;; which by default is #. The user interface is similar to that of -;; the standard commands `sort-fields' and `sort-numeric-fields', but -;; see the major mode documentation below. - -;; The global minor mode `csv-field-index-mode' provides display of -;; the current field index in the mode line, cf. `line-number-mode' -;; and `column-number-mode'. It is on by default. - -;;; Installation: - -;; Put this file somewhere that Emacs can find it (i.e. in one of the -;; directories in your `load-path' such as `site-lisp'), optionally -;; byte-compile it (recommended), and put this in your .emacs file: -;; -;; (add-to-list 'auto-mode-alist '("\\.[Cc][Ss][Vv]\\'" . csv-mode)) -;; (autoload 'csv-mode "csv-mode" -;; "Major mode for editing comma-separated value files." t) - -;;; History: - -;; Begun on 15 November 2003 to provide lexicographic sorting of -;; simple CSV data by field and released as csv.el. Facilities to -;; kill multiple fields and customize separator added on 9 April 2004. -;; Converted to a major mode and renamed csv-mode.el on 10 April 2004, -;; partly at the suggestion of Stefan Monnier to avoid conflict with csv.el by Ulf Jasper. -;; Field alignment, comment support and CSV mode customization group -;; added on 1 May 2004. Support for index ranges added on 6 June -;; 2004. Multiple field separators added on 12 June 2004. -;; Transposition added on 22 June 2004. Separator invisibility added -;; on 23 June 2004. - -;;; See also: - -;; the standard GNU Emacs 21 packages align.el, which will align -;; columns within a region, and delim-col.el, which helps to prettify -;; columns in a text region or rectangle; - -;; csv.el by Ulf Jasper , which provides -;; functions for reading/parsing comma-separated value files and is -;; available at http://de.geocities.com/ulf_jasper/emacs.html (and in -;; the gnu.emacs.sources archives). - -;;; To do (maybe): - -;; Make separators and quotes buffer-local and locally settable. -;; Support (La)TeX tables: set separator and comment; support record -;; end string. -;; Convert comma-separated to space- or tab-separated. - -;;; Code: - -(eval-when-compile (require 'cl-lib)) - -(defgroup CSV nil - "Major mode for editing files of comma-separated value type." - :group 'convenience) - -(defvar csv-separator-chars nil - "Field separators as a list of character. -Set by customizing `csv-separators' -- do not set directly!") - -(defvar csv-separator-regexp nil - "Regexp to match a field separator. -Set by customizing `csv-separators' -- do not set directly!") - -(defvar csv--skip-chars nil - "Char set used by `skip-chars-forward' etc. to skip fields. -Set by customizing `csv-separators' -- do not set directly!") - -(defvar csv-font-lock-keywords nil - "Font lock keywords to highlight the field separators in CSV mode. -Set by customizing `csv-separators' -- do not set directly!") - -(defcustom csv-separators '("," "\t") - "Field separators: a list of *single-character* strings. -For example: (\",\"), the default, or (\",\" \";\" \":\"). -Neighbouring fields may be separated by any one of these characters. -The first is used when inserting a field separator into the buffer. -All must be different from the field quote characters, `csv-field-quotes'." - ;; Suggested by Eckhard Neber - :type '(repeat string) - ;; FIXME: Character would be better, but in Emacs 21.3 does not display - ;; correctly in a customization buffer. - :set (lambda (variable value) - (mapc (lambda (x) - (if (/= (length x) 1) - (error "Non-single-char string %S" x)) - (if (and (boundp 'csv-field-quotes) - (member x csv-field-quotes)) - (error "%S is already a quote" x))) - value) - (custom-set-default variable value) - (setq csv-separator-chars (mapcar #'string-to-char value) - csv--skip-chars (apply #'concat "^\n" csv-separators) - csv-separator-regexp (apply #'concat `("[" ,@value "]")) - csv-font-lock-keywords - ;; NB: csv-separator-face variable evaluates to itself. - `((,csv-separator-regexp (0 'csv-separator-face)))))) - -(defcustom csv-field-quotes '("\"") - "Field quotes: a list of *single-character* strings. -For example: (\"\\\"\"), the default, or (\"\\\"\" \"\\='\" \"\\=`\"). -A field can be delimited by a pair of any of these characters. -All must be different from the field separators, `csv-separators'." - :type '(repeat string) - ;; Character would be better, but in Emacs 21 does not display - ;; correctly in a customization buffer. - :set (lambda (variable value) - (mapc (lambda (x) - (if (/= (length x) 1) - (error "Non-single-char string %S" x)) - (if (member x csv-separators) - (error "%S is already a separator" x))) - value) - (when (boundp 'csv-mode-syntax-table) - ;; FIRST remove old quote syntax: - (with-syntax-table text-mode-syntax-table - (mapc (lambda (x) - (modify-syntax-entry - (string-to-char x) - (string (char-syntax (string-to-char x))) - ;; symbol-value to avoid compiler warning: - (symbol-value 'csv-mode-syntax-table))) - csv-field-quotes)) - ;; THEN set new quote syntax: - (csv-set-quote-syntax value)) - ;; BEFORE setting new value of `csv-field-quotes': - (custom-set-default variable value))) - -(defun csv-set-quote-syntax (field-quotes) - "Set syntax for field quote characters FIELD-QUOTES to be \"string\". -FIELD-QUOTES should be a list of single-character strings." - (mapc (lambda (x) - (modify-syntax-entry - (string-to-char x) "\"" - ;; symbol-value to avoid compiler warning: - (symbol-value 'csv-mode-syntax-table))) - field-quotes)) - -(defvar csv-comment-start nil - "String that starts a comment line, or nil if no comment syntax. -Such comment lines are ignored by CSV mode commands. -This variable is buffer local\; its default value is that of -`csv-comment-start-default'. It is set by the function -`csv-set-comment-start' -- do not set it directly!") - -(make-variable-buffer-local 'csv-comment-start) - -(defcustom csv-comment-start-default "#" - "String that starts a comment line, or nil if no comment syntax. -Such comment lines are ignored by CSV mode commands. -Default value of buffer-local variable `csv-comment-start'. -Changing this variable does not affect any existing CSV mode buffer." - :type '(choice (const :tag "None" nil) string) - :set (lambda (variable value) - (custom-set-default variable value) - (setq-default csv-comment-start value))) - -(defcustom csv-align-style 'left - "Aligned field style: one of `left', `centre', `right' or `auto'. -Alignment style used by `csv-align-mode' and `csv-align-fields'. -Auto-alignment means left align text and right align numbers." - :type '(choice (const left) (const centre) - (const right) (const auto))) - -(defcustom csv-align-padding 1 - "Aligned field spacing: must be a positive integer. -Number of spaces used by `csv-align-mode' and `csv-align-fields' after separators." - :type 'integer) - -(defcustom csv-header-lines 0 - "Header lines to skip when setting region automatically." - :type 'integer) - -(defcustom csv-invisibility-default t - "If non-nil, make separators in aligned records invisible." - :type 'boolean) - -(defface csv-separator-face - '((t :inherit escape-glyph)) - "CSV mode face used to highlight separators.") - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Mode definition, key bindings and menu -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -(defconst csv-mode-line-format - '(csv-field-index-string ("" csv-field-index-string)) - "Mode line format string for CSV mode.") - -(defvar csv-mode-map - (let ((map (make-sparse-keymap))) - (define-key map [(control ?c) (control ?v)] 'csv-toggle-invisibility) - (define-key map [(control ?c) (control ?t)] 'csv-transpose) - (define-key map [(control ?c) (control ?c)] 'csv-set-comment-start) - (define-key map [(control ?c) (control ?u)] 'csv-unalign-fields) - (define-key map [(control ?c) (control ?a)] 'csv-align-fields) - (define-key map [(control ?c) (control ?z)] 'csv-yank-as-new-table) - (define-key map [(control ?c) (control ?y)] 'csv-yank-fields) - (define-key map [(control ?c) (control ?k)] 'csv-kill-fields) - (define-key map [(control ?c) (control ?d)] 'csv-toggle-descending) - (define-key map [(control ?c) (control ?r)] 'csv-reverse-region) - (define-key map [(control ?c) (control ?n)] 'csv-sort-numeric-fields) - (define-key map [(control ?c) (control ?s)] 'csv-sort-fields) - map)) - -;;;###autoload -(define-derived-mode csv-mode text-mode "CSV" - "Major mode for editing files of comma-separated value type. - -CSV mode is derived from `text-mode', and runs `text-mode-hook' before -running `csv-mode-hook'. It turns `auto-fill-mode' off by default. -CSV mode can be customized by user options in the CSV customization -group. The separators are specified by the value of `csv-separators'. - -CSV mode commands ignore blank lines and comment lines beginning with -the value of `csv-comment-start', which delimit \"paragraphs\". -\"Sexp\" is re-interpreted to mean \"field\", so that `forward-sexp' -\(\\[forward-sexp]), `kill-sexp' (\\[kill-sexp]), etc. all apply to fields. -Standard comment commands apply, such as `comment-dwim' (\\[comment-dwim]). - -If `font-lock-mode' is enabled then separators, quoted values and -comment lines are highlighted using respectively `csv-separator-face', -`font-lock-string-face' and `font-lock-comment-face'. - -The user interface (UI) for CSV mode commands is similar to that of -the standard commands `sort-fields' and `sort-numeric-fields', except -that if there is no prefix argument then the UI prompts for the field -index or indices. In `transient-mark-mode' only: if the region is not -set then the UI attempts to set it to include all consecutive CSV -records around point, and prompts for confirmation; if there is no -prefix argument then the UI prompts for it, offering as a default the -index of the field containing point if the region was not set -explicitly. The region set automatically is delimited by blank lines -and comment lines, and the number of header lines at the beginning of -the region given by the value of `csv-header-lines' are skipped. - -Sort order is controlled by `csv-descending'. - -CSV mode provides the following specific keyboard key bindings: - -\\{csv-mode-map}" - :group 'CSV - ;; We used to `turn-off-auto-fill' here instead, but that's not very - ;; effective since text-mode-hook is run afterwards anyway! - (setq-local normal-auto-fill-function nil) - ;; Set syntax for field quotes: - (csv-set-quote-syntax csv-field-quotes) - ;; Make sexp functions apply to fields: - (set (make-local-variable 'forward-sexp-function) #'csv-forward-field) - (csv-set-comment-start csv-comment-start) - ;; Font locking -- separator plus syntactic: - (setq font-lock-defaults '(csv-font-lock-keywords)) - (setq-local jit-lock-contextually nil) ;Each line should be independent. - (if csv-invisibility-default (add-to-invisibility-spec 'csv)) - ;; Mode line to support `csv-field-index-mode': - (set (make-local-variable 'mode-line-position) - (pcase mode-line-position - (`(,(or (pred consp) (pred stringp)) . ,_) - `(,@mode-line-position ,csv-mode-line-format)) - (_ `("" ,mode-line-position ,csv-mode-line-format)))) - (set (make-local-variable 'truncate-lines) t) - ;; Enable or disable `csv-field-index-mode' (could probably do this - ;; a bit more efficiently): - (csv-field-index-mode (symbol-value 'csv-field-index-mode))) - -(defun csv-set-comment-start (string) - "Set comment start for this CSV mode buffer to STRING. -It must be either a string or nil." - (interactive - (list (edit-and-eval-command - "Comment start (string or nil): " csv-comment-start))) - ;; Paragraph means a group of contiguous records: - (set (make-local-variable 'paragraph-separate) "[:space:]*$") ; White space. - (set (make-local-variable 'paragraph-start) "\n");Must include \n explicitly! - ;; Remove old comment-start/end if available - (with-syntax-table text-mode-syntax-table - (when comment-start - (modify-syntax-entry (string-to-char comment-start) - (string (char-syntax (string-to-char comment-start))) - csv-mode-syntax-table)) - (modify-syntax-entry ?\n - (string (char-syntax ?\n)) - csv-mode-syntax-table)) - (when string - (setq paragraph-separate (concat paragraph-separate "\\|" string) - paragraph-start (concat paragraph-start "\\|" string)) - (set (make-local-variable 'comment-start) string) - (modify-syntax-entry - (string-to-char string) "<" csv-mode-syntax-table) - (modify-syntax-entry ?\n ">" csv-mode-syntax-table)) - (setq csv-comment-start string)) - -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.[Cc][Ss][Vv]\\'" . csv-mode)) - -(defvar csv-descending nil - "If non-nil, CSV mode sort functions sort in order of descending sort key. -Usually they sort in order of ascending sort key.") - -(defun csv-toggle-descending () - "Toggle `csv-descending'." - (interactive) - (setq csv-descending (not csv-descending)) - (message "Sort order is %sscending" (if csv-descending "de" "a"))) - -(defun csv-toggle-invisibility () - ;; FIXME: Make it into a proper minor mode? - "Toggle `buffer-invisibility-spec'." - (interactive) - (if (memq 'csv buffer-invisibility-spec) - (remove-from-invisibility-spec 'csv) - (add-to-invisibility-spec 'csv)) - (message "Separators in aligned records will be %svisible \ -\(after re-aligning if soft\)" - (if (memq 'csv buffer-invisibility-spec) "in" "")) - (redraw-frame (selected-frame))) - -(easy-menu-define - csv-menu - csv-mode-map - "CSV major mode menu keymap" - '("CSV" - ["Sort By Field Lexicographically" csv-sort-fields :active t - :help "Sort lines in region lexicographically by the specified field"] - ["Sort By Field Numerically" csv-sort-numeric-fields :active t - :help "Sort lines in region numerically by the specified field"] - ["Reverse Order of Lines" csv-reverse-region :active t - :help "Reverse the order of the lines in the region"] - ["Use Descending Sort Order" csv-toggle-descending :active t - :style toggle :selected csv-descending - :help "If selected, use descending order when sorting"] - "--" - ["Kill Fields (Columns)" csv-kill-fields :active t - :help "Kill specified fields of each line in the region"] - ["Yank Fields (Columns)" csv-yank-fields :active t - :help "Yank killed fields as specified field of each line in region"] - ["Yank As New Table" csv-yank-as-new-table :active t - :help "Yank killed fields as a new table at point"] - ["Align Fields into Columns" csv-align-fields :active t - :help "Align the start of every field of each line in the region"] - ["Unalign Columns into Fields" csv-unalign-fields :active t - :help "Undo soft alignment and optionally remove redundant white space"] - ["Transpose Rows and Columns" csv-transpose :active t - :help "Rewrite rows (which may have different lengths) as columns"] - "--" - ["Forward Field" forward-sexp :active t - :help "Move forward across one field; with ARG, do it that many times"] - ["Backward Field" backward-sexp :active t - :help "Move backward across one field; with ARG, do it that many times"] - ["Kill Field Forward" kill-sexp :active t - :help "Kill field following cursor; with ARG, do it that many times"] - ["Kill Field Backward" backward-kill-sexp :active t - :help "Kill field preceding cursor; with ARG, do it that many times"] - "--" - ("Alignment Style" - ["Left" (setq csv-align-style 'left) :active t - :style radio :selected (eq csv-align-style 'left) - :help "If selected, `csv-align' left aligns fields"] - ["Centre" (setq csv-align-style 'centre) :active t - :style radio :selected (eq csv-align-style 'centre) - :help "If selected, `csv-align' centres fields"] - ["Right" (setq csv-align-style 'right) :active t - :style radio :selected (eq csv-align-style 'right) - :help "If selected, `csv-align' right aligns fields"] - ["Auto" (setq csv-align-style 'auto) :active t - :style radio :selected (eq csv-align-style 'auto) - :help "\ -If selected, `csv-align' left aligns text and right aligns numbers"] - ) - ["Set header line" csv-header-line :active t] - ["Auto-(re)align fields" csv-align-mode - :style toggle :selected csv-align-mode] - ["Show Current Field Index" csv-field-index-mode :active t - :style toggle :selected csv-field-index-mode - :help "If selected, display current field index in mode line"] - ["Make Separators Invisible" csv-toggle-invisibility :active t - :style toggle :selected (memq 'csv buffer-invisibility-spec) - :visible (not (tsv--mode-p)) - :help "If selected, separators in aligned records are invisible"] - ["Set Buffer's Comment Start" csv-set-comment-start :active t - :help "Set comment start string for this buffer"] - ["Customize CSV Mode" (customize-group 'CSV) :active t - :help "Open a customization buffer to change CSV mode options"] - )) - -(require 'sort) - -(defsubst csv-not-looking-at-record () - "Return t if looking at blank or comment line, nil otherwise. -Assumes point is at beginning of line." - (looking-at paragraph-separate)) - -(defun csv-interactive-args (&optional type) - "Get arg or field(s) and region interactively, offering sensible defaults. -Signal an error if the buffer is read-only. -If TYPE is noarg then return a list (beg end). -Otherwise, return a list (arg beg end), where arg is: - the raw prefix argument by default\; - a single field index if TYPE is single\; - a list of field indices or index ranges if TYPE is multiple. -Field defaults to the current prefix arg\; if not set, prompt user. - -A field index list consists of positive or negative integers or ranges, -separated by any non-integer characters. A range has the form m-n, -where m and n are positive or negative integers, m < n, and n defaults -to the last field index if omitted. - -In transient mark mode, if the mark is not active then automatically -select and highlight CSV records around point, and query user. -The default field when read interactively is the current field." - ;; Must be run interactively to activate mark! - (let* ((arg current-prefix-arg) (default-field 1) - (region - (if (not (use-region-p)) - ;; Set region automatically: - (save-excursion - (if arg - (beginning-of-line) - (let ((lbp (line-beginning-position))) - (while (re-search-backward csv-separator-regexp lbp 1) - ;; Move as far as possible, i.e. to beginning of line. - (setq default-field (1+ default-field))))) - (if (csv-not-looking-at-record) - (error "Point must be within CSV records")) - (let ((startline (point))) - ;; Set mark at beginning of region: - (while (not (or (bobp) (csv-not-looking-at-record))) - (forward-line -1)) - (if (csv-not-looking-at-record) (forward-line 1)) - ;; Skip header lines: - (forward-line csv-header-lines) - (set-mark (point)) ; OK since in save-excursion - ;; Move point to end of region: - (goto-char startline) - (beginning-of-line) - (while (not (or (eobp) (csv-not-looking-at-record))) - (forward-line 1)) - ;; Show mark briefly if necessary: - (unless (and (pos-visible-in-window-p) - (pos-visible-in-window-p (mark))) - (exchange-point-and-mark) - (sit-for 1) - (exchange-point-and-mark)) - (or (y-or-n-p "Region OK? ") - (error "Action aborted by user")) - (message nil) ; clear y-or-n-p message - (list (region-beginning) (region-end)))) - ;; Use region set by user: - (list (region-beginning) (region-end))))) - (setq default-field (number-to-string default-field)) - (cond - ((eq type 'multiple) - (if arg - ;; Ensure that field is a list: - (or (consp arg) - (setq arg (list (prefix-numeric-value arg)))) - ;; Read field interactively, ignoring non-integers: - (setq arg - (mapcar - (lambda (x) - (if (string-match "-" x 1) ; not first character - ;; Return a range as a pair - the cdr may be nil: - (let ((m (substring x 0 (match-beginning 0))) - (n (substring x (match-end 0)))) - (cons (car (read-from-string m)) - (and (not (string= n "")) - (car (read-from-string n))))) - ;; Return a number as a number: - (car (read-from-string x)))) - (split-string - (read-string - "Fields (sequence of integers or ranges): " default-field) - "[^-+0-9]+"))))) - ((eq type 'single) - (if arg - (setq arg (prefix-numeric-value arg)) - (while (not (integerp arg)) - (setq arg (eval-minibuffer "Field (integer): " default-field)))))) - (if (eq type 'noarg) region (cons arg region)))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Sorting by field -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun csv-nextrecfun () - "Called by `csv-sort-fields-1' with point at end of previous record. -It moves point to the start of the next record. -It should move point to the end of the buffer if there are no more records." - (forward-line) - (while (and (not (eobp)) (csv-not-looking-at-record)) - (forward-line))) - -(defun csv-sort-fields-1 (field beg end startkeyfun endkeyfun) - "Modified version of `sort-fields-1' that skips blank or comment lines. - -FIELD is a single field index, and BEG and END specify the region to -sort. - -STARTKEYFUN moves from the start of the record to the start of the key. -It may return either a non-nil value to be used as the key, or -else the key is the substring between the values of point after -STARTKEYFUN and ENDKEYFUN are called. If STARTKEYFUN is nil, the key -starts at the beginning of the record. - -ENDKEYFUN moves from the start of the sort key to the end of the sort key. -ENDKEYFUN may be nil if STARTKEYFUN returns a value or if it would be the -same as ENDRECFUN." - (let ((tbl (syntax-table))) - (if (zerop field) (setq field 1)) - (unwind-protect - (save-excursion - (save-restriction - (narrow-to-region beg end) - (goto-char (point-min)) - (set-syntax-table sort-fields-syntax-table) - (sort-subr csv-descending - 'csv-nextrecfun 'end-of-line - startkeyfun endkeyfun))) - (set-syntax-table tbl)))) - -(defun csv-sort-fields (field beg end) - "Sort lines in region lexicographically by the ARGth field of each line. -If not set, the region defaults to the CSV records around point. -Fields are separated by `csv-separators' and null fields are allowed anywhere. -Field indices increase from 1 on the left or decrease from -1 on the right. -A prefix argument specifies a single field, otherwise prompt for field index. -Ignore blank and comment lines. The variable `sort-fold-case' -determines whether alphabetic case affects the sort order. -When called non-interactively, FIELD is a single field index\; -BEG and END specify the region to sort." - ;; (interactive "*P\nr") - (interactive (csv-interactive-args 'single)) - (barf-if-buffer-read-only) - (csv-sort-fields-1 field beg end - (lambda () (csv-sort-skip-fields field) nil) - (lambda () (skip-chars-forward csv--skip-chars)))) - -(defun csv-sort-numeric-fields (field beg end) - "Sort lines in region numerically by the ARGth field of each line. -If not set, the region defaults to the CSV records around point. -Fields are separated by `csv-separators'. -Null fields are allowed anywhere and sort as zeros. -Field indices increase from 1 on the left or decrease from -1 on the right. -A prefix argument specifies a single field, otherwise prompt for field index. -Specified non-null field must contain a number in each line of the region, -which may begin with \"0x\" or \"0\" for hexadecimal and octal values. -Otherwise, the number is interpreted according to sort-numeric-base. -Ignore blank and comment lines. -When called non-interactively, FIELD is a single field index\; -BEG and END specify the region to sort." - ;; (interactive "*P\nr") - (interactive (csv-interactive-args 'single)) - (barf-if-buffer-read-only) - (csv-sort-fields-1 field beg end - (lambda () - (csv-sort-skip-fields field) - (let* ((case-fold-search t) - (base - (if (looking-at "\\(0x\\)[0-9a-f]\\|\\(0\\)[0-7]") - (cond ((match-beginning 1) - (goto-char (match-end 1)) - 16) - ((match-beginning 2) - (goto-char (match-end 2)) - 8) - (t nil))))) - (string-to-number (buffer-substring (point) - (save-excursion - (forward-sexp 1) - (point))) - (or base sort-numeric-base)))) - nil)) - -(defun csv-reverse-region (beg end) - "Reverse the order of the lines in the region. -This is just a CSV-mode style interface to `reverse-region', which is -the function that should be used non-interactively. It takes two -point or marker arguments, BEG and END, delimiting the region." - ;; (interactive "*P\nr") - (interactive (csv-interactive-args 'noarg)) - (barf-if-buffer-read-only) - (reverse-region beg end)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Moving by field -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defsubst csv-end-of-field () - "Skip forward over one field." - (skip-chars-forward " ") - (if (eq (char-syntax (following-char)) ?\") - (goto-char (scan-sexps (point) 1))) - (skip-chars-forward csv--skip-chars)) - -(defsubst csv-beginning-of-field () - "Skip backward over one field." - (skip-syntax-backward " ") - (if (eq (char-syntax (preceding-char)) ?\") - (goto-char (scan-sexps (point) -1))) - (skip-chars-backward csv--skip-chars)) - -(defun csv-forward-field (arg) - "Move forward across one field, cf. `forward-sexp'. -With ARG, do it that many times. Negative arg -N means -move backward across N fields." - (interactive "p") - (if (< arg 0) - (csv-backward-field (- arg)) - (while (>= (setq arg (1- arg)) 0) - (if (or (bolp) - (when (and (not (eobp)) (eolp)) (forward-char) t)) - (while (and (not (eobp)) (csv-not-looking-at-record)) - (forward-line 1))) - (if (memq (following-char) csv-separator-chars) (forward-char)) - (csv-end-of-field)))) - -(defun csv-backward-field (arg) - "Move backward across one field, cf. `backward-sexp'. -With ARG, do it that many times. Negative arg -N means -move forward across N fields." - (interactive "p") - (if (< arg 0) - (csv-forward-field (- arg)) - (while (>= (setq arg (1- arg)) 0) - (when (or (eolp) - (when (and (not (bobp)) (bolp)) (backward-char) t)) - (while (progn - (beginning-of-line) - (csv-not-looking-at-record)) - (backward-char)) - (end-of-line)) - (if (memq (preceding-char) csv-separator-chars) (backward-char)) - (csv-beginning-of-field)))) - -(defun csv-sort-skip-fields (n &optional yank) - "Position point at the beginning of field N on the current line. -Fields are separated by `csv-separators'\; null terminal field allowed. -Assumes point is initially at the beginning of the line. -YANK non-nil allows N to be greater than the number of fields, in -which case extend the record as necessary." - (if (> n 0) - ;; Skip across N - 1 fields. - (let ((i (1- n))) - (while (> i 0) - (csv-end-of-field) - (if (eolp) - (if yank - (if (> i 1) (insert (car csv-separators))) - (error "Line has too few fields: %s" - (buffer-substring - (save-excursion (beginning-of-line) (point)) - (save-excursion (end-of-line) (point))))) - (forward-char)) ; skip separator - (setq i (1- i)))) - (end-of-line) - ;; Skip back across -N - 1 fields. - (let ((i (1- (- n)))) - (while (> i 0) - (csv-beginning-of-field) - (if (bolp) - (error "Line has too few fields: %s" - (buffer-substring - (save-excursion (beginning-of-line) (point)) - (save-excursion (end-of-line) (point))))) - (backward-char) ; skip separator - (setq i (1- i))) - ;; Position at the front of the field - ;; even if moving backwards. - (csv-beginning-of-field)))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Field index mode -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Based partly on paren.el - -(defcustom csv-field-index-delay 0.125 - "Time in seconds to delay before updating field index display." - :type '(number :tag "seconds")) - -(defvar csv-field-index-idle-timer nil) - -(defvar csv-field-index-string nil) -(make-variable-buffer-local 'csv-field-index-string) - -(defvar csv-field-index-old nil) -(make-variable-buffer-local 'csv-field-index-old) - -(define-minor-mode csv-field-index-mode - "Toggle CSV-Field-Index mode. -With prefix ARG, turn CSV-Field-Index mode on if and only if ARG is positive. -Returns the new status of CSV-Field-Index mode (non-nil means on). -When CSV-Field-Index mode is enabled, the current field index appears in -the mode line after `csv-field-index-delay' seconds of Emacs idle time." - :global t - :init-value t ; for documentation, since default is t - ;; This macro generates a function that first sets the mode - ;; variable, then runs the following code, runs the mode hooks, - ;; displays a message if interactive, updates the mode line and - ;; finally returns the variable value. - - ;; First, always disable the mechanism (to avoid having two timers): - (when csv-field-index-idle-timer - (cancel-timer csv-field-index-idle-timer) - (setq csv-field-index-idle-timer nil)) - ;; Now, if the mode is on and any buffer is in CSV mode then - ;; re-initialize and enable the mechanism by setting up a new timer: - (if csv-field-index-mode - (if (memq t (mapcar (lambda (buffer) - (with-current-buffer buffer - (when (derived-mode-p 'csv-mode) - (setq csv-field-index-string nil - csv-field-index-old nil) - t))) - (buffer-list))) - (setq csv-field-index-idle-timer - (run-with-idle-timer csv-field-index-delay t - #'csv-field-index))) - ;; but if the mode is off then remove the display from the mode - ;; lines of all CSV buffers: - (mapc (lambda (buffer) - (with-current-buffer buffer - (when (derived-mode-p 'csv-mode) - (setq csv-field-index-string nil - csv-field-index-old nil) - (force-mode-line-update)))) - (buffer-list)))) - -(defun csv--field-index () - (save-excursion - (let ((lbp (line-beginning-position)) (field 1)) - (while (re-search-backward csv-separator-regexp lbp 'move) - ;; Move as far as possible, i.e. to beginning of line. - (setq field (1+ field))) - (unless (csv-not-looking-at-record) field)))) - -(defun csv-field-index () - "Construct `csv-field-index-string' to display in mode line. -Called by `csv-field-index-idle-timer'." - (if (derived-mode-p 'csv-mode) - (let ((field (csv--field-index))) - (when (not (eq field csv-field-index-old)) - (setq csv-field-index-old field - csv-field-index-string - (and field (format "F%d" field))) - (force-mode-line-update))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Killing and yanking fields -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defvar csv-killed-fields nil - "A list of the fields or sub-records last killed by `csv-kill-fields'.") - -(defun csv-kill-fields (fields beg end) - "Kill specified fields of each line in the region. -If not set, the region defaults to the CSV records around point. -Fields are separated by `csv-separators' and null fields are allowed anywhere. -Field indices increase from 1 on the left or decrease from -1 on the right. -The fields are stored for use by `csv-yank-fields'. Fields can be -specified in any order but are saved in increasing index order. -Ignore blank and comment lines. - -When called interactively, a prefix argument specifies a single field, -otherwise prompt for a field list, which may include ranges in the form -m-n, where m < n and n defaults to the last field index if omitted. - -When called non-interactively, FIELDS is a single field index or a -list of field indices, with ranges specified as (m.n) or (m), and BEG -and END specify the region to process." - ;; (interactive "*P\nr") - (interactive (csv-interactive-args 'multiple)) - (barf-if-buffer-read-only) - ;; Kill the field(s): - (setq csv-killed-fields nil) - (save-excursion - (save-restriction - (narrow-to-region beg end) - (goto-char (point-min)) - (if (or (cdr fields) (consp (car fields))) - (csv-kill-many-columns fields) - (csv-kill-one-column (car fields))))) - (setq csv-killed-fields (nreverse csv-killed-fields))) - -(defun csv-kill-one-field (field) - "Kill field with index FIELD in current line. -Return killed text. Assumes point is at beginning of line." - ;; Move to start of field to kill: - (csv-sort-skip-fields field) - ;; Kill to end of field (cf. `kill-region'): - (prog1 (delete-and-extract-region - (point) - (progn (csv-end-of-field) (point))) - (if (eolp) - (unless (bolp) (delete-char -1)) ; Delete trailing separator at eol - (delete-char 1)))) ; or following separator otherwise. - -(defun csv-kill-one-column (field) - "Kill field with index FIELD in all lines in (narrowed) buffer. -Save killed fields in `csv-killed-fields'. -Assumes point is at `point-min'. Called by `csv-kill-fields'. -Ignore blank and comment lines." - (while (not (eobp)) - (or (csv-not-looking-at-record) - (push (csv-kill-one-field field) csv-killed-fields)) - (forward-line))) - -(defun csv-kill-many-columns (fields) - "Kill several fields in all lines in (narrowed) buffer. -FIELDS is an unordered list of field indices. -Save killed fields in increasing index order in `csv-killed-fields'. -Assumes point is at `point-min'. Called by `csv-kill-fields'. -Ignore blank and comment lines." - (if (eolp) (error "First record is empty")) - ;; Convert non-positive to positive field numbers: - (let ((last 1) (f fields)) - (csv-end-of-field) - (while (not (eolp)) - (forward-char) ; skip separator - (csv-end-of-field) - (setq last (1+ last))) ; last = # fields in first record - (while f - (cond ((consp (car f)) - ;; Expand a field range: (m.n) -> m m+1 ... n-1 n. - ;; If n is nil then it defaults to the number of fields. - (let* ((range (car f)) (cdrf (cdr f)) - (m (car range)) (n (cdr range))) - (if (< m 0) (setq m (+ m last 1))) - (if n - (if (< n 0) (setq n (+ n last 1))) - (setq n last)) - (setq range (list n)) - (while (> n m) (push (setq n (1- n)) range)) - (setcar f (car range)) - (setcdr f (cdr range)) - (setcdr (setq f (last range)) cdrf))) - ((zerop (car f)) (setcar f 1)) - ((< (car f) 0) (setcar f (+ f last 1)))) - (setq f (cdr f)))) - (goto-char (point-min)) - ;; Kill from right to avoid miscounting: - (setq fields (sort fields '>)) - (while (not (eobp)) - (or (csv-not-looking-at-record) - (let ((fields fields) killed-fields field) - (while fields - (setq field (car fields) - fields (cdr fields)) - (beginning-of-line) - (push (csv-kill-one-field field) killed-fields)) - (push (mapconcat #'identity killed-fields (car csv-separators)) - csv-killed-fields))) - (forward-line))) - -(defun csv-yank-fields (field beg end) - "Yank fields as the ARGth field of each line in the region. -ARG may be arbitrarily large and records are extended as necessary. -If not set, the region defaults to the CSV records around point\; -if point is not in a CSV record then offer to yank as a new table. -The fields yanked are those last killed by `csv-kill-fields'. -Fields are separated by `csv-separators' and null fields are allowed anywhere. -Field indices increase from 1 on the left or decrease from -1 on the right. -A prefix argument specifies a single field, otherwise prompt for field index. -Ignore blank and comment lines. When called non-interactively, FIELD -is a single field index\; BEG and END specify the region to process." - ;; (interactive "*P\nr") - (interactive (condition-case err - (csv-interactive-args 'single) - (error (list nil nil err)))) - (barf-if-buffer-read-only) - (if (null beg) - (if (y-or-n-p (concat (error-message-string end) - ". Yank as a new table? ")) - (csv-yank-as-new-table) - (error (error-message-string end))) - (if (<= field 0) (setq field (1+ field))) - (save-excursion - (save-restriction - (narrow-to-region beg end) - (goto-char (point-min)) - (let ((fields csv-killed-fields)) - (while (not (eobp)) - (unless (csv-not-looking-at-record) - ;; Yank at start of specified field if possible, - ;; otherwise yank at end of record: - (if (zerop field) - (end-of-line) - (csv-sort-skip-fields field 'yank)) - (and (eolp) (insert (car csv-separators))) - (when fields - (insert (car fields)) - (setq fields (cdr fields))) - (or (eolp) (insert (car csv-separators)))) - (forward-line))))))) - -(defun csv-yank-as-new-table () - "Yank fields as a new table starting at point. -The fields yanked are those last killed by `csv-kill-fields'." - (interactive "*") - (let ((fields csv-killed-fields)) - (while fields - (insert (car fields) ?\n) - (setq fields (cdr fields))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Aligning fields -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun csv--make-overlay (beg end &optional buffer front-advance rear-advance props) - (let ((o (make-overlay beg end buffer front-advance rear-advance))) - (overlay-put o 'csv t) - (while props - (overlay-put o (pop props) (pop props))) - o)) - -(defun csv--delete-overlay (o) - (and (overlay-get o 'csv) (delete-overlay o))) - -(defun csv--column-widths (beg end) - "Return a list of two lists (COLUMN-WIDTHS FIELD-WIDTHS). -COLUMN-WIDTHS is a list of elements (WIDTH START END) -indicating the widths of the columns after point (and the position of the -widest field that determined the overall width). -FIELD-WIDTHS contains the widths of each individual field after -point." - (let ((column-widths '()) - (field-widths '())) - (goto-char beg) - ;; Construct list of column widths: - (while (< (point) end) ; for each record... - (or (csv-not-looking-at-record) - (let ((w column-widths) - (col (current-column)) - (beg (point)) - field-width) - (while (not (eolp)) - (csv-end-of-field) - (setq field-width (- (current-column) col)) - (push field-width field-widths) - (if w - (if (> field-width (caar w)) - (setcar w (list field-width beg (point)))) - (setq w (list (list field-width beg (point))) - column-widths (nconc column-widths w))) - (or (eolp) (forward-char)) ; Skip separator. - (setq w (cdr w) col (current-column) beg (point))))) - (forward-line)) - (list column-widths (nreverse field-widths)))) - -(defun csv-align-fields (hard beg end) - "Align all the fields in the region to form columns. -The alignment style is specified by `csv-align-style'. The number of -spaces specified by `csv-align-padding' appears after each separator. -Use soft alignment done by displaying virtual white space after the -separators unless invoked with an argument, in which case insert real -space characters into the buffer after the separators. -Unalign first (see `csv-unalign-fields'). Ignore blank and comment lines. - -In hard-aligned records, separators become invisible whenever -`buffer-invisibility-spec' is non-nil. In soft-aligned records, make -separators invisible if and only if `buffer-invisibility-spec' is -non-nil when the records are aligned\; this can be changed only by -re-aligning. \(Unaligning always makes separators visible.) - -When called non-interactively, use hard alignment if HARD is non-nil\; -BEG and END specify the region to align. -If there is no selected region, default to the whole buffer." - (interactive (cons current-prefix-arg - (if (use-region-p) - (list (region-beginning) (region-end)) - (list (point-min) (point-max))))) - ;; FIXME: Use csv--jit-align when applicable! - (setq end (copy-marker end)) - (csv-unalign-fields hard beg end) ; If hard then barfs if buffer read only. - (save-excursion - (pcase-let ((`(,column-widths ,field-widths) (csv--column-widths beg end))) - (save-restriction - (narrow-to-region beg end) - (set-marker end nil) - - ;; Align fields: - (goto-char (point-min)) - (while (not (eobp)) ; for each record... - (unless (csv-not-looking-at-record) - (let ((w column-widths) - (column 0)) ;Desired position of left-side of this column. - (while (and w (not (eolp))) - (let* ((beg (point)) - (align-padding (if (bolp) 0 csv-align-padding)) - (left-padding 0) (right-padding 0) - (field-width (pop field-widths)) - (column-width (car (pop w))) - (x (- column-width field-width))) ; Required padding. - (csv-end-of-field) - (set-marker end (point)) ; End of current field. - ;; beg = beginning of current field - ;; end = (point) = end of current field - - ;; Compute required padding: - (cond - ((eq csv-align-style 'left) - ;; Left align -- pad on the right: - (setq left-padding align-padding - right-padding x)) - ((eq csv-align-style 'right) - ;; Right align -- pad on the left: - (setq left-padding (+ align-padding x))) - ((eq csv-align-style 'auto) - ;; Auto align -- left align text, right align numbers: - (if (string-match "\\`[-+.[:digit:]]+\\'" - (buffer-substring beg (point))) - ;; Right align -- pad on the left: - (setq left-padding (+ align-padding x)) - ;; Left align -- pad on the right: - (setq left-padding align-padding - right-padding x))) - ((eq csv-align-style 'centre) - ;; Centre -- pad on both left and right: - (let ((y (/ x 2))) ; truncated integer quotient - (setq left-padding (+ align-padding y) - right-padding (- x y))))) - - (cond - (hard ;; Hard alignment... - (when (> left-padding 0) ; Pad on the left. - ;; Insert spaces before field: - (if (= beg end) ; null field - (insert (make-string left-padding ?\ )) - (goto-char beg) ; beginning of current field - (insert (make-string left-padding ?\ )) - (goto-char end))) ; end of current field - (unless (eolp) - (if (> right-padding 0) ; pad on the right - ;; Insert spaces after field: - (insert (make-string right-padding ?\ ))) - ;; Make separator (potentially) invisible; - ;; in Emacs 21.3, neighbouring overlays - ;; conflict, so use the following only - ;; with hard alignment: - (csv--make-overlay (point) (1+ (point)) nil t nil - '(invisible csv evaporate t)) - (forward-char))) ; skip separator - - ;; Soft alignment... - ((or (memq 'csv buffer-invisibility-spec) - ;; For TSV, hidden or not doesn't make much difference, - ;; but the behavior is slightly better when we "hide" - ;; the TABs with a `display' property than if we add - ;; before/after-strings. - (tsv--mode-p)) - - ;; Hide separators... - ;; Merge right-padding from previous field - ;; with left-padding from this field: - (if (zerop column) - (when (> left-padding 0) - ;; Display spaces before first field - ;; by overlaying first character: - (csv--make-overlay - beg (1+ beg) nil nil nil - `(before-string ,(make-string left-padding ?\ )))) - ;; Display separator as spaces: - (with-silent-modifications - (put-text-property - (1- beg) beg - 'display `(space :align-to - ,(+ left-padding column))))) - (unless (eolp) (forward-char)) ; Skip separator. - (setq column (+ column column-width align-padding))) - - (t ;; Do not hide separators... - (let ((overlay (csv--make-overlay beg (point) nil nil t))) - (when (> left-padding 0) ; Pad on the left. - ;; Display spaces before field: - (overlay-put overlay 'before-string - (make-string left-padding ?\ ))) - (unless (eolp) - (if (> right-padding 0) ; Pad on the right. - ;; Display spaces after field: - (overlay-put - overlay - 'after-string (make-string right-padding ?\ ))) - (forward-char)))) ; Skip separator. - - ))))) - (forward-line))))) - (set-marker end nil)) - -(defun csv-unalign-fields (hard beg end) - "Undo soft alignment and optionally remove redundant white space. -Undo soft alignment introduced by `csv-align-fields'. If invoked with -an argument then also remove all spaces and tabs around separators. -Also make all invisible separators visible again. -Ignore blank and comment lines. When called non-interactively, remove -spaces and tabs if HARD non-nil\; BEG and END specify region to unalign. -If there is no selected region, default to the whole buffer." - (interactive (cons current-prefix-arg - (if (use-region-p) - (list (region-beginning) (region-end)) - (list (point-min) (point-max))))) - ;; Remove any soft alignment: - (mapc #'csv--delete-overlay (overlays-in beg end)) - (with-silent-modifications - (remove-list-of-text-properties beg end '(display invisible))) - (when hard - (barf-if-buffer-read-only) - ;; Remove any white-space padding around separators: - (save-excursion - (save-restriction - (narrow-to-region beg end) - (goto-char (point-min)) - (while (not (eobp)) - (or (csv-not-looking-at-record) - (while (not (eolp)) - ;; Delete horizontal white space forward: - ;; (delete-horizontal-space) - ;; This relies on left-to-right argument evaluation; - ;; see info node (elisp) Function Forms. - (delete-region (point) - (+ (point) (skip-chars-forward " \t"))) - (csv-end-of-field) - ;; Delete horizontal white space backward: - ;; (delete-horizontal-space t) - (delete-region (point) - (+ (point) (skip-chars-backward " \t"))) - (or (eolp) (forward-char)))) - (forward-line)))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Transposing rows and columns -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun csv-transpose (beg end) - "Rewrite rows (which may have different lengths) as columns. -Null fields are introduced as necessary within records but are -stripped from the ends of records. Preserve soft alignment. -This function is its own inverse. Ignore blank and comment lines. -When called non-interactively, BEG and END specify region to process." - ;; (interactive "*P\nr") - (interactive (csv-interactive-args 'noarg)) - (barf-if-buffer-read-only) - (save-excursion - (save-restriction - (narrow-to-region beg end) - (goto-char (point-min)) - ;; Delete rows and collect them as a reversed list of lists of - ;; fields, skipping comment and blank lines: - (let ((sep (car csv-separators)) - (align (overlays-in beg end)) - rows columns) - ;; Remove soft alignment if necessary: - (when align - (mapc #'csv--delete-overlay align) - (setq align t)) - (while (not (eobp)) - (if (csv-not-looking-at-record) - ;; Skip blank and comment lines: - (forward-line) - (let ((lep (line-end-position))) - (push - (split-string - (buffer-substring-no-properties (point) lep) - csv-separator-regexp) - rows) - (delete-region (point) lep) - (or (eobp) (delete-char 1))))) - ;; Rows must have monotonic decreasing lengths to be - ;; transposable, so ensure this by padding with null fields. - ;; rows is currently a reversed list of field lists, which - ;; must therefore have monotonic increasing lengths. - (let ((oldlen (length (car rows))) newlen - (r (cdr rows))) - (while r - (setq newlen (length (car r))) - (if (< newlen oldlen) - (nconc (car r) (make-list (- oldlen newlen) nil)) - (setq oldlen newlen)) - (setq r (cdr r)))) - ;; Collect columns as a reversed list of lists of fields: - (while rows - (let (column (r rows) row) - (while r - (setq row (car r)) - ;; Provided it would not be a trailing null field, push - ;; field onto column: - (if (or column (string< "" (car row))) - (push (car row) column)) - ;; Pop field off row: - (setcar r (cdr row)) - ;; If row is now empty then remove it: - (or (car r) (setq rows (cdr rows))) - (setq r (cdr r))) - (push column columns))) - ;; Insert columns into buffer as rows: - (setq columns (nreverse columns)) - (while columns - (insert (mapconcat #'identity (car columns) sep) ?\n) - (setq columns (cdr columns))) - ;; Re-do soft alignment if necessary: - (if align (csv-align-fields nil (point-min) (point-max))))))) - -(defvar-local csv--header-line nil) -(defvar-local csv--header-hscroll nil) -(defvar-local csv--header-string nil) - -(defun csv-header-line (&optional use-current-line) - "Set/unset the header line. -If the optional prefix arg USE-CURRENT-LINE is nil, use the first line -as the header line. -If there is already a header line, then unset the header line." - (interactive "P") - (if csv--header-line - (progn - (delete-overlay csv--header-line) - (setq csv--header-line nil) - (kill-local-variable 'header-line-format)) - (save-excursion - (unless use-current-line (goto-char (point-min))) - (setq csv--header-line (make-overlay (line-beginning-position) - (line-end-position) - nil nil t)) - (overlay-put csv--header-line 'modification-hooks - '(csv--header-flush))) - (csv--header-flush) - (setq header-line-format - '(:eval (csv--header-string))))) - -(defun csv--header-flush (&rest _) - ;; Force re-computation of the header-line. - (setq csv--header-hscroll nil)) - -(defun csv--header-string () - ;; FIXME: Won't work with multiple windows showing that same buffer. - (if (eql (window-hscroll) csv--header-hscroll) - csv--header-string - (setq csv--header-hscroll (window-hscroll)) - (setq csv--header-string - (csv--compute-header-string)))) - -(defun csv--compute-header-string () - (with-demoted-errors "csv--compute-header-string %S" - (save-excursion - (goto-char (overlay-start csv--header-line)) - ;; Re-set the line-end-position, just in case. - (move-overlay csv--header-line (point) (line-end-position)) - (jit-lock-fontify-now (point) (line-end-position)) - ;; Not sure why it is sometimes nil! - (move-to-column (or csv--header-hscroll 0)) - (let ((str (buffer-substring (point) (line-end-position))) - (i 0)) - (while (and i (< i (length str))) - (let ((prop (get-text-property i 'display str))) - (and (eq (car-safe prop) 'space) - (eq (car-safe (cdr prop)) :align-to) - (let* ((x (nth 2 prop)) - (nexti (next-single-property-change i 'display str)) - (newprop - `(space :align-to - ,(if (numberp x) - (- x (or csv--header-hscroll 0)) - `(- ,x csv--header-hscroll))))) - (put-text-property i (or nexti (length str)) - 'display newprop str) - (setq i nexti)))) - (setq i (next-single-property-change i 'display str))) - (concat (propertize " " 'display '((space :align-to 0))) str))))) - -;;; Auto-alignment - -(defcustom csv-align-max-width 40 - "Maximum width of a column in `csv-align-mode'. -This does not apply to the last column (for which the usual `truncate-lines' -setting works better)." - :type 'integer) - -(defvar-local csv--config-column-widths nil - "Settings per column, stored as a list indexed by the column.") - -(defun csv-align--set-column (column value) - (let ((len (length csv--config-column-widths))) - (if (< len column) - (setq csv--config-column-widths - (nconc csv--config-column-widths (make-list (- column len) nil)))) - (setf (nth (1- column) csv--config-column-widths) value))) - -(defun csv-align-set-column-width (column width) - "Set the max WIDTH to use for COLUMN." - (interactive - (let* ((field (or (csv--field-index) 1)) - (curwidth (nth (1- field) csv--config-column-widths))) - (list field - (cond - ((numberp current-prefix-arg) - current-prefix-arg) - (current-prefix-arg - (read-number (format "Column width (for field %d): " field) - curwidth)) - (t (if curwidth nil (csv--ellipsis-width))))))) - (when (eql width csv-align-max-width) - (setq width nil)) - (csv-align--set-column column width) - (jit-lock-refontify)) - -(defvar-local csv--jit-columns nil) - -(defun csv--jit-merge-columns (column-widths) - ;; FIXME: The incremental update (delayed by jit-lock-context-time) of column - ;; width is a bit jarring at times. It's OK while scrolling or when - ;; extending a column, but not right when enabling the csv-align-mode or - ;; when shortening the longest field (or deleting the line containing it), - ;; because in that case we have *several* cascaded updates, e.g.: - ;; - Remove the line with the longest field of column N. - ;; - Edit some line: this line is updated as if its field was the widest, - ;; hence its subsequent fields are too much to the left. - ;; - The rest is updated starting from the first few lines (according - ;; to jit-lock-chunk-size). - ;; - After the first few lines, come the next set of few lines, - ;; which may cause the previous few lines to need refresh again. - ;; - etc.. until arriving again at the edited line which is re-aligned - ;; again. - ;; - etc.. until the end of the windows, potentially causing yet more - ;; refreshes as we discover yet-wider fields for this column. - (let ((old-columns csv--jit-columns) - (changed nil)) - (while (and old-columns column-widths) - (when (or (> (caar column-widths) (caar old-columns)) - ;; Apparently modification-hooks aren't run when the - ;; whole text containing the overlay is deleted (e.g. - ;; the whole line), so detect this case here. - ;; It's a bit too late, but better than never. - (null (overlay-buffer (cdar old-columns)))) - (setq changed t) ;; Return non-nil if some existing column changed. - (pcase-let ((`(,width ,beg ,end) (car column-widths))) - (setf (caar old-columns) width) - (move-overlay (cdar old-columns) beg end))) - (setq old-columns (cdr old-columns)) - (setq column-widths (cdr column-widths))) - (when column-widths - ;; New columns appeared. - (setq csv--jit-columns - (nconc csv--jit-columns - (mapcar (lambda (x) - (pcase-let* - ((`(,width ,beg ,end) x) - (ol (make-overlay beg end))) - (overlay-put ol 'csv-width t) - (overlay-put ol 'evaporate t) - (overlay-put ol 'modification-hooks - (list #'csv--jit-width-change)) - (cons width ol))) - column-widths)))) - changed)) - -(defun csv--jit-width-change (ol after _beg _end &optional len) - (when (and after (> len 0)) - ;; (let ((x (rassq ol csv--jit-columns))) - ;; (when x (setf (car x) -1))) - (delete-overlay ol))) - -(defun csv--jit-unalign (beg end) - (remove-text-properties beg end - '(display nil csv--jit nil invisible nil - cursor-sensor-functions nil csv--revealed nil)) - (remove-overlays beg end 'csv--jit t)) - -(defun csv--jit-flush (beg end) - "Cause all the buffer (except for the BEG...END region) to be re-aligned." - (cl-assert (>= end beg)) - ;; The buffer shouldn't have changed since beg/end were computed, - ;; but just in case, let's make sure they're still sane. - (when (< beg (point-min)) - (setq beg (point-min) end (max end beg))) - (when (< (point-max) end) - (setq end (point-max) beg (min end beg))) - (let ((pos (point-min))) - (while (and (< pos beg) - (setq pos (text-property-any pos beg 'csv--jit t))) - (jit-lock-refontify - pos (setq pos (or (text-property-any pos beg 'csv--jit nil) beg)))) - (setq pos end) - (while (and (< pos (point-max)) - (setq pos (text-property-any pos (point-max) 'csv--jit t))) - (jit-lock-refontify - pos (setq pos (or (text-property-any pos (point-max) 'csv--jit nil) - (point-max)))))) - (csv--header-flush)) - -(defun csv--ellipsis-width () - (let ((ellipsis - (when standard-display-table - (display-table-slot standard-display-table - 'selective-display)))) - (if ellipsis (length ellipsis) 3))) - -(defun csv-align--cursor-truncated (window oldpos dir) - ;; FIXME: Neither the `entered' nor the `left' event are guaranteed - ;; to be sent, and for the `left' case, even when we do get called, - ;; it may be unclear where the revealed text was (it's somewhere around - ;; `oldpos', but that position can be stale). - ;; Worse, if we have several windows displaying the buffer, when one - ;; cursor leaves we may need to keep the text revealed because of - ;; another window's cursor. - (let* ((prop (if (eq dir 'entered) 'invisible 'csv--revealed)) - (pos (cond - ((eq dir 'entered) (window-point window)) - (t (max (point-min) - (min (point-max) - (or oldpos (window-point window))))))) - (start (cond - ((and (> pos (point-min)) - (eq (get-text-property (1- pos) prop) 'csv-truncate)) - (or (previous-single-property-change pos prop) (point-min))) - (t pos))) - (end (if (eq (get-text-property pos prop) 'csv-truncate) - (or (next-single-property-change pos prop) (point-max)) - pos))) - (unless (eql start end) - (with-silent-modifications - (put-text-property start end - (if (eq dir 'entered) 'csv--revealed 'invisible) - 'csv-truncate) - (remove-text-properties start end (list prop)))))) - -(defun csv--jit-align (beg end) - (save-excursion - ;; This is run with inhibit-modification-hooks set, so the overlays' - ;; modification-hook doesn't work :-( - (and csv--header-line - (<= beg (overlay-end csv--header-line)) - (>= end (overlay-start csv--header-line)) - (csv--header-flush)) - ;; First, round up to a whole number of lines. - (goto-char end) - (unless (bolp) (forward-line 1) (setq end (point))) - (goto-char beg) - (unless (bolp) (forward-line 1) (setq beg (point))) - (csv--jit-unalign beg end) - (put-text-property beg end 'csv--jit t) - - (pcase-let* ((`(,column-widths ,field-widths) (csv--column-widths beg end)) - (changed (csv--jit-merge-columns column-widths)) - (ellipsis-width (csv--ellipsis-width))) - (when changed - ;; Do it after the current redisplay is over. - (run-with-timer jit-lock-context-time nil #'csv--jit-flush beg end)) - - ;; Align fields: - (goto-char beg) - (while (< (point) end) - (unless (csv-not-looking-at-record) - (let ((w csv--jit-columns) - (widths-config csv--config-column-widths) - (column 0)) ;Desired position of left-side of this column. - (while (and w (not (eolp))) - (let* ((field-beg (point)) - (width-config (pop widths-config)) - (align-padding (if (bolp) 0 csv-align-padding)) - (left-padding 0) (right-padding 0) - (field-width (pop field-widths)) - (column-width - (min (car (pop w)) - (or width-config - ;; Don't apply csv-align-max-width - ;; to the last field! - (if w csv-align-max-width - most-positive-fixnum)))) - (x (- column-width field-width)) ; Required padding. - (truncate nil)) - (csv-end-of-field) - ;; beg = beginning of current field - ;; end = (point) = end of current field - (when (< x 0) - (setq truncate (max column - (+ column column-width - align-padding (- ellipsis-width)))) - (setq x 0)) - ;; Compute required padding: - (pcase csv-align-style - ('left - ;; Left align -- pad on the right: - (setq left-padding align-padding - right-padding x)) - ('right - ;; Right align -- pad on the left: - (setq left-padding (+ align-padding x))) - ('auto - ;; Auto align -- left align text, right align numbers: - (if (string-match "\\`[-+.[:digit:]]+\\'" - (buffer-substring field-beg (point))) - ;; Right align -- pad on the left: - (setq left-padding (+ align-padding x)) - ;; Left align -- pad on the right: - (setq left-padding align-padding - right-padding x))) - ('centre - ;; Centre -- pad on both left and right: - (let ((y (/ x 2))) ; truncated integer quotient - (setq left-padding (+ align-padding y) - right-padding (- x y))))) - - (cond - - ((or (memq 'csv buffer-invisibility-spec) - ;; For TSV, hidden or not doesn't make much difference, - ;; but the behavior is slightly better when we "hide" - ;; the TABs with a `display' property than if we add - ;; before/after-strings. - (tsv--mode-p)) - - ;; Hide separators... - ;; Merge right-padding from previous field - ;; with left-padding from this field: - (if (zerop column) - (when (> left-padding 0) - ;; Display spaces before first field - ;; by overlaying first character: - (csv--make-overlay - field-beg (1+ field-beg) nil nil nil - `(before-string ,(make-string left-padding ?\ ) - csv--jit t))) - ;; Display separator as spaces: - (with-silent-modifications - (put-text-property - (1- field-beg) field-beg - 'display `(space :align-to - ,(+ left-padding column)))))) - - (t ;; Do not hide separators... - (let ((overlay (csv--make-overlay field-beg (point) - nil nil t - '(csv--jit t)))) - (when (> left-padding 0) ; Pad on the left. - ;; Display spaces before field: - (overlay-put overlay 'before-string - (make-string left-padding ?\ ))) - (unless (eolp) - (if (> right-padding 0) ; Pad on the right. - ;; Display spaces after field: - (overlay-put - overlay - 'after-string (make-string right-padding ?\ ))))))) - (setq column (+ column column-width align-padding)) - ;; Do it after applying the property, so `move-to-column' can - ;; take it into account. - (when truncate - (let ((trunc-pos - (save-excursion - ;; ¡¡ BIG UGLY HACK !! - ;; `current-column' and `move-to-column' count - ;; text hidden with an ellipsis "as if" it were - ;; fully visible, which is completely wrong here, - ;; so circumvent this by temporarily pretending - ;; that `csv-truncate' is fully invisible (which - ;; isn't quite right either, but should work - ;; just well enough for us here). - (let ((buffer-invisibility-spec - buffer-invisibility-spec)) - (add-to-invisibility-spec 'csv-truncate) - (move-to-column truncate)) - (point)))) - (put-text-property trunc-pos (point) - 'invisible 'csv-truncate) - (when (> (- (point) trunc-pos) 1) - ;; Arrange to temporarily untruncate the string when - ;; cursor moves into it. - ;; FIXME: This only works if - ;; `global-disable-point-adjustment' is non-nil! - ;; Arguably this should be fixed by making - ;; point-adjustment code pay attention to - ;; cursor-sensor-functions! - (put-text-property - (1+ trunc-pos) (point) - 'cursor-sensor-functions - (list #'csv-align--cursor-truncated))))) - (unless (eolp) (forward-char)) ; Skip separator. - )))) - (forward-line))) - `(jit-lock-bounds ,beg . ,end))) - -(define-minor-mode csv-align-mode - "Align columns on the fly." - :global nil - (csv-unalign-fields nil (point-min) (point-max)) ;Just in case. - (cond - (csv-align-mode - (add-to-invisibility-spec '(csv-truncate . t)) - (kill-local-variable 'csv--jit-columns) - (cursor-sensor-mode 1) - (jit-lock-register #'csv--jit-align) - (jit-lock-refontify)) - (t - (remove-from-invisibility-spec '(csv-truncate . t)) - (jit-lock-unregister #'csv--jit-align) - (csv--jit-unalign (point-min) (point-max)))) - (csv--header-flush)) - -;;; TSV support - -;; Since "the" CSV format is really a bunch of different formats, it includes -;; TSV as a subcase, but this subcase is sufficiently interesting that it has -;; its own mime-type and mostly standard file extension, also it suffers -;; less from the usual quoting problems of CSV (because the only problematic -;; chars are LF and TAB, really, which are much less common inside fields than -;; commas, space, and semi-colons) so it's "better behaved". - -(defvar tsv-mode-syntax-table - ;; Inherit from `text-mode-syntax-table' rather than from - ;; `csv-mode-syntax-table' so as not to inherit the - ;; `csv-field-quotes' settings. - (let ((st (make-syntax-table text-mode-syntax-table))) - st)) - -(defvar tsv-mode-map - (let ((map (make-sparse-keymap))) - ;; In `tsv-mode', the `csv-invisibility-default/csv-toggle-invisibility' - ;; business doesn't make much sense. - (define-key map [remap csv-toggle-invisibility] #'undefined) - map)) - -;;;###autoload -(add-to-list 'auto-mode-alist '("\\.tsv\\'" . tsv-mode)) - -(defun tsv--mode-p () - (equal csv-separator-chars '(?\t))) - -;;;###autoload -(define-derived-mode tsv-mode csv-mode "TSV" - "Major mode for editing files of tab-separated value type." - :group 'CSV - ;; In TSV we know TAB is the only possible separator. - (setq-local csv-separators '("\t")) - ;; FIXME: Copy&pasted from the `:set'ter of csv-separators! - (setq-local csv-separator-chars '(?\t)) - (setq-local csv--skip-chars "^\n\t") - (setq-local csv-separator-regexp "\t") - (setq-local csv-font-lock-keywords - ;; NB: csv-separator-face variable evaluates to itself. - `((,csv-separator-regexp (0 'csv-separator-face)))) - - ;; According to wikipedia, TSV doesn't use quotes but uses backslash escapes - ;; of the form \n, \t, \r, and \\ instead. - (setq-local csv-field-quotes nil)) - -;;;; ChangeLog: - -;; 2019-10-22 Stefan Monnier -;; -;; * packages/csv-mode/csv-mode.el (csv-align--cursor-truncated): Fix C-e -;; case -;; -;; 2019-10-22 Stefan Monnier -;; -;; * packages/csv-mode/csv-mode.el: Auto-shorten columns as well -;; -;; (csv--column-widths): Also return the position of the widest field in -;; each column. -;; (csv-align-fields, csv--jit-align): Update accordingly. -;; (csv--jit-width-change): New function. -;; (csv--jit-merge-columns): Use it on overlays placed on the widest field -;; of each column, to detect when they're shortened. -;; -;; 2019-10-19 Stefan Monnier -;; -;; * packages/csv-mode/csv-mode.el: More cvs-align-mode improvements -;; -;; Rename csv-align-fields-* to cvs-align-*. -;; (csv-transpose): Use split-string. -;; (csv-split-string): Delete function. -;; (csv--config-column-widths): New var. -;; (csv-align--set-column): New function. -;; (csv-align-set-column-width): New command. -;; (csv--jit-align): Use them to obey the per-column width settings. Delay -;; context refresh by jit-lock-context-time. Set cursor-sensor-functions to -;; untruncate fields on-the-fly. -;; (csv-align--cursor-truncated): New function. -;; (csv-align-mode): Activate cursor-sensor-mode. -;; -;; 2019-10-19 Stefan Monnier -;; -;; * packages/csv-mode/csv-mode.el: Fix incorrect truncation -;; -;; (csv--field-index): New function, extracted from csv-field-index. -;; (csv--jit-align): Don't apply csv-align-fields-max-width to the last -;; column. Fix move-to-column call. -;; -;; 2019-10-10 Stefan Monnier -;; -;; * packages/csv-mode/csv-mode.el: Fix header-line's alignment -;; -;; (csv-header-line): Change csv--header-line into an overlay. Add a -;; modification-hooks to auto-refresh the header-line. -;; (csv--header-flush, csv--header-string): New functions. -;; (csv--compute-header-string): Make sure jit-lock was applied. -;; csv--header-hscroll can be nil sometimes somehow! -;; (csv--jit-flush, csv-align-fields-mode): Flush header-line as well. -;; (csv--jit-align): Flush header-line when applicable. Fix typo. -;; -;; 2019-10-09 Filipp Gunbin -;; -;; packages/csv-mode/csv-mode.el: Fix csv-align-fields doc -;; -;; (csv-align-fields): docstring mentioned csv-align-fields instead of -;; csv-align-padding -;; -;; 2019-09-29 Stefan Monnier -;; -;; * packages/csv-mode/csv-mode.el: Remove Francis as maintainer -;; -;; (csv-unalign-fields): Also remove the `invisible` property since we use -;; it to truncate fields in csv--jit-align. -;; (csv-align-fields-max-width): Rename from csv-align-field-max-width to -;; match the "csv-align-fields" prefix. -;; (csv--ellipsis-width): New function. -;; (csv--jit-align): Use it to truncate more correctly. -;; -;; 2019-09-27 Stefan Monnier -;; -;; * packages/csv-mode/csv-mode.el (csv-align-field-max-width): New var -;; -;; (csv--jit-unalign): Erase invisible property as well. -;; (csv--jit-align): Truncate field to fit within csv-align-field-max-width -;; when needed. -;; (csv-align-fields-mode): Add/remove `csv-truncate` to invisibility spec. -;; -;; 2019-09-27 Francis Wright -;; -;; * packages/csv-mode/csv-mode.el: Fix for customize-mode -;; -;; (csv-mode, tsv-mode): Specify :group explicitly for `customize-mode`s -;; benefit -;; -;; 2019-09-24 Stefan Monnier -;; -;; * packages/csv-mode/csv-mode.el: Add tsv-mode and csv-align-fields-mode -;; -;; Require cl-lib. Don't set buffer-invisibility-spec directly. -;; (csv--skip-chars): Rename from misleading csv--skip-regexp. -;; (csv-mode): Set normal-auto-fill-function to really disable -;; auto-fill-mode. -;; (csv--column-widths): Only operate over new args beg..end. -;; (csv-align-fields): No need to narrow before csv--column-widths any -;; more. -;; (csv-align-fields-mode): New minor mode. -;; (tsv-mode): New major mode. -;; -;; 2019-09-18 Simen Heggestþyl -;; -;; Speed up 'csv-align-fields' -;; -;; * packages/csv-mode/csv-mode.el: Bump version number and make the -;; dependency on Emacs 24.1 or higher explicit. -;; (csv--column-widths): Return the field widths as well. -;; (csv-align-fields): Speed up by using the field widths already computed -;; by 'csv--column-widths' (bug#37393). -;; -;; 2017-12-05 Stefan Monnier -;; -;; * csv-mode/csv-mode.el (csv-header-line): New command -;; -;; (csv-menu): Add an entry for it. -;; (csv--header-line, csv--header-hscroll, csv--header-string): New vars. -;; (csv--compute-header-string): New function. -;; -;; 2016-07-11 Paul Eggert -;; -;; Fix some quoting problems in doc strings -;; -;; Most of these are minor issues involving, e.g., quoting `like this' -;; instead of 'like this'. A few involve escaping ` and ' with a preceding -;; \= when the characters should not be turned into curved single quotes. -;; -;; 2016-04-21 Leo Liu -;; -;; Fix csv-mode to delete its own overlays only -;; -;; * csv-mode/csv-mode.el (csv--make-overlay, csv--delete-overlay): New -;; functions. -;; (csv-align-fields, csv-unalign-fields, csv-transpose): Use them. -;; -;; 2016-03-04 Francis Wright -;; -;; * csv-mode/csv-mode.el: Remove out-of-date "URL:" header. -;; -;; 2016-03-03 Stefan Monnier -;; -;; * csv-mode, landmark: Fix maintainer's email -;; -;; 2015-07-09 Leo Liu -;; -;; Fix column width calculation in cvs-mode.el -;; -;; * csv-mode/cvs-mode.el (csv--column-widths, csv-align-fields): Fix -;; column width calculation. -;; -;; 2015-05-24 Leo Liu -;; -;; * csv-mode/cvs-mode.el (csv-set-comment-start): Handle nil. -;; -;; See also http://debbugs.gnu.org/20564. -;; -;; 2015-04-15 Stefan Monnier -;; -;; (csv-mode): Set mode-line-position rather than mode-line-format. -;; -;; Fixes: debbugs:20343 -;; -;; * csv-mode/csv-mode.el (csv-mode-line-format): Only keep the CSV part of -;; the mode line. -;; -;; 2014-01-15 Stefan Monnier -;; -;; * csv-mode (csv-mode-line-help-echo): Remove. -;; -;; 2013-04-24 Stefan Monnier -;; -;; * csv-mode.el (csv-kill-one-field): Check for presence before deleting -;; trailing separator. Remove last arg and turn into a function. -;; (csv-kill-one-column, csv-kill-many-columns): Adjust callers. -;; -;; 2012-10-22 Stefan Monnier -;; -;; * packages/csv-mode/csv-mode.el (csv-end-of-field): Don't skip TABs. -;; (csv--skip-regexp): Rename from csv-skip-regexp. -;; -;; 2012-10-10 Stefan Monnier -;; -;; * csv-mode.el: Bump version number. -;; -;; 2012-10-10 Stefan Monnier -;; -;; * csv-mode.el: Use lexical-binding. Remove redundant :group args. -;; (csv-separators): Add TAB to the default. -;; (csv-invisibility-default): Change default to t. -;; (csv-separator-face): Inherit from escape-glyph. Remove variable. -;; (csv-mode-line-format): Remove trailing "--". Move next to line-number. -;; (csv-interactive-args): Use use-region-p. -;; (csv--column-widths): New function, extracted from csv-align-fields. -;; (csv-align-fields): Use it. Use whole buffer by default. Use :align-to -;; and text-properties when possible. -;; (csv-unalign-fields): Also remove properties. -;; (csv-mode): Truncate lines. -;; -;; 2012-03-24 Chong Yidong -;; -;; Commentary fix for quarter-plane.el. -;; -;; 2012-03-24 Chong Yidong -;; -;; Commentary tweaks for csv-mode, ioccur, and nhexl-mode packages. -;; -;; 2012-03-24 Chong Yidong -;; -;; csv-mode.el: Improve commentary. -;; -;; 2012-03-12 Stefan Monnier -;; -;; * packages/csv-mode/csv-mode.el: Minor installation cleanups. Fix up -;; copyright notice. Set version. -;; (csv-separators, csv-field-quotes): Fix calls to `error'. -;; (csv-mode-line-help-echo, csv-mode-line-format): Replace -;; mode-line-format for default-mode-line-format. -;; (csv-mode-map): Declare and initialize. -;; (csv-mode): Add autoload cookie. -;; (csv-set-comment-start): Make sure vars are made buffer-local. -;; (csv-field-index-mode, csv-field-index): Use derived-mode-p. -;; (csv-align-fields): Improve insertion types of overlay's markers. -;; -;; 2012-03-12 Stefan Monnier -;; -;; Add csv-mode.el. -;; - - - -(provide 'csv-mode) - -;;; csv-mode.el ends here diff --git a/localelpa/eldoc-1.15.0.tar b/localelpa/eldoc-1.15.0.tar deleted file mode 100644 index 0d546dad6b1650b6eb522e322b98c5d6f06c25f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51200 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 diff --git a/localelpa/let-alist-1.0.6.el b/localelpa/let-alist-1.0.6.el deleted file mode 100644 index 96b397f1d6..0000000000 --- a/localelpa/let-alist-1.0.6.el +++ /dev/null @@ -1,184 +0,0 @@ -;;; let-alist.el --- Easily let-bind values of an assoc-list by their names -*- lexical-binding: t; -*- - -;; Copyright (C) 2014-2019 Free Software Foundation, Inc. - -;; Author: Artur Malabarba -;; Package-Requires: ((emacs "24.1")) -;; Version: 1.0.6 -;; Keywords: extensions lisp -;; Prefix: let-alist -;; Separator: - - -;; This is an Elpa :core package. Don't use functionality that is not -;; compatible with Emacs 24.1. - -;; This file is part of GNU Emacs. - -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see . - -;;; Commentary: - -;; This package offers a single macro, `let-alist'. This macro takes a -;; first argument (whose value must be an alist) and a body. -;; -;; The macro expands to a let form containing body, where each dotted -;; symbol inside body is let-bound to their cdrs in the alist. Dotted -;; symbol is any symbol starting with a `.'. Only those present in -;; the body are let-bound and this search is done at compile time. -;; -;; For instance, the following code -;; -;; (let-alist alist -;; (if (and .title .body) -;; .body -;; .site -;; .site.contents)) -;; -;; essentially expands to -;; -;; (let ((.title (cdr (assq 'title alist))) -;; (.body (cdr (assq 'body alist))) -;; (.site (cdr (assq 'site alist))) -;; (.site.contents (cdr (assq 'contents (cdr (assq 'site alist)))))) -;; (if (and .title .body) -;; .body -;; .site -;; .site.contents)) -;; -;; If you nest `let-alist' invocations, the inner one can't access -;; the variables of the outer one. You can, however, access alists -;; inside the original alist by using dots inside the symbol, as -;; displayed in the example above by the `.site.contents'. -;; -;;; Code: - - -(defun let-alist--deep-dot-search (data) - "Return alist of symbols inside DATA that start with a `.'. -Perform a deep search and return an alist where each car is the -symbol, and each cdr is the same symbol without the `.'." - (cond - ((symbolp data) - (let ((name (symbol-name data))) - (when (string-match "\\`\\." name) - ;; Return the cons cell inside a list, so it can be appended - ;; with other results in the clause below. - (list (cons data (intern (replace-match "" nil nil name))))))) - ((vectorp data) - (apply #'nconc (mapcar #'let-alist--deep-dot-search data))) - ((not (consp data)) nil) - ((eq (car data) 'let-alist) - ;; For nested ‘let-alist’ forms, ignore symbols appearing in the - ;; inner body because they don’t refer to the alist currently - ;; being processed. See Bug#24641. - (let-alist--deep-dot-search (cadr data))) - (t (append (let-alist--deep-dot-search (car data)) - (let-alist--deep-dot-search (cdr data)))))) - -(defun let-alist--access-sexp (symbol variable) - "Return a sexp used to access SYMBOL inside VARIABLE." - (let* ((clean (let-alist--remove-dot symbol)) - (name (symbol-name clean))) - (if (string-match "\\`\\." name) - clean - (let-alist--list-to-sexp - (mapcar #'intern (nreverse (split-string name "\\."))) - variable)))) - -(defun let-alist--list-to-sexp (list var) - "Turn symbols LIST into recursive calls to `cdr' `assq' on VAR." - `(cdr (assq ',(car list) - ,(if (cdr list) (let-alist--list-to-sexp (cdr list) var) - var)))) - -(defun let-alist--remove-dot (symbol) - "Return SYMBOL, sans an initial dot." - (let ((name (symbol-name symbol))) - (if (string-match "\\`\\." name) - (intern (replace-match "" nil nil name)) - symbol))) - - -;;; The actual macro. -;;;###autoload -(defmacro let-alist (alist &rest body) - "Let-bind dotted symbols to their cdrs in ALIST and execute BODY. -Dotted symbol is any symbol starting with a `.'. Only those present -in BODY are let-bound and this search is done at compile time. - -For instance, the following code - - (let-alist alist - (if (and .title .body) - .body - .site - .site.contents)) - -essentially expands to - - (let ((.title (cdr (assq \\='title alist))) - (.body (cdr (assq \\='body alist))) - (.site (cdr (assq \\='site alist))) - (.site.contents (cdr (assq \\='contents (cdr (assq \\='site alist)))))) - (if (and .title .body) - .body - .site - .site.contents)) - -If you nest `let-alist' invocations, the inner one can't access -the variables of the outer one. You can, however, access alists -inside the original alist by using dots inside the symbol, as -displayed in the example above." - (declare (indent 1) (debug t)) - (let ((var (make-symbol "alist"))) - `(let ((,var ,alist)) - (let ,(mapcar (lambda (x) `(,(car x) ,(let-alist--access-sexp (car x) var))) - (delete-dups (let-alist--deep-dot-search body))) - ,@body)))) - -;;;; ChangeLog: - -;; 2015-12-01 Artur Malabarba -;; -;; packages/let-alist: Define it as a :core package -;; -;; 2015-06-11 Artur Malabarba -;; -;; * let-alist (let-alist--deep-dot-search): Fix cons -;; -;; 2015-03-07 Artur Malabarba -;; -;; let-alist: Update copyright -;; -;; 2014-12-22 Artur Malabarba -;; -;; packages/let-alist: Use `make-symbol' instead of `gensym'. -;; -;; 2014-12-20 Artur Malabarba -;; -;; packages/let-alist: Enable access to deeper alists -;; -;; 2014-12-14 Artur Malabarba -;; -;; let-alist.el: Add lexical binding. Version bump. -;; -;; 2014-12-11 Artur Malabarba -;; -;; let-alist: New package -;; - - -(provide 'let-alist) - -;;; let-alist.el ends here diff --git a/localelpa/nadvice-0.3.el b/localelpa/nadvice-0.3.el deleted file mode 100644 index 7c4ba046ca..0000000000 --- a/localelpa/nadvice-0.3.el +++ /dev/null @@ -1,124 +0,0 @@ -;;; nadvice.el --- Forward compatibility for Emacs-24.4's nadvice - -;; Copyright (C) 2018 Free Software Foundation, Inc. - -;; Author: Stefan Monnier -;; Version: 0.3 -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; This package tries to re-implement some of nadvice.el's functionality -;; on top of the old defadvice system, to help users of defadvice -;; move to the new advice system without dropping support for Emacs<24.4. -;; -;; Limitations; -;; - only supports `advice-add' and `advice-remove'; -;; - only handles the :before, :after, :override, and :around kinds of advice; -;; - requires a named rather than anonymous function; -;; - and does not support any additional properties like `name' or `depth'. -;; -;; It was tested on Emacs-22 and I can't see any obvious reason why it -;; wouldn't work on older Emacsen. - -;;; Code: - -(declare-function ad-remove-advice "advice") - -(unless (fboundp 'add-function) - ;; If `add-function' is defined, we're presumably running on - ;; an Emacs that comes with the real nadvice.el, so let's be careful - ;; to do nothing in that case! - - ;; Load `advice' manually, in case `advice-remove' is called first, - ;; since ad-remove-advice is not autoloaded. - (require 'advice) - -;;;###autoload -(defun advice-add (symbol where function &optional props) - (when props - (error "This version of nadvice.el does not support PROPS")) - (unless (symbolp function) - (error "This version of nadvice.el requires FUNCTION to be a symbol")) - (let ((body (cond - ((eq where :before) - `(progn (apply #',function (ad-get-args 0)) ad-do-it)) - ((eq where :after) - `(progn ad-do-it (apply #',function (ad-get-args 0)))) - ((eq where :override) - `(setq ad-return-value (apply #',function (ad-get-args 0)))) - ((eq where :around) - `(setq ad-return-value - (apply #',function - (lambda (&rest nadvice--rest-arg) - (ad-set-args 0 nadvice--rest-arg) - ad-do-it) - (ad-get-args 0)))) - (t (error "This version of nadvice.el does not handle %S" - where))))) - (ad-add-advice symbol - `(,function nil t (advice lambda () ,body)) - 'around - nil) - (ad-activate symbol))) - -;;;###autoload -(defun advice-remove (symbol function) - ;; Just return nil if there is no advice, rather than signaling an - ;; error. - (condition-case nil - (ad-remove-advice symbol 'around function) - (error nil)) - (condition-case nil - (ad-activate symbol) - (error nil))) - -) - -;;;; ChangeLog: - -;; 2018-09-15 Thomas Fitzsimmons -;; -;; packages/nadvice: Fix advice-remove behaviour -;; -;; * packages/nadvice/nadvice.el: Bump version to 0.3. -;; (advice-remove): Do not signal an error if the function already has no -;; advice. -;; -;; 2018-09-12 Stefan Monnier -;; -;; * nadvice.el: ad-remove-advice is not autoloaded -;; -;; 2018-09-12 Stefan Monnier -;; -;; * nadvice.el: Fix typo -;; -;; 2018-09-12 Stefan Monnier -;; -;; * nadvice/nadvice.el (advice-add): Add support for :override -;; -;; 2018-09-12 Stefan Monnier -;; -;; * nadvice.el: Fix copyright! -;; -;; 2018-09-12 Stefan Monnier -;; -;; * nadvice: New forward compatibility package -;; - - -(provide 'nadvice) -;;; nadvice.el ends here diff --git a/localelpa/seq-2.24.tar b/localelpa/seq-2.24.tar deleted file mode 100644 index a1412a5949092cd2b380fc9c9fb056adb1b73af4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71680 zcmeHw>2g~~lCFOv<{?f%(F3qe&|D>3Uao10a`TRc)`(PhIINE00dPoS1mMtEwDcRy zgUpM~licsitgNcD0gx>9a+{hSlLV@=s&cE$tgNhg_G@o{XaDev&pzG1UHp4+bo3d1 z>^(TzcRvsK)Bb(7cX;^V=IR3ZM8)Bo$Q zzfL^qlU}dKzrY2_XmUHwCfRh6E=Kt@xyonB$s`@jll{Y;w@L5!y=0ud9SzcPuRod& zN7L(v$$}rZT7dW{zkN3wUEeH{?xU?_e|PV&x4*l8kUW`XS#q9VE#9QFEP0YIr$Yh1 zot#bwJJR)dxwy$^50kUeARnjm^lzhZ`r#y`D{3U$Z0RL z=^&d6lz&f0nE7a$0aC_k_lDW)Z2bN8bh(qyuBFYz&1jxnjmBAmpSS63k>pp&k7qA* zojM!;C)-?c_H#G;hsnEqnGDitGRuae`C>NeFBbr{NYd%>3t(%K4@Xz;I7xhlg=Vv4 zag!yBY&My5B>qA0KW3O^I!>M~`{U6dc{&9_o3ev=K8zQAFRW(;N{d_PNG zXR|p8c3@*TfZItv<7B$&f-|2b`7H^%h3URa#wn&{U29ez7C1~sQDBo>G-a&R=hHTedQNpsHG2Bxs2 zv+E_%MR>vig>S(TqZx#KHivv#OH7MUC{`XiAZgoqHpnq;YYfgJhH`ufIPv6#)#_ms z$Ma+zW*}&Hm|a0J&2o<28|L_L(w*OCgXCXl8D`nfhwrvpNz#6r=eK1f?6l5DlhGK4 z%OX!MhdJ@x5s{-pf*_{1DpL5mNQ>{KP#pG2zlyLH4}K24sMc6 zcLMVN`N@B5iwaoIrdZI+?$tEuvIoA{5+FM}J6ml`919#^hFQ|Z{y@U)ZSk}I@;d2u z=kF%{e0}>sv_t>{<)<~-{RuC;|yB}{yV(}3w9?UZBM%H`6sDgP=6eTJB z0m~3g@;aT3Qh=OON{*lZcupyzdT>G{dq0Hb#mU*D6MiwpMi|K;gi3l*kkj!z2bjTl zIaHN)DWi6hNjiH)J(Q2f`5RT|X<{R{rs)LDbLs&v$DD!a6_sHbeWzAT_q~`d1hq~E zi8YK+k{x_nFFDedoG(UMktesm7xBP8261-QqbP7eFM9@>dU~qHTuxP8&cFZ!BG8j5 z;kTb>3n(RS70bT=Dq5}(nTE$`LA#Gz!I`JZPU}K3v;-r=){8bAfFSnfg4K7hday?r z0YNQn>}T3znq7mPU^l?_%&tKn>|=k)X8E>n&UQ1IW;RspQ+a&zZ8A-E4M#S0ks!*x zQ!e(owG#pQwbvU07d`5qz?*^(ic3j<0)#0g4=tct1zC|7V09v-*umZktrgd9 zgYY9%uqw25yFf6HeETBTn>1X_@`-S)^0;l@GStY4gCaW)R5QUC)OD2v{FIe^DEr_9wrkUgNpqrHu=De@j* zd)2)jhxxSk*Tkc+p)6?wJc^7O^C$=QJ_L{OndvTK`!|E#fEM+zC?#mm6oxY~`rFVb ze5Lka_ExvVqCx0B_4^ShKmIlGZ81meIM_DE>7+kQlP*jalj&eu6n+!!OJkoa2&-M+ zkxYY`99$fPr7^Q4Q|gEC2#mCI`iECdreQlF6arzeKu^>cFYzGeZq{i zotK-j*eBc0SWF5ik#%`wa^nVVxrW%LBqs=rUf4Q~cMIeaes*?ZIvA8_XxDoxL6_@J!K<~f{}HCqZvpu`kTlc2PkG_*GwtQxl?bL)<+8<7`8 zn_v%hh^$_JmZh&08~l64VjZ>|Z}{aFHweF0pymy^Oq)mQqr147>Z4Cj@he0^W=PKg^H;k4_O1a?v1g-W9_{o=YE}JUM>x^a9L$ zmLsAhVj$ZFXWc2i@Bx&r!v=W0%GAQe%Fv7`#Oy5A=Q=!)N2be3pAPj^SmOt= zaAZX4*ahoimCSF(4PrI8Rehf9A;bhX#mlq%tAGMi($ug%R$_u1u>i4PO*}oLoq}G6 zM5o`3=2?g>H2AR7CD_HlDQ&vnCh$`6SxtzbNVR#6v$8eJi}(pz!7{Z$G}^}V02jdz z>Y7W7S=aW#4~@wPk+~x@EMDi&mCC7(&2TH2_zoj#hMJyd2k9CVeHXTL290bW=tFyI zJMR_2pxg|X?gH_n9=O;#uyGZ+z5ct%%;IpN1J(LD31C=?&?)C2hUhs@ZQVh|PU}Q9 zWHI`fB09gok=q=yG|@1kw=4P?K12X91I1cOOPqoDuO~P+mxBgB28A=9B0A=dSCj`m z(!AT1CVfS3Fr*7=#jDovz5M1I*`kuP49EAVw(S$o{(JlqC{PebgvxntJQqpO=pyZ% za73}d;mlPb;3#{TgcEHWitG&C1tHpq<#Iba;r@8SKV=7;;{X!7qvHPJk zHH-k23s$(4?j8AJG>-8~lgZop#r1Km9t~Xp(upVcIs$DA5{Dz$5>gt`0=?Fge%!VU zb$cF(`zsM-tHHvIY4^m4wZHbkvx0W92PDvH`hdp8bvhd&pf0?N!~SgcQ836uNl|xx zJ2p(>>~X@wD)O%92V3+$k0vL34xL+D9tV3K|IJVtTd#$>Cs6mRQ1{FF;WnSc*@A2n z(B=%Po-9Ok18$}JK@RX~P5ur&Y7G;?NhATlXsyrh z^oFxzV*6z|9r4uG2(d~_q;e2TCE2vysrNfv;^da6;5;c{5MhXI&?$yF6D;IdR*tTq zo`aY`!XrHqdSKS7F$$Ymr)&C9_|b4(A!k2gc5R1diBw%olQjc+6F40Bk_% zP#bqOHzIP*ldh&nL?2OG;4DkBD7+YGv?SsB1ApU;Tuz{Ej^P`3285*`;yHs!_LhMl z?DxprY5$NSw}|c+%`%7sQcw`V`yEm@em8H&14Zy4N>rURdvNy5AG@N}AUg8d=lVlr zNk+w->P*TaNSbTkxDh=(rFr*r_c`(AAY!nUu#3RecK^7Rq0E16{TZ0kl z9jk&ec&B!deC1~mW2y^gcB8zQ&X>2hMW=(j3_f=HS(Hz#z9Ej(v{UB4h-vO}Z_r^* z4r)IN4Vd);`>k{?%;VsTpP!vbB+(BfvCVVnZG4lMrA*rxnncg+DQ}v{0(n!pw{$6n zE-1_hqr99ly+)vzspPY3ZU-rpC;bVbUiVU=KsXuRhne?USI0>i(YlvTm-t3?Z8&%U z<2;VQjJF z>6kOo2>QCxtjwQJ3nw#@0Omb3%oI>=VJ~O~9UOiz5xEj;N0N%LaaV$}0cUw>j zt}P~FnECk)@+O>%6*fV#NHAr?<|kyLSXy+=))8$lZi8d1;=wp_qLontr+5mf!zDy5 zW+hCGOqfKxpD_|&7H+N@manAka)16+9O>0QbOZ4u#!e|mW)`9jeMA6BP48ase-u}l zII`0}bCT^$$4jS4lN_3}2UMblQt-#V_yA>B!MgCLN*_H19u8HYCh~Flb0LH|5=3OV zfig#|S&@`~mCbll;NzlRu+F4+@}35O{#MBM+}}unGMbbSe=QP-mMO+KLAwzAJ{Akc z?+&oKYO;`BcEWiEy+`|vn^vt(6J5r0C$VcZdz_8kW`vk&>s9t{j^HLPlQ0c2a}iYN zL2d2)>JWHSSs;)4=vz@7sG*oc48S1>=l29gCW;j34l-=d-E=0D?J%D1VpX z{Wf<7s^8<6^o^N#K4F#y%qFL(V%rP+YDwYdF%~hd7C8(t++33M@3|@@H8}5eB)^m> z5E|Q}%;+t$0n<gBYh5!yd-ly zMHrh20uqUc4j}x=sUnf#C*rPRFFJ{{Aj7#yPRK)$E^QTMg#p>O*+K)#8Wg%SpzTW< zhDEwW$=njqD`&O~zse$?o}OQSkB)4TIrt-Wg+FD&pTsmbK|ukOdAF1QQW3 z80tpT5aD7cZ}mG;+CJg+cpa&t)Z6GlT);)L?~HdJ4=yI8ksi7eHKj0Vsvk*C*K1R} zg&b>#y(luMSmLJ0ypiZYn@*}s-*l#+PMg!T2;a$Xh0R6$C{3`|Y~&Rvk&WgzW>~*3 zLWF$<3Gd5J8z2e>!EfUyIW&kf+9T|FjS(>wF&LsWQ6{{%g4Z*aWf(G+(V)-Iau@EAZFIPrP)M&xzKYwT!@f=kUb-b;TGHN}&=rWtee@-Fh)y+SkDg*C9 zxikk!2qa#9NC&TQn|KHx)L-%9X%o*)p^-%CL6>yo5-G&5eDlj)YAM}N>XvSR`D=@I zhl!K6{XRJC_AN)|8v;q>hB=@~AQLHONqI%0B~d@{}6Bz_Dy zOC#9D)Px#GX<1j*@2WxdG}vD`0(D_TF0fp=Rwlt+rK#)=dN);Wl@SA;G%hp~b(Kye zbXUze_VJ(LQ(Pqnn;dYW-abzlks z&Kj#a!O;R`PDeuI@^p0ytB>vLks(w=^T8^~OgZzjiB8I5bSd?^-__fIL8%EMt9meY z-+C#6hOZIs=v^VW)*I)8S3UUiQUW9C5_|1q*8;lGwO~L&TW~{=JZpFKuSQ@O|8QtK5kAK?)OdJ9srA>h6r|dma6y^8GMJNBHXqE|z*qbm^W%GlqHPTe|q# z5%*F{UHOgiB2=1uYkP;dtZFz}6Cu!}=%k3O>9^}R5HCKQZXv-zA+Rcs8E|2_=$Ot4 zh$JM|%*>7pt^0e=O#y`2>EG+-7oAFKWXTwzfHpg({NTwcfG|1zd)?&p5YO8o%A|Gx zaV3cow+Wb!G7w4N)fh+R*qiuqf64H&?2)ifPN!;G{O^DN&;HRiGCW2p=)JzX?B8i} zg`Hh;dJWzi?0m~bj$=Be$K#my21RzpQfNnNN#JOWN(pj%ljpB6RVjTV15#%7Q|0tk zszhkw%6n*(Z~t%$wo+Uw2nYT1woPY9Ng7^`)e>!iT?akIN@U!f?Hkx?cm^We-HKFV)`E*?4kbOUH!LD z&jF^?jp$MW?t;2NoN3#D=imqNxF6M3cH28zQJ3rkE4$rQa67Po!CeKnn)-P3W&Y7B zxE18eNfhh6X+f|VoZ1mipF22+gQMa9gbX^@LTo&V`iqkGUJ~<$YAT1F`}is>MLN|9 z3w22nBTT~R^psw#Gr9Xq6nD_fWt>MLdnB$+Bq~9?r2#I>#<#mHrK`&lL7XsBQBc23 zf{B~f0jnweHF6ir0kKqgDEh`1e7JPSwWtiG_tDv{P{B*%3-DJ_f{3ml>ls->i;rs+ z+*uDZu92ljZXrEyd%nf!ksma~ISR03TrE#kjSqD6NX5=LCFdJ(V)qEaG)bz#?bx}y zhohN8;=9>3d0rC&=|T#S)-t54DK~4F6ha7^Z&kW72o}VFS>#3TNq$xQ$-ZV>1#g#I zM_|Ps%R1sv93CuFyDC#VJ-ayh@#MLjzoDhNXe94Aza}5LeykS9yP4)vmj=m)!_CG( zLmfhrVoFeSsy0&@4YG$)p-2M2DXq1Z^Dzytba}zQDtwrKr;8K^RNXuT7*f6K3Q2Yy zO2LZ?hfIkKcl_9-rQJgc1%-f_O*-0Dqo||YB`VB~ld>aE2|rm<8<$a9M|0af5K3CF z!p`R42(5_16+?4ma!D=SB(G1xVyk8*R7;N54l2+v74l_if|6$MZ#f7ieQS`5BrC4S zbzn5B+?vb*N)_8va?7&s(-xD1EwGm zj!Bh-f95@rWMEb2P40~T6?wACztQTV8CS+oId2+-pO>(PkE(LvSD>eE5=;{AgRrkq z3-1TJ2|Mf9-0E6!A**Jy2NkuNSY#8w)TZeOcSeNbuSG_#603sLRU-wGmGKwRyZ|7k zifY$&xF|z@uL*j-T}2t#7|3(MDHU=FRS(td3WY{yjZia;YGtB?b>N-G9Kwq1ut|?X zPQ8a_hkkYY)?a&uoh~kkCxaxu;pQjW>Ec^n5$n@N=nkMPQCAi^a1|k}DAGEXxSu>o zzD&MKxTO^}aKv^F*a|fRb$lOiWMh19FrKpjgG+|Yx+Sj% zzj~o7@osgn)=gp~ygOj4r?UoQO)z4lH;}G&54HtHavM`X_Lg;vUqOMU=?3)(CRs@V zNh?{uiEl&zg|!O*kitf0ZRi~^U$J^3nRCl%K(n*RR&9LmKd}hMgx_c@+0BS`WNz>e zoGIv_-0R@Bt9l4k9aP`+2=*nc*LA~J5uiJEb>JG^SKIW#R`OsKV&tfKt@(W@7D>fI z=4LDaRNB0WUi14RBuD@m{Bb zA1I#sK`L#T-m*z+xKvKAV*0rC`tjL{r=+M^HqBW6r^%`E*KyX$1i}c(QF6bGa&m_U zfzv?^_4IWH^f%L9^ z6H=^Cg~4=Bfdba3-eiz;lq8moXRd2FQ+oKXoUBiTUgXOOc(K`LLHW@)%dhn z_Ue`;XEB=k3U*)}ZU!^gO|rBp1s1jV)kajG zUgw*nJ?N)b?X9Gp_6PVW2r@ksSw_A+LqL1*T@ZEsq z?4?(=R`zOhW)gaD2q9^0oONX7aJ0AY<7Y{~psIjIqbVQ)jd;b_9gwpA1 zJO#g){I3qL6sBXxYB3-%l}F!K$q0|iWE7TLt0P0>PFG9^RYBOT;QJobOe5eI_*Y{ip>l*~B^}gVj>Ko( zsK1m({qx#Uwa1^6U!1c11)~Z+HXpT(Yc=CMLC*Mi2pzg+nP`pl?(rD^$rJqN4fJk% z#y^K*9z^3tLLOt5=Uosd<!%8`8eGI(?tl0#$Z~4M7b%veNdcHZfsA8dovDD9SV*6+ESWI zYiW)igrWV|wZT?1rLoouFQ6*ms+NrnC{&$V7pjlk0?K&iRdn87mt8FGTC-lTlr<-C z{%cy9B+~1$aP@#S8QfN&`Xbg@dX6IFOE1^ZRSnW<_*s`%>>6{sDlbq?4*f=`?*ygw6F7C1Udo^U?NB1C|GQkNGHsTUR@QxC1`joZPnEmPxrw%{mW=(5U0B?L}1mjP#94cZ2A+PpmA5 z3Rrh+pEyPf#cwI#D4;`x8*&eY&{Z3#JXw@n(f_6F+%!|^>v@$szu>62=0Rp_4@@gmTxmqL? z#_Ocowu-U_;KjG%QbaG^SQxDVpYW@3Ut4hL&Y-l6jNnMVyw|blxXgJK7dXwKTW)!U z&JS=G`iZ)tjUk4HS{wKhY=?=3v4u4nKt|0`ofm&8ZUevqQ~-6Yh!uX-xTK!!*}KX9 zZgK$5-c62-c_ZZ%`8RIPdb-V7HSO211b{1839woqIzA-1DOe4w5P$sFL|aC`r39Uj zS2U)ms$be~;kmxm|GaHi_Lyiz214U5{`xV>OgCT|upM9yLD0jaHt6knwwi(QL3pSuMd0Ff9Ws*U>1xxA!gV-<{;%-n|M+B98Mi zBtdhi5=5cKdq%GUjwPDUlNG>g#vuLN9_ZUMHCCJ%(h6OK(D4$7pkt~BMF+9j43{cl z&^_s=Gd-*-2&`HO;#ZFeR(!z(zm~Zax=-}0x!^wLM}Ku?W6r?EonZqkW^Unyd(1qd z#6^$Ft+#*L+svYgclHmzz%L*3&S!jcarOPr-Gjq}UEKdXd~kG#-*yk>{_pPI-TluP zho9EQRKt$?T`|(ZE+tn^R8QE{W%0@Y2SzKZ8Qp!RP$y7^Y{? z0IZLU+HF@O-f7^WO45dC_1bZ34=i-UVbUu;7r=cVR}VD+%i5Gf7l%ga5*_Y|E9Qn* zc+yuaADc$?332Hc&u^jNGr!Px4ZH$y)tXbz*#?#u@!W%Sgr{|)<8N^U---It7~B4qnjFQ z>_3!!iS555+J8s)@9e*iPKP>xkTKl|d7J-MjU|+Yn-69q)C`8gmZG&_4vL3Zf72Xs z6I7t%eli-vvQq1hHPw*++%p*56%gj29flw0rbX*_sCLrf5&1*F#B*#(`>WwsS4VsO z-RxkH9(|SVriWjC`Q`53?)}4~z5T1hFCToF4!+vk?YEPM%h@<--z*ll^M_x2fpVbf z&h>P;lh3Zda5I^UYjd-hASK};s%PP?8iq%@?X%G!$0H`mvuv@*r){|3?WunB{jGeo zb45=!c{ss?pnS;!l}6>|Beql>G(?W;_*lyz?+gream+;uj|gs`#baUptt{YgLi7jc`6Um+D_z(s3!Y|J8${W zCTgJL3VxPcXE^AJZBe}9%S%V7fW$pu$~#Q*Vlo_Lc{YIofH~Z2_{#8~qn^6Qp$CJhOEO7rFVacD2InYeSX+0wYT8%4vdz((Busl9?J0#}0BZZ5!32iJ?)W~=^ zg&nlFf5ZzFfb1MAtHQ#g^!Tn%0+3;xcrPl86m{%gOLVbNw zK;=0+GDxcGqY$T8czY0J{7W{=`C^W}ILPPZ7t7g{bd+M?(&}2Ona$V`HoHE2PWbL1 z(n0loR9iwEIU17_J%%$33&2+N>IR-g*hRf>34(Ee#O>KKj(;i%=NeCT|)C^|A2_gu_~5fVyb<{ul!qvNh@xOs6FKyz<%x)RInhxG2Z=itZZ?=@*Hy!Nn&T;B7c#V`mZ&e%@lwNy zB6$+gWSKmv_RKj+y*sb~pRuu8-5B>A59;7Rmn|-bIm4PAS-%V3u|yns8ebPfp87U} zrgr+5P`ez_CbcCxz(4d`7J4RhtAr!D>fNNDkMW)?-t&&$%jKVOHN8tYQAGiCFN&;+ zheCZQ8t9Hxy-+&&chV*wmrCd%e#Wmrb{sFZFLGk2L+Eb9_ zOBagdQ?t;b(b{pQR9pp2- zX>f}-*QvQ{UI)N9CvO)s*5u9@_r>E{`a}m

;d5Rp^s;s7!~)o3l4y)G!zVuGb^% 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 diff --git a/localelpa/spinner-1.7.3.el b/localelpa/spinner-1.7.3.el deleted file mode 100644 index 90aec8f4a7..0000000000 --- a/localelpa/spinner-1.7.3.el +++ /dev/null @@ -1,406 +0,0 @@ -;;; spinner.el --- Add spinners and progress-bars to the mode-line for ongoing operations -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 Free Software Foundation, Inc. - -;; Author: Artur Malabarba -;; Version: 1.7.3 -;; URL: https://github.com/Malabarba/spinner.el -;; Keywords: processes mode-line - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: -;; -;; 1 Usage -;; ═══════ -;; -;; First of all, don’t forget to add `(spinner "VERSION")' to your -;; package’s dependencies. -;; -;; -;; 1.1 Major-modes -;; ─────────────── -;; -;; 1. Just call `(spinner-start)' and a spinner will be added to the -;; mode-line. -;; 2. Call `(spinner-stop)' on the same buffer when you want to remove -;; it. -;; -;; The default spinner is a line drawing that rotates. You can pass an -;; argument to `spinner-start' to specify which spinner you want. All -;; possibilities are listed in the `spinner-types' variable, but here are -;; a few examples for you to try: -;; -;; ‱ `(spinner-start 'vertical-breathing 10)' -;; ‱ `(spinner-start 'minibox)' -;; ‱ `(spinner-start 'moon)' -;; ‱ `(spinner-start 'triangle)' -;; -;; You can also define your own as a vector of strings (see the examples -;; in `spinner-types'). -;; -;; -;; 1.2 Minor-modes -;; ─────────────── -;; -;; Minor-modes can create a spinner with `spinner-create' and then add it -;; to their mode-line lighter. They can then start the spinner by setting -;; a variable and calling `spinner-start-timer'. Finally, they can stop -;; the spinner (and the timer) by just setting the same variable to nil. -;; -;; Here’s an example for a minor-mode named `foo'. Assuming that -;; `foo--lighter' is used as the mode-line lighter, the following code -;; will add an *inactive* global spinner to the mode-line. -;; ┌──── -;; │ (defvar foo--spinner (spinner-create 'rotating-line)) -;; │ (defconst foo--lighter -;; │ '(" foo" (:eval (spinner-print foo--spinner)))) -;; └──── -;; -;; 1. To activate the spinner, just call `(spinner-start foo--spinner)'. -;; It will show up on the mode-line and start animating. -;; 2. To get rid of it, call `(spinner-stop foo--spinner)'. It will then -;; disappear again. -;; -;; Some minor-modes will need spinners to be buffer-local. To achieve -;; that, just make the `foo--spinner' variable buffer-local and use the -;; third argument of the `spinner-create' function. The snippet below is an -;; example. -;; -;; ┌──── -;; │ (defvar-local foo--spinner nil) -;; │ (defconst foo--lighter -;; │ '(" foo" (:eval (spinner-print foo--spinner)))) -;; │ (defun foo--start-spinner () -;; │ "Create and start a spinner on this buffer." -;; │ (unless foo--spinner -;; │ (setq foo--spinner (spinner-create 'moon t))) -;; │ (spinner-start foo--spinner)) -;; └──── -;; -;; 1. To activate the spinner, just call `(foo--start-spinner)'. -;; 2. To get rid of it, call `(spinner-stop foo--spinner)'. -;; -;; This will use the `moon' spinner, but you can use any of the names -;; defined in the `spinner-types' variable or even define your own. - - -;;; Code: -(eval-when-compile - (require 'cl)) - -(defconst spinner-types - '((3-line-clock . ["─" "┘" "┮" "└" "├" "┌" "┬" "┐"]) - (2-line-clock . ["┘" "└" "┌" "┐"]) - (flipping-line . ["_" "\\" "|" "/"]) - (rotating-line . ["-" "\\" "|" "/"]) - (progress-bar . ["[ ]" "[= ]" "[== ]" "[=== ]" "[====]" "[ ===]" "[ ==]" "[ =]"]) - (progress-bar-filled . ["| |" "|█ |" "|██ |" "|███ |" "|████|" "| ███|" "| ██|" "| █|"]) - (vertical-breathing . ["▁" "▂" "▃" "▄" "▅" "▆" "▇" "█" "▇" "▆" "▅" "▄" "▃" "▂" "▁" " "]) - (vertical-rising . ["▁" "▄" "█" "▀" "▔"]) - (horizontal-breathing . [" " "▏" "▎" "▍" "▌" "▋" "▊" "▉" "▉" "▊" "▋" "▌" "▍" "▎" "▏"]) - (horizontal-breathing-long - . [" " "▎ " "▌ " "▊ " "█ " "█▎" "█▌" "█▊" "██" "█▊" "█▌" "█▎" "█ " "▊ " "▋ " "▌ " "▍ " "▎ " "▏ "]) - (horizontal-moving . [" " "▌ " "█ " "▐▌" " █" " ▐"]) - (minibox . ["▖" "▘" "▝" "▗"]) - (triangle . ["◱" "◣" "â—€" "â—„"]) - (box-in-box . ["◰" "◳" "â—Č" "◱"]) - (box-in-circle . ["◮" "◷" "◶" "â—”"]) - (half-circle . ["◐" "◓" "◑" "◒"]) - (moon . ["🌑" "🌘" "🌖" "🌕" "🌔" "🌒"])) - "Predefined alist of spinners. -Each car is a symbol identifying the spinner, and each cdr is a -vector, the spinner itself.") - -(defun spinner-make-progress-bar (width &optional char) - "Return a vector of strings of the given WIDTH. -The vector is a valid spinner type and is similar to the -`progress-bar' spinner, except without the sorrounding brackets. -CHAR is the character to use for the moving bar (defaults to =)." - (let ((whole-string (concat (make-string (1- width) ?\s) - (make-string 4 (or char ?=)) - (make-string width ?\s)))) - (apply #'vector (mapcar (lambda (n) (substring whole-string n (+ n width))) - (number-sequence (+ width 3) 0 -1))))) - -(defvar spinner-current nil - "Spinner curently being displayed on the `mode-line-process'.") -(make-variable-buffer-local 'spinner-current) - -(defconst spinner--mode-line-construct - '(:eval (spinner-print spinner-current)) - "Construct used to display a spinner in `mode-line-process'.") -(put 'spinner--mode-line-construct 'risky-local-variable t) - -(defvar spinner-frames-per-second 10 - "Default speed at which spinners spin, in frames per second. -Each spinner can override this value.") - - -;;; The spinner object. -(defun spinner--type-to-frames (type) - "Return a vector of frames corresponding to TYPE. -The list of possible built-in spinner types is given by the -`spinner-types' variable, but you can also use your own (see -below). - -If TYPE is nil, the frames of this spinner are given by the first -element of `spinner-types'. -If TYPE is a symbol, it specifies an element of `spinner-types'. -If TYPE is `random', use a random element of `spinner-types'. -If TYPE is a list, it should be a list of symbols, and a random -one is chosen as the spinner type. -If TYPE is a vector, it should be a vector of strings and these -are used as the spinner's frames. This allows you to make your -own spinner animations." - (cond - ((vectorp type) type) - ((not type) (cdr (car spinner-types))) - ((eq type 'random) - (cdr (elt spinner-types - (random (length spinner-types))))) - ((listp type) - (cdr (assq (elt type (random (length type))) - spinner-types))) - ((symbolp type) (cdr (assq type spinner-types))) - (t (error "Unknown spinner type: %s" type)))) - -(defstruct (spinner - (:copier nil) - (:conc-name spinner--) - (:constructor make-spinner (&optional type buffer-local frames-per-second delay-before-start))) - (frames (spinner--type-to-frames type)) - (counter 0) - (fps (or frames-per-second spinner-frames-per-second)) - (timer (timer-create)) - (active-p nil) - (buffer (when buffer-local - (if (bufferp buffer-local) - buffer-local - (current-buffer)))) - (delay (or delay-before-start 0))) - -;;;###autoload -(defun spinner-create (&optional type buffer-local fps delay) - "Create a spinner of the given TYPE. -The possible TYPEs are described in `spinner--type-to-frames'. - -FPS, if given, is the number of desired frames per second. -Default is `spinner-frames-per-second'. - -If BUFFER-LOCAL is non-nil, the spinner will be automatically -deactivated if the buffer is killed. If BUFFER-LOCAL is a -buffer, use that instead of current buffer. - -When started, in order to function properly, the spinner runs a -timer which periodically calls `force-mode-line-update' in the -curent buffer. If BUFFER-LOCAL was set at creation time, then -`force-mode-line-update' is called in that buffer instead. When -the spinner is stopped, the timer is deactivated. - -DELAY, if given, is the number of seconds to wait after starting -the spinner before actually displaying it. It is safe to cancel -the spinner before this time, in which case it won't display at -all." - (make-spinner type buffer-local fps delay)) - -(defun spinner-print (spinner) - "Return a string of the current frame of SPINNER. -If SPINNER is nil, just return nil. -Designed to be used in the mode-line with: - (:eval (spinner-print some-spinner))" - (when (and spinner (spinner--active-p spinner)) - (let ((frame (spinner--counter spinner))) - (when (>= frame 0) - (elt (spinner--frames spinner) frame))))) - -(defun spinner--timer-function (spinner) - "Function called to update SPINNER. -If SPINNER is no longer active, or if its buffer has been killed, -stop the SPINNER's timer." - (let ((buffer (spinner--buffer spinner))) - (if (or (not (spinner--active-p spinner)) - (and buffer (not (buffer-live-p buffer)))) - (spinner-stop spinner) - ;; Increment - (callf (lambda (x) (if (< x 0) - (1+ x) - (% (1+ x) (length (spinner--frames spinner))))) - (spinner--counter spinner)) - ;; Update mode-line. - (if (buffer-live-p buffer) - (with-current-buffer buffer - (force-mode-line-update)) - (force-mode-line-update))))) - -(defun spinner--start-timer (spinner) - "Start a SPINNER's timer." - (let ((old-timer (spinner--timer spinner))) - (when (timerp old-timer) - (cancel-timer old-timer)) - - (setf (spinner--active-p spinner) t) - - (unless (ignore-errors (> (spinner--fps spinner) 0)) - (error "A spinner's FPS must be a positive number")) - (setf (spinner--counter spinner) (round (- (* (or (spinner--delay spinner) 0) - (spinner--fps spinner))))) - ;; Create timer. - (let* ((repeat (/ 1.0 (spinner--fps spinner))) - (time (timer-next-integral-multiple-of-time (current-time) repeat)) - ;; Create the timer as a lex variable so it can cancel itself. - (timer (spinner--timer spinner))) - (timer-set-time timer time repeat) - (timer-set-function timer #'spinner--timer-function (list spinner)) - (timer-activate timer) - ;; Return a stopping function. - (lambda () (spinner-stop spinner))))) - - -;;; The main functions -;;;###autoload -(defun spinner-start (&optional type-or-object fps delay) - "Start a mode-line spinner of given TYPE-OR-OBJECT. -If TYPE-OR-OBJECT is an object created with `make-spinner', -simply activate it. This method is designed for minor modes, so -they can use the spinner as part of their lighter by doing: - \\='(:eval (spinner-print THE-SPINNER)) -To stop this spinner, call `spinner-stop' on it. - -If TYPE-OR-OBJECT is anything else, a buffer-local spinner is -created with this type, and it is displayed in the -`mode-line-process' of the buffer it was created it. Both -TYPE-OR-OBJECT and FPS are passed to `make-spinner' (which see). -To stop this spinner, call `spinner-stop' in the same buffer. - -Either way, the return value is a function which can be called -anywhere to stop this spinner. You can also call `spinner-stop' -in the same buffer where the spinner was created. - -FPS, if given, is the number of desired frames per second. -Default is `spinner-frames-per-second'. - -DELAY, if given, is the number of seconds to wait until actually -displaying the spinner. It is safe to cancel the spinner before -this time, in which case it won't display at all." - (unless (spinner-p type-or-object) - ;; Choose type. - (if (spinner-p spinner-current) - (setf (spinner--frames spinner-current) (spinner--type-to-frames type-or-object)) - (setq spinner-current (make-spinner type-or-object (current-buffer) fps delay))) - (setq type-or-object spinner-current) - ;; Maybe add to mode-line. - (unless (memq 'spinner--mode-line-construct mode-line-process) - (setq mode-line-process - (list (or mode-line-process "") - 'spinner--mode-line-construct)))) - - ;; Create timer. - (when fps (setf (spinner--fps type-or-object) fps)) - (when delay (setf (spinner--delay type-or-object) delay)) - (spinner--start-timer type-or-object)) - -(defun spinner-start-print (spinner) - "Like `spinner-print', but also start SPINNER if it's not active." - (unless (spinner--active-p spinner) - (spinner-start spinner)) - (spinner-print spinner)) - -(defun spinner-stop (&optional spinner) - "Stop SPINNER, defaulting to the current buffer's spinner. -It is always safe to call this function, even if there is no -active spinner." - (let ((spinner (or spinner spinner-current))) - (when (spinner-p spinner) - (let ((timer (spinner--timer spinner))) - (when (timerp timer) - (cancel-timer timer))) - (setf (spinner--active-p spinner) nil) - (force-mode-line-update)))) - -;;;; ChangeLog: - -;; 2016-11-17 Artur Malabarba -;; -;; Merge commit '0637791f005f747532b4439439a81c3415961377' -;; -;; 2016-07-11 Paul Eggert -;; -;; Fix some quoting problems in doc strings -;; -;; Most of these are minor issues involving, e.g., quoting `like this' -;; instead of 'like this'. A few involve escaping ` and ' with a preceding -;; \= when the characters should not be turned into curved single quotes. -;; -;; 2016-04-01 Artur Malabarba -;; -;; Remove reference to thread-last -;; -;; 2016-02-08 Artur Malabarba -;; -;; Spinner version 1.7 -;; -;; Offer a spinner-make-progress-bar function. Make spinner-stop never -;; signal. Allow floating-point delays. -;; -;; 2016-02-07 Artur Malabarba -;; -;; Update the mode-line after spinner-stop -;; -;; 2015-08-11 Artur Malabarba -;; -;; Merge commit '8d8c459d7757cf5774f11be9147d7a54f5f9bbd7' -;; -;; 2015-05-02 Artur Malabarba -;; -;; * spinner: Rename constructor. -;; -;; 2015-04-30 Artur Malabarba -;; -;; * spinner/spinner.el: Rewrite spinners as structures -;; -;; 2015-04-09 Artur Malabarba -;; -;; spinner: Fix readme -;; -;; 2015-04-09 Artur Malabarba -;; -;; spinner: Fix leftover mode-line-format code -;; -;; 2015-04-09 Artur Malabarba -;; -;; Merge commit 'c44ef65515f50bd38304a6f50adebc984fb8e431' -;; -;; 2015-03-07 Artur Malabarba -;; -;; Merge commit '7eca7d023c95bc21c7838467b3a58d549afaf68d' -;; -;; 2015-03-07 Artur Malabarba -;; -;; Merge commit 'a7b4e52766977b58c6b9899305e962a2b5235bda' -;; -;; 2015-03-07 Artur Malabarba -;; -;; Add 'packages/spinner/' from commit -;; '9477ee899d62259d4b946f243cdcdd9cdeb1e910' -;; -;; git-subtree-dir: packages/spinner git-subtree-mainline: -;; 5736e852fd48a0f1ba1c328dd4d03e3fa008a406 git-subtree-split: -;; 9477ee899d62259d4b946f243cdcdd9cdeb1e910 -;; - - -(provide 'spinner) - -;;; spinner.el ends here diff --git a/localelpa/undo-tree-0.7.4.el b/localelpa/undo-tree-0.7.4.el deleted file mode 100644 index 9fa1fc608d..0000000000 --- a/localelpa/undo-tree-0.7.4.el +++ /dev/null @@ -1,4751 +0,0 @@ -;;; undo-tree.el --- Treat undo history as a tree -*- lexical-binding: t; -*- - -;; Copyright (C) 2009-2020 Free Software Foundation, Inc - -;; Author: Toby Cubitt -;; Maintainer: Toby Cubitt -;; Version: 0.7.4 -;; Keywords: convenience, files, undo, redo, history, tree -;; URL: http://www.dr-qubit.org/emacs.php -;; Repository: http://www.dr-qubit.org/git/undo-tree.git - -;; This file is part of Emacs. -;; -;; This file is free software: you can redistribute it and/or modify it under -;; the terms of the GNU General Public License as published by the Free -;; Software Foundation, either version 3 of the License, or (at your option) -;; any later version. -;; -;; This program is distributed in the hope that it will be useful, but WITHOUT -;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -;; more details. -;; -;; You should have received a copy of the GNU General Public License along -;; with GNU Emacs. If not, see . - - -;;; Commentary: -;; -;; Emacs has a powerful undo system. Unlike the standard undo/redo system in -;; most software, it allows you to recover *any* past state of a buffer -;; (whereas the standard undo/redo system can lose past states as soon as you -;; redo). However, this power comes at a price: many people find Emacs' undo -;; system confusing and difficult to use, spawning a number of packages that -;; replace it with the less powerful but more intuitive undo/redo system. -;; -;; Both the loss of data with standard undo/redo, and the confusion of Emacs' -;; undo, stem from trying to treat undo history as a linear sequence of -;; changes. It's not. The `undo-tree-mode' provided by this package replaces -;; Emacs' undo system with a system that treats undo history as what it is: a -;; branching tree of changes. This simple idea allows the more intuitive -;; behaviour of the standard undo/redo system to be combined with the power of -;; never losing any history. An added side bonus is that undo history can in -;; some cases be stored more efficiently, allowing more changes to accumulate -;; before Emacs starts discarding history. -;; -;; The only downside to this more advanced yet simpler undo system is that it -;; was inspired by Vim. But, after all, most successful religions steal the -;; best ideas from their competitors! -;; -;; -;; Installation -;; ============ -;; -;; This package has only been tested with Emacs versions 24 and CVS. It should -;; work in Emacs versions 22 and 23 too, but will not work without -;; modifications in earlier versions of Emacs. -;; -;; To install `undo-tree-mode', make sure this file is saved in a directory in -;; your `load-path', and add the line: -;; -;; (require 'undo-tree) -;; -;; to your .emacs file. Byte-compiling undo-tree.el is recommended (e.g. using -;; "M-x byte-compile-file" from within emacs). -;; -;; If you want to replace the standard Emacs' undo system with the -;; `undo-tree-mode' system in all buffers, you can enable it globally by -;; adding: -;; -;; (global-undo-tree-mode) -;; -;; to your .emacs file. -;; -;; -;; Quick-Start -;; =========== -;; -;; If you're the kind of person who likes to jump in the car and drive, -;; without bothering to first figure out whether the button on the left dips -;; the headlights or operates the ejector seat (after all, you'll soon figure -;; it out when you push it), then here's the minimum you need to know: -;; -;; `undo-tree-mode' and `global-undo-tree-mode' -;; Enable undo-tree mode (either in the current buffer or globally). -;; -;; C-_ C-/ (`undo-tree-undo') -;; Undo changes. -;; -;; M-_ C-? (`undo-tree-redo') -;; Redo changes. -;; -;; `undo-tree-switch-branch' -;; Switch undo-tree branch. -;; (What does this mean? Better press the button and see!) -;; -;; C-x u (`undo-tree-visualize') -;; Visualize the undo tree. -;; (Better try pressing this button too!) -;; -;; C-x r u (`undo-tree-save-state-to-register') -;; Save current buffer state to register. -;; -;; C-x r U (`undo-tree-restore-state-from-register') -;; Restore buffer state from register. -;; -;; -;; -;; In the undo-tree visualizer: -;; -;; p C-p (`undo-tree-visualize-undo') -;; Undo changes. -;; -;; n C-n (`undo-tree-visualize-redo') -;; Redo changes. -;; -;; b C-b (`undo-tree-visualize-switch-branch-left') -;; Switch to previous undo-tree branch. -;; -;; f C-f (`undo-tree-visualize-switch-branch-right') -;; Switch to next undo-tree branch. -;; -;; C- M-{ (`undo-tree-visualize-undo-to-x') -;; Undo changes up to last branch point. -;; -;; C- M-} (`undo-tree-visualize-redo-to-x') -;; Redo changes down to next branch point. -;; -;; n C-n (`undo-tree-visualize-redo') -;; Redo changes. -;; -;; (`undo-tree-visualizer-mouse-set') -;; Set state to node at mouse click. -;; -;; t (`undo-tree-visualizer-toggle-timestamps') -;; Toggle display of time-stamps. -;; -;; d (`undo-tree-visualizer-toggle-diff') -;; Toggle diff display. -;; -;; s (`undo-tree-visualizer-selection-mode') -;; Toggle keyboard selection mode. -;; -;; q (`undo-tree-visualizer-quit') -;; Quit undo-tree-visualizer. -;; -;; C-q (`undo-tree-visualizer-abort') -;; Abort undo-tree-visualizer. -;; -;; , < -;; Scroll left. -;; -;; . > -;; Scroll right. -;; -;; M-v -;; Scroll up. -;; -;; C-v -;; Scroll down. -;; -;; -;; -;; In visualizer selection mode: -;; -;; p C-p (`undo-tree-visualizer-select-previous') -;; Select previous node. -;; -;; n C-n (`undo-tree-visualizer-select-next') -;; Select next node. -;; -;; b C-b (`undo-tree-visualizer-select-left') -;; Select left sibling node. -;; -;; f C-f (`undo-tree-visualizer-select-right') -;; Select right sibling node. -;; -;; M-v -;; Select node 10 above. -;; -;; C-v -;; Select node 10 below. -;; -;; (`undo-tree-visualizer-set') -;; Set state to selected node and exit selection mode. -;; -;; s (`undo-tree-visualizer-mode') -;; Exit selection mode. -;; -;; t (`undo-tree-visualizer-toggle-timestamps') -;; Toggle display of time-stamps. -;; -;; d (`undo-tree-visualizer-toggle-diff') -;; Toggle diff display. -;; -;; q (`undo-tree-visualizer-quit') -;; Quit undo-tree-visualizer. -;; -;; C-q (`undo-tree-visualizer-abort') -;; Abort undo-tree-visualizer. -;; -;; , < -;; Scroll left. -;; -;; . > -;; Scroll right. -;; -;; -;; -;; Persistent undo history: -;; -;; Note: Requires Emacs version 24.3 or higher. -;; -;; `undo-tree-auto-save-history' (variable) -;; automatically save and restore undo-tree history along with buffer -;; (disabled by default) -;; -;; `undo-tree-save-history' (command) -;; manually save undo history to file -;; -;; `undo-tree-load-history' (command) -;; manually load undo history from file -;; -;; -;; -;; Compressing undo history: -;; -;; Undo history files cannot grow beyond the maximum undo tree size, which -;; is limited by `undo-limit', `undo-strong-limit' and -;; `undo-outer-limit'. Nevertheless, undo history files can grow quite -;; large. If you want to automatically compress undo history, add the -;; following advice to your .emacs file (replacing ".gz" with the filename -;; extension of your favourite compression algorithm): -;; -;; (defadvice undo-tree-make-history-save-file-name -;; (after undo-tree activate) -;; (setq ad-return-value (concat ad-return-value ".gz"))) -;; -;; -;; -;; -;; Undo Systems -;; ============ -;; -;; To understand the different undo systems, it's easiest to consider an -;; example. Imagine you make a few edits in a buffer. As you edit, you -;; accumulate a history of changes, which we might visualize as a string of -;; past buffer states, growing downwards: -;; -;; o (initial buffer state) -;; | -;; | -;; o (first edit) -;; | -;; | -;; o (second edit) -;; | -;; | -;; x (current buffer state) -;; -;; -;; Now imagine that you undo the last two changes. We can visualize this as -;; rewinding the current state back two steps: -;; -;; o (initial buffer state) -;; | -;; | -;; x (current buffer state) -;; | -;; | -;; o -;; | -;; | -;; o -;; -;; -;; However, this isn't a good representation of what Emacs' undo system -;; does. Instead, it treats the undos as *new* changes to the buffer, and adds -;; them to the history: -;; -;; o (initial buffer state) -;; | -;; | -;; o (first edit) -;; | -;; | -;; o (second edit) -;; | -;; | -;; x (buffer state before undo) -;; | -;; | -;; o (first undo) -;; | -;; | -;; x (second undo) -;; -;; -;; Actually, since the buffer returns to a previous state after an undo, -;; perhaps a better way to visualize it is to imagine the string of changes -;; turning back on itself: -;; -;; (initial buffer state) o -;; | -;; | -;; (first edit) o x (second undo) -;; | | -;; | | -;; (second edit) o o (first undo) -;; | / -;; |/ -;; o (buffer state before undo) -;; -;; Treating undos as new changes might seem a strange thing to do. But the -;; advantage becomes clear as soon as we imagine what happens when you edit -;; the buffer again. Since you've undone a couple of changes, new edits will -;; branch off from the buffer state that you've rewound to. Conceptually, it -;; looks like this: -;; -;; o (initial buffer state) -;; | -;; | -;; o -;; |\ -;; | \ -;; o x (new edit) -;; | -;; | -;; o -;; -;; The standard undo/redo system only lets you go backwards and forwards -;; linearly. So as soon as you make that new edit, it discards the old -;; branch. Emacs' undo just keeps adding changes to the end of the string. So -;; the undo history in the two systems now looks like this: -;; -;; Undo/Redo: Emacs' undo -;; -;; o o -;; | | -;; | | -;; o o o -;; .\ | |\ -;; . \ | | \ -;; . x (new edit) o o | -;; (discarded . | / | -;; branch) . |/ | -;; . o | -;; | -;; | -;; x (new edit) -;; -;; Now, what if you change your mind about those undos, and decide you did -;; like those other changes you'd made after all? With the standard undo/redo -;; system, you're lost. There's no way to recover them, because that branch -;; was discarded when you made the new edit. -;; -;; However, in Emacs' undo system, those old buffer states are still there in -;; the undo history. You just have to rewind back through the new edit, and -;; back through the changes made by the undos, until you reach them. Of -;; course, since Emacs treats undos (even undos of undos!) as new changes, -;; you're really weaving backwards and forwards through the history, all the -;; time adding new changes to the end of the string as you go: -;; -;; o -;; | -;; | -;; o o o (undo new edit) -;; | |\ |\ -;; | | \ | \ -;; o o | | o (undo the undo) -;; | / | | | -;; |/ | | | -;; (trying to get o | | x (undo the undo) -;; to this state) | / -;; |/ -;; o -;; -;; So far, this is still reasonably intuitive to use. It doesn't behave so -;; differently to standard undo/redo, except that by going back far enough you -;; can access changes that would be lost in standard undo/redo. -;; -;; However, imagine that after undoing as just described, you decide you -;; actually want to rewind right back to the initial state. If you're lucky, -;; and haven't invoked any command since the last undo, you can just keep on -;; undoing until you get back to the start: -;; -;; (trying to get o x (got there!) -;; to this state) | | -;; | | -;; o o o o (keep undoing) -;; | |\ |\ | -;; | | \ | \ | -;; o o | | o o (keep undoing) -;; | / | | | / -;; |/ | | |/ -;; (already undid o | | o (got this far) -;; to this state) | / -;; |/ -;; o -;; -;; But if you're unlucky, and you happen to have moved the point (say) after -;; getting to the state labelled "got this far", then you've "broken the undo -;; chain". Hold on to something solid, because things are about to get -;; hairy. If you try to undo now, Emacs thinks you're trying to undo the -;; undos! So to get back to the initial state you now have to rewind through -;; *all* the changes, including the undos you just did: -;; -;; (trying to get o x (finally got there!) -;; to this state) | | -;; | | -;; o o o o o o -;; | |\ |\ |\ |\ | -;; | | \ | \ | \ | \ | -;; o o | | o o | | o o -;; | / | | | / | | | / -;; |/ | | |/ | | |/ -;; (already undid o | | o<. | | o -;; to this state) | / : | / -;; |/ : |/ -;; o : o -;; : -;; (got this far, but -;; broke the undo chain) -;; -;; Confused? -;; -;; In practice you can just hold down the undo key until you reach the buffer -;; state that you want. But whatever you do, don't move around in the buffer -;; to *check* that you've got back to where you want! Because you'll break the -;; undo chain, and then you'll have to traverse the entire string of undos -;; again, just to get back to the point at which you broke the -;; chain. Undo-in-region and commands such as `undo-only' help to make using -;; Emacs' undo a little easier, but nonetheless it remains confusing for many -;; people. -;; -;; -;; So what does `undo-tree-mode' do? Remember the diagram we drew to represent -;; the history we've been discussing (make a few edits, undo a couple of them, -;; and edit again)? The diagram that conceptually represented our undo -;; history, before we started discussing specific undo systems? It looked like -;; this: -;; -;; o (initial buffer state) -;; | -;; | -;; o -;; |\ -;; | \ -;; o x (current state) -;; | -;; | -;; o -;; -;; Well, that's *exactly* what the undo history looks like to -;; `undo-tree-mode'. It doesn't discard the old branch (as standard undo/redo -;; does), nor does it treat undos as new changes to be added to the end of a -;; linear string of buffer states (as Emacs' undo does). It just keeps track -;; of the tree of branching changes that make up the entire undo history. -;; -;; If you undo from this point, you'll rewind back up the tree to the previous -;; state: -;; -;; o -;; | -;; | -;; x (undo) -;; |\ -;; | \ -;; o o -;; | -;; | -;; o -;; -;; If you were to undo again, you'd rewind back to the initial state. If on -;; the other hand you redo the change, you'll end up back at the bottom of the -;; most recent branch: -;; -;; o (undo takes you here) -;; | -;; | -;; o (start here) -;; |\ -;; | \ -;; o x (redo takes you here) -;; | -;; | -;; o -;; -;; So far, this is just like the standard undo/redo system. But what if you -;; want to return to a buffer state located on a previous branch of the -;; history? Since `undo-tree-mode' keeps the entire history, you simply need -;; to tell it to switch to a different branch, and then redo the changes you -;; want: -;; -;; o -;; | -;; | -;; o (start here, but switch -;; |\ to the other branch) -;; | \ -;; (redo) o o -;; | -;; | -;; (redo) x -;; -;; Now you're on the other branch, if you undo and redo changes you'll stay on -;; that branch, moving up and down through the buffer states located on that -;; branch. Until you decide to switch branches again, of course. -;; -;; Real undo trees might have multiple branches and sub-branches: -;; -;; o -;; ____|______ -;; / \ -;; o o -;; ____|__ __| -;; / | \ / \ -;; o o o o x -;; | | -;; / \ / \ -;; o o o o -;; -;; Trying to imagine what Emacs' undo would do as you move about such a tree -;; will likely frazzle your brain circuits! But in `undo-tree-mode', you're -;; just moving around this undo history tree. Most of the time, you'll -;; probably only need to stay on the most recent branch, in which case it -;; behaves like standard undo/redo, and is just as simple to understand. But -;; if you ever need to recover a buffer state on a different branch, the -;; possibility of switching between branches and accessing the full undo -;; history is still there. -;; -;; -;; -;; The Undo-Tree Visualizer -;; ======================== -;; -;; Actually, it gets better. You don't have to imagine all these tree -;; diagrams, because `undo-tree-mode' includes an undo-tree visualizer which -;; draws them for you! In fact, it draws even better diagrams: it highlights -;; the node representing the current buffer state, it highlights the current -;; branch, and you can toggle the display of time-stamps (by hitting "t") and -;; a diff of the undo changes (by hitting "d"). (There's one other tiny -;; difference: the visualizer puts the most recent branch on the left rather -;; than the right.) -;; -;; Bring up the undo tree visualizer whenever you want by hitting "C-x u". -;; -;; In the visualizer, the usual keys for moving up and down a buffer instead -;; move up and down the undo history tree (e.g. the up and down arrow keys, or -;; "C-n" and "C-p"). The state of the "parent" buffer (the buffer whose undo -;; history you are visualizing) is updated as you move around the undo tree in -;; the visualizer. If you reach a branch point in the visualizer, the usual -;; keys for moving forward and backward in a buffer instead switch branch -;; (e.g. the left and right arrow keys, or "C-f" and "C-b"). -;; -;; Clicking with the mouse on any node in the visualizer will take you -;; directly to that node, resetting the state of the parent buffer to the -;; state represented by that node. -;; -;; You can also select nodes directly using the keyboard, by hitting "s" to -;; toggle selection mode. The usual motion keys now allow you to move around -;; the tree without changing the parent buffer. Hitting will reset the -;; state of the parent buffer to the state represented by the currently -;; selected node. -;; -;; It can be useful to see how long ago the parent buffer was in the state -;; represented by a particular node in the visualizer. Hitting "t" in the -;; visualizer toggles the display of time-stamps for all the nodes. (Note -;; that, because of the way `undo-tree-mode' works, these time-stamps may be -;; somewhat later than the true times, especially if it's been a long time -;; since you last undid any changes.) -;; -;; To get some idea of what changes are represented by a given node in the -;; tree, it can be useful to see a diff of the changes. Hit "d" in the -;; visualizer to toggle a diff display. This normally displays a diff between -;; the current state and the previous one, i.e. it shows you the changes that -;; will be applied if you undo (move up the tree). However, the diff display -;; really comes into its own in the visualizer's selection mode (see above), -;; where it instead shows a diff between the current state and the currently -;; selected state, i.e. it shows you the changes that will be applied if you -;; reset to the selected state. -;; -;; (Note that the diff is generated by the Emacs `diff' command, and is -;; displayed using `diff-mode'. See the corresponding customization groups if -;; you want to customize the diff display.) -;; -;; Finally, hitting "q" will quit the visualizer, leaving the parent buffer in -;; whatever state you ended at. Hitting "C-q" will abort the visualizer, -;; returning the parent buffer to whatever state it was originally in when the -;; visualizer was invoked. -;; -;; -;; -;; Undo-in-Region -;; ============== -;; -;; Emacs allows a very useful and powerful method of undoing only selected -;; changes: when a region is active, only changes that affect the text within -;; that region will be undone. With the standard Emacs undo system, changes -;; produced by undoing-in-region naturally get added onto the end of the -;; linear undo history: -;; -;; o -;; | -;; | x (second undo-in-region) -;; o | -;; | | -;; | o (first undo-in-region) -;; o | -;; | / -;; |/ -;; o -;; -;; You can of course redo these undos-in-region as usual, by undoing the -;; undos: -;; -;; o -;; | -;; | o_ -;; o | \ -;; | | | -;; | o o (undo the undo-in-region) -;; o | | -;; | / | -;; |/ | -;; o x (undo the undo-in-region) -;; -;; -;; In `undo-tree-mode', undo-in-region works much the same way: when there's -;; an active region, undoing only undoes changes that affect that region. In -;; `undo-tree-mode', redoing when there's an active region similarly only -;; redoes changes that affect that region. -;; -;; However, the way these undo- and redo-in-region changes are recorded in the -;; undo history is quite different. The good news is, you don't need to -;; understand this to use undo- and redo-in-region in `undo-tree-mode' - just -;; go ahead and use them! They'll probably work as you expect. But if you're -;; masochistic enough to want to understand conceptually what's happening to -;; the undo tree as you undo- and redo-in-region, then read on... -;; -;; -;; Undo-in-region creates a new branch in the undo history. The new branch -;; consists of an undo step that undoes some of the changes that affect the -;; current region, and another step that undoes the remaining changes needed -;; to rejoin the previous undo history. -;; -;; Previous undo history Undo-in-region -;; -;; o o -;; | | -;; | | -;; | | -;; o o -;; | | -;; | | -;; | | -;; o o_ -;; | | \ -;; | | x (undo-in-region) -;; | | | -;; x o o -;; -;; As long as you don't change the active region after undoing-in-region, -;; continuing to undo-in-region extends the new branch, pulling more changes -;; that affect the current region into an undo step immediately above your -;; current location in the undo tree, and pushing the point at which the new -;; branch is attached further up the tree: -;; -;; First undo-in-region Second undo-in-region -;; -;; o o -;; | | -;; | | -;; | | -;; o o_ -;; | | \ -;; | | x (undo-in-region) -;; | | | -;; o_ o | -;; | \ | | -;; | x | o -;; | | | | -;; o o o o -;; -;; Redoing takes you back down the undo tree, as usual (as long as you haven't -;; changed the active region after undoing-in-region, it doesn't matter if it -;; is still active): -;; -;; o -;; | -;; | -;; | -;; o_ -;; | \ -;; | o -;; | | -;; o | -;; | | -;; | o (redo) -;; | | -;; o x (redo) -;; -;; -;; What about redo-in-region? Obviously, redo-in-region only makes sense if -;; you have already undone some changes, so that there are some changes to -;; redo! Redoing-in-region splits off a new branch of the undo history below -;; your current location in the undo tree. This time, the new branch consists -;; of a first redo step that redoes some of the redo changes that affect the -;; current region, followed by *all* the remaining redo changes. -;; -;; Previous undo history Redo-in-region -;; -;; o o -;; | | -;; | | -;; | | -;; x o_ -;; | | \ -;; | | x (redo-in-region) -;; | | | -;; o o | -;; | | | -;; | | | -;; | | | -;; o o o -;; -;; As long as you don't change the active region after redoing-in-region, -;; continuing to redo-in-region extends the new branch, pulling more redo -;; changes into a redo step immediately below your current location in the -;; undo tree. -;; -;; First redo-in-region Second redo-in-region -;; -;; o o -;; | | -;; | | -;; | | -;; o_ o_ -;; | \ | \ -;; | x | o -;; | | | | -;; o | o | -;; | | | | -;; | | | x (redo-in-region) -;; | | | | -;; o o o o -;; -;; Note that undo-in-region and redo-in-region only ever add new changes to -;; the undo tree, they *never* modify existing undo history. So you can always -;; return to previous buffer states by switching to a previous branch of the -;; tree. - - - -;;; Code: - -(require 'cl-lib) -(require 'diff) -(require 'gv) - - - -;;; ===================================================================== -;;; Compatibility hacks for older Emacsen - -;; `characterp' isn't defined in Emacs versions < 23 -(unless (fboundp 'characterp) - (defalias 'characterp 'char-valid-p)) - -;; `region-active-p' isn't defined in Emacs versions < 23 -(unless (fboundp 'region-active-p) - (defun region-active-p () (and transient-mark-mode mark-active))) - - -;; `registerv' defstruct isn't defined in Emacs versions < 24 -(unless (fboundp 'registerv-make) - (defmacro registerv-make (data &rest _dummy) data)) - -(unless (fboundp 'registerv-data) - (defmacro registerv-data (data) data)) - - -;; `diff-no-select' and `diff-file-local-copy' aren't defined in Emacs -;; versions < 24 (copied and adapted from Emacs 24) -(unless (fboundp 'diff-no-select) - (defun diff-no-select (old new &optional switches no-async buf) - ;; Noninteractive helper for creating and reverting diff buffers - (unless (bufferp new) (setq new (expand-file-name new))) - (unless (bufferp old) (setq old (expand-file-name old))) - (or switches (setq switches diff-switches)) ; If not specified, use default. - (unless (listp switches) (setq switches (list switches))) - (or buf (setq buf (get-buffer-create "*Diff*"))) - (let* ((old-alt (diff-file-local-copy old)) - (new-alt (diff-file-local-copy new)) - (command - (mapconcat 'identity - `(,diff-command - ;; Use explicitly specified switches - ,@switches - ,@(mapcar #'shell-quote-argument - (nconc - (when (or old-alt new-alt) - (list "-L" (if (stringp old) - old (prin1-to-string old)) - "-L" (if (stringp new) - new (prin1-to-string new)))) - (list (or old-alt old) - (or new-alt new))))) - " ")) - (thisdir default-directory)) - (with-current-buffer buf - (setq buffer-read-only t) - (buffer-disable-undo (current-buffer)) - (let ((inhibit-read-only t)) - (erase-buffer)) - (buffer-enable-undo (current-buffer)) - (diff-mode) - (set (make-local-variable 'revert-buffer-function) - (lambda (_ignore-auto _noconfirm) - (diff-no-select old new switches no-async (current-buffer)))) - (setq default-directory thisdir) - (let ((inhibit-read-only t)) - (insert command "\n")) - (if (and (not no-async) (fboundp 'start-process)) - (let ((proc (start-process "Diff" buf shell-file-name - shell-command-switch command))) - (set-process-filter proc 'diff-process-filter) - (set-process-sentinel - proc (lambda (proc _msg) - (with-current-buffer (process-buffer proc) - (diff-sentinel (process-exit-status proc)) - (if old-alt (delete-file old-alt)) - (if new-alt (delete-file new-alt)))))) - ;; Async processes aren't available. - (let ((inhibit-read-only t)) - (diff-sentinel - (call-process shell-file-name nil buf nil - shell-command-switch command)) - (if old-alt (delete-file old-alt)) - (if new-alt (delete-file new-alt))))) - buf))) - -(unless (fboundp 'diff-file-local-copy) - (defun diff-file-local-copy (file-or-buf) - (if (bufferp file-or-buf) - (with-current-buffer file-or-buf - (let ((tempfile (make-temp-file "buffer-content-"))) - (write-region nil nil tempfile nil 'nomessage) - tempfile)) - (file-local-copy file-or-buf)))) - - -;; `user-error' isn't defined in Emacs < 24.3 -(unless (fboundp 'user-error) - (defalias 'user-error 'error) - ;; prevent debugger being called on user errors - (add-to-list 'debug-ignored-errors "^No further undo information") - (add-to-list 'debug-ignored-errors "^No further redo information") - (add-to-list 'debug-ignored-errors "^No further redo information for region")) - - - - - -;;; ===================================================================== -;;; Global variables and customization options - -(defvar buffer-undo-tree nil - "Tree of undo entries in current buffer.") -(put 'buffer-undo-tree 'permanent-local t) -(make-variable-buffer-local 'buffer-undo-tree) - - -(defgroup undo-tree nil - "Tree undo/redo." - :group 'undo) - - -(defcustom undo-tree-limit 80000000 - "Value of `undo-limit' used in `undo-tree-mode'. - -If `undo-limit' is larger than `undo-tree-limit', the larger of -the two values will be used. - -See also `undo-tree-strong-limit' and `undo-tree-outer-limit'. - -Setting this to nil prevents `undo-tree-mode' ever discarding -undo history. (As far as possible. In principle, it is still -possible for Emacs to discard undo history behind -`undo-tree-mode's back.) USE THIS SETTING AT YOUR OWN RISK! Emacs -may crash if undo history exceeds Emacs' available memory. This -is particularly risky if `undo-tree-auto-save-history' is -enabled, as in that case undo history is preserved even between -Emacs sessions." - :group 'undo-tree - :type '(choice integer (const nil))) - - -(defcustom undo-tree-strong-limit 120000000 - "Value of `undo-strong-limit' used in `undo-tree-mode'. - -If `undo-strong-limit' is larger than `undo-tree-strong-limit' -the larger of the two values will be used." - :group 'undo-tree - :type 'integer) - - -(defcustom undo-tree-outer-limit 360000000 - "Value of `undo-outer-limit' used in `undo-tree-mode'. - -If `undo-outer-limit' is larger than `undo-tree-outer-limit' the -larger of the two values will be used." - :group 'undo-tree - :type 'integer) - - -(defcustom undo-tree-mode-lighter " Undo-Tree" - "Lighter displayed in mode line -when `undo-tree-mode' is enabled." - :group 'undo-tree - :type 'string) - - -(defcustom undo-tree-incompatible-major-modes '(term-mode) - "List of major-modes in which `undo-tree-mode' should not be enabled. -\(See `turn-on-undo-tree-mode'.\)" - :group 'undo-tree - :type '(repeat symbol)) - - -(defcustom undo-tree-enable-undo-in-region nil - "When non-nil, enable undo-in-region. - -When undo-in-region is enabled, undoing or redoing when the -region is active (in `transient-mark-mode') or with a prefix -argument (not in `transient-mark-mode') only undoes changes -within the current region." - :group 'undo-tree - :type 'boolean) - - -(defcustom undo-tree-auto-save-history nil - "When non-nil, `undo-tree-mode' will save undo history to file -when a buffer is saved to file. - -It will automatically load undo history when a buffer is loaded -from file, if an undo save file exists. - -By default, undo-tree history is saved to a file called -\"..~undo-tree~\" in the same directory as the -file itself. To save under a different directory, customize -`undo-tree-history-directory-alist' (see the documentation for -that variable for details). - -WARNING! `undo-tree-auto-save-history' will not work properly in -Emacs versions prior to 24.3, so it cannot be enabled via -the customization interface in versions earlier than that one. To -ignore this warning and enable it regardless, set -`undo-tree-auto-save-history' to a non-nil value outside of -customize." - :group 'undo-tree - :type (if (version-list-< (version-to-list emacs-version) '(24 3)) - '(choice (const :tag "" nil)) - 'boolean)) - - -(defcustom undo-tree-history-directory-alist nil - "Alist of filename patterns and undo history directory names. -Each element looks like (REGEXP . DIRECTORY). Undo history for -files with names matching REGEXP will be saved in DIRECTORY. -DIRECTORY may be relative or absolute. If it is absolute, so -that all matching files are backed up into the same directory, -the file names in this directory will be the full name of the -file backed up with all directory separators changed to `!' to -prevent clashes. This will not work correctly if your filesystem -truncates the resulting name. - -For the common case of all backups going into one directory, the -alist should contain a single element pairing \".\" with the -appropriate directory name. - -If this variable is nil, or it fails to match a filename, the -backup is made in the original file's directory. - -On MS-DOS filesystems without long names this variable is always -ignored." - :group 'undo-tree - :type '(repeat (cons (regexp :tag "Regexp matching filename") - (directory :tag "Undo history directory name")))) - - - -(defcustom undo-tree-visualizer-relative-timestamps t - "When non-nil, display times relative to current time -when displaying time stamps in visualizer. - -Otherwise, display absolute times." - :group 'undo-tree - :type 'boolean) - - -(defcustom undo-tree-visualizer-timestamps nil - "When non-nil, display time-stamps by default -in undo-tree visualizer. - -\\You can always toggle time-stamps on and off \ -using \\[undo-tree-visualizer-toggle-timestamps], regardless of the -setting of this variable." - :group 'undo-tree - :type 'boolean) - - -(defcustom undo-tree-visualizer-diff nil - "When non-nil, display diff by default in undo-tree visualizer. - -\\You can always toggle the diff display \ -using \\[undo-tree-visualizer-toggle-diff], regardless of the -setting of this variable." - :group 'undo-tree - :type 'boolean) - - -(defcustom undo-tree-visualizer-lazy-drawing 100 - "When non-nil, use lazy undo-tree drawing in visualizer. - -Setting this to a number causes the visualizer to switch to lazy -drawing when the number of nodes in the tree is larger than this -value. - -Lazy drawing means that only the visible portion of the tree will -be drawn initially, and the tree will be extended later as -needed. For the most part, the only visible effect of this is to -significantly speed up displaying the visualizer for very large -trees. - -There is one potential negative effect of lazy drawing. Other -branches of the tree will only be drawn once the node from which -they branch off becomes visible. So it can happen that certain -portions of the tree that would be shown with lazy drawing -disabled, will not be drawn immediately when it is -enabled. However, this effect is quite rare in practice." - :group 'undo-tree - :type '(choice (const :tag "never" nil) - (const :tag "always" t) - (integer :tag "> size"))) - - -(defvar undo-tree-pre-save-element-functions '() - "Special hook to modify undo-tree elements prior to saving. -Each function on this hook is called in turn on each undo element -in the tree by `undo-tree-save-history' prior to writing the undo -history to file. It should return either nil, which removes that -undo element from the saved history, or a replacement element to -use instead (which should be identical to the original element if -that element should be saved unchanged).") - - -(defvar undo-tree-post-load-element-functions '() - "Special hook to modify undo-tree undo elements after loading. -Each function on this hook is called in turn on each undo element -in the tree by `undo-tree-load-history' after loading the undo -history from file. It should return either nil, which removes that -undo element from the loaded history, or a replacement element to -use instead (which should be identical to the original element if -that element should be loaded unchanged).") - - -(defface undo-tree-visualizer-default-face - '((((class color)) :foreground "gray")) - "Face used to draw undo-tree in visualizer." - :group 'undo-tree) - -(defface undo-tree-visualizer-current-face - '((((class color)) :foreground "red")) - "Face used to highlight current undo-tree node in visualizer." - :group 'undo-tree) - -(defface undo-tree-visualizer-active-branch-face - '((((class color) (background dark)) - (:foreground "white" :weight bold)) - (((class color) (background light)) - (:foreground "black" :weight bold))) - "Face used to highlight active undo-tree branch in visualizer." - :group 'undo-tree) - -(defface undo-tree-visualizer-register-face - '((((class color)) :foreground "yellow")) - "Face used to highlight undo-tree nodes saved to a register -in visualizer." - :group 'undo-tree) - -(defface undo-tree-visualizer-unmodified-face - '((((class color)) :foreground "cyan")) - "Face used to highlight nodes corresponding to unmodified buffers -in visualizer." - :group 'undo-tree) - - -(defvar undo-tree-visualizer-parent-buffer nil - "Parent buffer in visualizer.") -(put 'undo-tree-visualizer-parent-buffer 'permanent-local t) -(make-variable-buffer-local 'undo-tree-visualizer-parent-buffer) - -;; stores modification time of parent buffer's file, if any -(defvar undo-tree-visualizer-parent-mtime nil) -(put 'undo-tree-visualizer-parent-mtime 'permanent-local t) -(make-variable-buffer-local 'undo-tree-visualizer-parent-mtime) - -;; stores current horizontal spacing needed for drawing undo-tree -(defvar undo-tree-visualizer-spacing nil) -(put 'undo-tree-visualizer-spacing 'permanent-local t) -(make-variable-buffer-local 'undo-tree-visualizer-spacing) - -;; calculate horizontal spacing required for drawing tree with current -;; settings -(defsubst undo-tree-visualizer-calculate-spacing () - (if undo-tree-visualizer-timestamps - (if undo-tree-visualizer-relative-timestamps 9 13) - 3)) - -;; holds node that was current when visualizer was invoked -(defvar undo-tree-visualizer-initial-node nil) -(put 'undo-tree-visualizer-initial-node 'permanent-local t) -(make-variable-buffer-local 'undo-tree-visualizer-initial-node) - -;; holds currently selected node in visualizer selection mode -(defvar undo-tree-visualizer-selected-node nil) -(put 'undo-tree-visualizer-selected-node 'permanent-local t) -(make-variable-buffer-local 'undo-tree-visualizer-selected) - -;; used to store nodes at edge of currently drawn portion of tree -(defvar undo-tree-visualizer-needs-extending-down nil) -(put 'undo-tree-visualizer-needs-extending-down 'permanent-local t) -(make-variable-buffer-local 'undo-tree-visualizer-needs-extending-down) -(defvar undo-tree-visualizer-needs-extending-up nil) -(put 'undo-tree-visualizer-needs-extending-up 'permanent-local t) -(make-variable-buffer-local 'undo-tree-visualizer-needs-extending-up) - -;; dynamically bound to t when undoing from visualizer, to inhibit -;; `undo-tree-kill-visualizer' hook function in parent buffer -(defvar undo-tree-inhibit-kill-visualizer nil) - -;; can be let-bound to a face name, used in drawing functions -(defvar undo-tree-insert-face nil) - -;; visualizer buffer names -(defconst undo-tree-visualizer-buffer-name " *undo-tree*") -(defconst undo-tree-diff-buffer-name "*undo-tree Diff*") - - - - -;;; ================================================================= -;;; Default keymaps - -(defvar undo-tree-map nil - "Keymap used in undo-tree-mode.") - -(unless undo-tree-map - (let ((map (make-sparse-keymap))) - ;; remap `undo' and `undo-only' to `undo-tree-undo' - (define-key map [remap undo] 'undo-tree-undo) - (define-key map [remap undo-only] 'undo-tree-undo) - ;; bind standard undo bindings (since these match redo counterparts) - (define-key map (kbd "C-/") 'undo-tree-undo) - (define-key map "\C-_" 'undo-tree-undo) - ;; redo doesn't exist normally, so define our own keybindings - (define-key map (kbd "C-?") 'undo-tree-redo) - (define-key map (kbd "M-_") 'undo-tree-redo) - ;; just in case something has defined `redo'... - (define-key map [remap redo] 'undo-tree-redo) - ;; we use "C-x u" for the undo-tree visualizer - (define-key map (kbd "\C-x u") 'undo-tree-visualize) - ;; bind register commands - (define-key map (kbd "C-x r u") 'undo-tree-save-state-to-register) - (define-key map (kbd "C-x r U") 'undo-tree-restore-state-from-register) - ;; set keymap - (setq undo-tree-map map))) - - -(defvar undo-tree-visualizer-mode-map nil - "Keymap used in undo-tree visualizer.") - -(unless undo-tree-visualizer-mode-map - (let ((map (make-sparse-keymap))) - ;; vertical motion keys undo/redo - (define-key map [remap previous-line] 'undo-tree-visualize-undo) - (define-key map [remap next-line] 'undo-tree-visualize-redo) - (define-key map [up] 'undo-tree-visualize-undo) - (define-key map "p" 'undo-tree-visualize-undo) - (define-key map "\C-p" 'undo-tree-visualize-undo) - (define-key map [down] 'undo-tree-visualize-redo) - (define-key map "n" 'undo-tree-visualize-redo) - (define-key map "\C-n" 'undo-tree-visualize-redo) - ;; horizontal motion keys switch branch - (define-key map [remap forward-char] - 'undo-tree-visualize-switch-branch-right) - (define-key map [remap backward-char] - 'undo-tree-visualize-switch-branch-left) - (define-key map [right] 'undo-tree-visualize-switch-branch-right) - (define-key map "f" 'undo-tree-visualize-switch-branch-right) - (define-key map "\C-f" 'undo-tree-visualize-switch-branch-right) - (define-key map [left] 'undo-tree-visualize-switch-branch-left) - (define-key map "b" 'undo-tree-visualize-switch-branch-left) - (define-key map "\C-b" 'undo-tree-visualize-switch-branch-left) - ;; paragraph motion keys undo/redo to significant points in tree - (define-key map [remap backward-paragraph] 'undo-tree-visualize-undo-to-x) - (define-key map [remap forward-paragraph] 'undo-tree-visualize-redo-to-x) - (define-key map "\M-{" 'undo-tree-visualize-undo-to-x) - (define-key map "\M-}" 'undo-tree-visualize-redo-to-x) - (define-key map [C-up] 'undo-tree-visualize-undo-to-x) - (define-key map [C-down] 'undo-tree-visualize-redo-to-x) - ;; mouse sets buffer state to node at click - (define-key map [mouse-1] 'undo-tree-visualizer-mouse-set) - ;; toggle timestamps - (define-key map "t" 'undo-tree-visualizer-toggle-timestamps) - ;; toggle diff - (define-key map "d" 'undo-tree-visualizer-toggle-diff) - ;; toggle selection mode - (define-key map "s" 'undo-tree-visualizer-selection-mode) - ;; horizontal scrolling may be needed if the tree is very wide - (define-key map "," 'undo-tree-visualizer-scroll-left) - (define-key map "." 'undo-tree-visualizer-scroll-right) - (define-key map "<" 'undo-tree-visualizer-scroll-left) - (define-key map ">" 'undo-tree-visualizer-scroll-right) - ;; vertical scrolling may be needed if the tree is very tall - (define-key map [next] 'undo-tree-visualizer-scroll-up) - (define-key map [prior] 'undo-tree-visualizer-scroll-down) - ;; quit/abort visualizer - (define-key map "q" 'undo-tree-visualizer-quit) - (define-key map "\C-q" 'undo-tree-visualizer-abort) - ;; set keymap - (setq undo-tree-visualizer-mode-map map))) - - -(defvar undo-tree-visualizer-selection-mode-map nil - "Keymap used in undo-tree visualizer selection mode.") - -(unless undo-tree-visualizer-selection-mode-map - (let ((map (make-sparse-keymap))) - ;; vertical motion keys move up and down tree - (define-key map [remap previous-line] - 'undo-tree-visualizer-select-previous) - (define-key map [remap next-line] - 'undo-tree-visualizer-select-next) - (define-key map [up] 'undo-tree-visualizer-select-previous) - (define-key map "p" 'undo-tree-visualizer-select-previous) - (define-key map "\C-p" 'undo-tree-visualizer-select-previous) - (define-key map [down] 'undo-tree-visualizer-select-next) - (define-key map "n" 'undo-tree-visualizer-select-next) - (define-key map "\C-n" 'undo-tree-visualizer-select-next) - ;; vertical scroll keys move up and down quickly - (define-key map [next] - (lambda () (interactive) (undo-tree-visualizer-select-next 10))) - (define-key map [prior] - (lambda () (interactive) (undo-tree-visualizer-select-previous 10))) - ;; horizontal motion keys move to left and right siblings - (define-key map [remap forward-char] 'undo-tree-visualizer-select-right) - (define-key map [remap backward-char] 'undo-tree-visualizer-select-left) - (define-key map [right] 'undo-tree-visualizer-select-right) - (define-key map "f" 'undo-tree-visualizer-select-right) - (define-key map "\C-f" 'undo-tree-visualizer-select-right) - (define-key map [left] 'undo-tree-visualizer-select-left) - (define-key map "b" 'undo-tree-visualizer-select-left) - (define-key map "\C-b" 'undo-tree-visualizer-select-left) - ;; horizontal scroll keys move left or right quickly - (define-key map "," - (lambda () (interactive) (undo-tree-visualizer-select-left 10))) - (define-key map "." - (lambda () (interactive) (undo-tree-visualizer-select-right 10))) - (define-key map "<" - (lambda () (interactive) (undo-tree-visualizer-select-left 10))) - (define-key map ">" - (lambda () (interactive) (undo-tree-visualizer-select-right 10))) - ;; sets buffer state to node at point - (define-key map "\r" 'undo-tree-visualizer-set) - ;; mouse selects node at click - (define-key map [mouse-1] 'undo-tree-visualizer-mouse-select) - ;; toggle diff - (define-key map "d" 'undo-tree-visualizer-selection-toggle-diff) - ;; set keymap - (setq undo-tree-visualizer-selection-mode-map map))) - - - - -;;; ===================================================================== -;;; Undo-tree data structure - -(cl-defstruct - (undo-tree - :named - (:constructor nil) - (:constructor make-undo-tree - (&aux - (root (undo-tree-make-node nil nil)) - (current root) - (size 0) - (count 0) - (object-pool (make-hash-table :test 'eq :weakness 'value)))) - (:copier nil)) - root current size count object-pool) - -(defun undo-tree-copy (tree) - ;; Return a copy of undo-tree TREE. - (unwind-protect - (let ((new (make-undo-tree))) - (undo-tree-decircle tree) - (let ((max-lisp-eval-depth (* 100 (undo-tree-count tree))) - (max-specpdl-size (* 100 (undo-tree-count tree)))) - (setf (undo-tree-root new) - (undo-tree-node-copy (undo-tree-root tree) - new (undo-tree-current tree)))) - (setf (undo-tree-size new) - (undo-tree-size tree)) - (setf (undo-tree-count new) - (undo-tree-count tree)) - (setf (undo-tree-object-pool new) - (copy-hash-table (undo-tree-object-pool tree))) - (undo-tree-recircle new) - new) - (undo-tree-recircle tree))) - - -(cl-defstruct - (undo-tree-node - (:type vector) ; create unnamed struct - (:constructor nil) - (:constructor undo-tree-make-node - (previous undo - &optional redo - &aux - (timestamp (current-time)) - (branch 0))) - (:constructor undo-tree-make-node-backwards - (next-node undo - &optional redo - &aux - (next (list next-node)) - (timestamp (current-time)) - (branch 0))) - (:constructor undo-tree-make-empty-node ()) - (:copier nil)) - previous next undo redo timestamp branch meta-data) - - -(defmacro undo-tree-node-p (n) - (let ((len (length (undo-tree-make-node nil nil)))) - `(and (vectorp ,n) (= (length ,n) ,len)))) - -(defun undo-tree-node-copy (node &optional tree current) - ;; Return a copy of undo-tree NODE, sans previous link or meta-data. - ;; If TREE and CURRENT are supplied, set (undo-tree-current TREE) to the - ;; copy of CURRENT node, if found. - (let* ((new (undo-tree-make-empty-node)) - (stack (list (cons node new))) - n) - (while (setq n (pop stack)) - (setf (undo-tree-node-undo (cdr n)) - (copy-tree (undo-tree-node-undo (car n)) 'copy-vectors)) - (setf (undo-tree-node-redo (cdr n)) - (copy-tree (undo-tree-node-redo (car n)) 'copy-vectors)) - (setf (undo-tree-node-timestamp (cdr n)) - (copy-sequence (undo-tree-node-timestamp (car n)))) - (setf (undo-tree-node-branch (cdr n)) - (undo-tree-node-branch (car n))) - (setf (undo-tree-node-next (cdr n)) - (mapcar (lambda (_) (undo-tree-make-empty-node)) - (make-list (length (undo-tree-node-next (car n))) nil))) - ;; set (undo-tree-current TREE) to copy if we've found CURRENT - (when (and tree (eq (car n) current)) - (setf (undo-tree-current tree) (cdr n))) - ;; recursively copy next nodes - (let ((next0 (undo-tree-node-next (car n))) - (next1 (undo-tree-node-next (cdr n)))) - (while (and next0 next1) - (push (cons (pop next0) (pop next1)) stack)))) - new)) - - -(cl-defstruct - (undo-tree-region-data - (:type vector) ; create unnamed struct - (:constructor nil) - (:constructor undo-tree-make-region-data - (&optional undo-beginning undo-end - redo-beginning redo-end)) - (:constructor undo-tree-make-undo-region-data - (undo-beginning undo-end)) - (:constructor undo-tree-make-redo-region-data - (redo-beginning redo-end)) - (:copier nil)) - undo-beginning undo-end redo-beginning redo-end) - - -(defmacro undo-tree-region-data-p (r) - (let ((len (length (undo-tree-make-region-data)))) - `(and (vectorp ,r) (= (length ,r) ,len)))) - -(defmacro undo-tree-node-clear-region-data (node) - `(setf (undo-tree-node-meta-data ,node) - (delq nil - (delq :region - (plist-put (undo-tree-node-meta-data ,node) - :region nil))))) - - -(defmacro undo-tree-node-undo-beginning (node) - `(let ((r (plist-get (undo-tree-node-meta-data ,node) :region))) - (when (undo-tree-region-data-p r) - (undo-tree-region-data-undo-beginning r)))) - -(defmacro undo-tree-node-undo-end (node) - `(let ((r (plist-get (undo-tree-node-meta-data ,node) :region))) - (when (undo-tree-region-data-p r) - (undo-tree-region-data-undo-end r)))) - -(defmacro undo-tree-node-redo-beginning (node) - `(let ((r (plist-get (undo-tree-node-meta-data ,node) :region))) - (when (undo-tree-region-data-p r) - (undo-tree-region-data-redo-beginning r)))) - -(defmacro undo-tree-node-redo-end (node) - `(let ((r (plist-get (undo-tree-node-meta-data ,node) :region))) - (when (undo-tree-region-data-p r) - (undo-tree-region-data-redo-end r)))) - - -(gv-define-setter undo-tree-node-undo-beginning (val node) - `(let ((r (plist-get (undo-tree-node-meta-data ,node) :region))) - (unless (undo-tree-region-data-p r) - (setf (undo-tree-node-meta-data ,node) - (plist-put (undo-tree-node-meta-data ,node) :region - (setq r (undo-tree-make-region-data))))) - (setf (undo-tree-region-data-undo-beginning r) ,val))) - -(gv-define-setter undo-tree-node-undo-end (val node) - `(let ((r (plist-get (undo-tree-node-meta-data ,node) :region))) - (unless (undo-tree-region-data-p r) - (setf (undo-tree-node-meta-data ,node) - (plist-put (undo-tree-node-meta-data ,node) :region - (setq r (undo-tree-make-region-data))))) - (setf (undo-tree-region-data-undo-end r) ,val))) - -(gv-define-setter undo-tree-node-redo-beginning (val node) - `(let ((r (plist-get (undo-tree-node-meta-data ,node) :region))) - (unless (undo-tree-region-data-p r) - (setf (undo-tree-node-meta-data ,node) - (plist-put (undo-tree-node-meta-data ,node) :region - (setq r (undo-tree-make-region-data))))) - (setf (undo-tree-region-data-redo-beginning r) ,val))) - -(gv-define-setter undo-tree-node-redo-end (val node) - `(let ((r (plist-get (undo-tree-node-meta-data ,node) :region))) - (unless (undo-tree-region-data-p r) - (setf (undo-tree-node-meta-data ,node) - (plist-put (undo-tree-node-meta-data ,node) :region - (setq r (undo-tree-make-region-data))))) - (setf (undo-tree-region-data-redo-end r) ,val))) - - - -(cl-defstruct - (undo-tree-visualizer-data - (:type vector) ; create unnamed struct - (:constructor nil) - (:constructor undo-tree-make-visualizer-data - (&optional lwidth cwidth rwidth marker)) - (:copier nil)) - lwidth cwidth rwidth marker) - - -(defmacro undo-tree-visualizer-data-p (v) - (let ((len (length (undo-tree-make-visualizer-data)))) - `(and (vectorp ,v) (= (length ,v) ,len)))) - -(defun undo-tree-node-clear-visualizer-data (node) - (let ((plist (undo-tree-node-meta-data node))) - (if (eq (car plist) :visualizer) - (setf (undo-tree-node-meta-data node) (nthcdr 2 plist)) - (while (and plist (not (eq (cadr plist) :visualizer))) - (setq plist (cdr plist))) - (if plist (setcdr plist (nthcdr 3 plist)))))) - -(defmacro undo-tree-node-lwidth (node) - `(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer))) - (when (undo-tree-visualizer-data-p v) - (undo-tree-visualizer-data-lwidth v)))) - -(defmacro undo-tree-node-cwidth (node) - `(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer))) - (when (undo-tree-visualizer-data-p v) - (undo-tree-visualizer-data-cwidth v)))) - -(defmacro undo-tree-node-rwidth (node) - `(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer))) - (when (undo-tree-visualizer-data-p v) - (undo-tree-visualizer-data-rwidth v)))) - -(defmacro undo-tree-node-marker (node) - `(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer))) - (when (undo-tree-visualizer-data-p v) - (undo-tree-visualizer-data-marker v)))) - - -(gv-define-setter undo-tree-node-lwidth (val node) - `(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer))) - (unless (undo-tree-visualizer-data-p v) - (setf (undo-tree-node-meta-data ,node) - (plist-put (undo-tree-node-meta-data ,node) :visualizer - (setq v (undo-tree-make-visualizer-data))))) - (setf (undo-tree-visualizer-data-lwidth v) ,val))) - -(gv-define-setter undo-tree-node-cwidth (val node) - `(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer))) - (unless (undo-tree-visualizer-data-p v) - (setf (undo-tree-node-meta-data ,node) - (plist-put (undo-tree-node-meta-data ,node) :visualizer - (setq v (undo-tree-make-visualizer-data))))) - (setf (undo-tree-visualizer-data-cwidth v) ,val))) - -(gv-define-setter undo-tree-node-rwidth (val node) - `(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer))) - (unless (undo-tree-visualizer-data-p v) - (setf (undo-tree-node-meta-data ,node) - (plist-put (undo-tree-node-meta-data ,node) :visualizer - (setq v (undo-tree-make-visualizer-data))))) - (setf (undo-tree-visualizer-data-rwidth v) ,val))) - -(gv-define-setter undo-tree-node-marker (val node) - `(let ((v (plist-get (undo-tree-node-meta-data ,node) :visualizer))) - (unless (undo-tree-visualizer-data-p v) - (setf (undo-tree-node-meta-data ,node) - (plist-put (undo-tree-node-meta-data ,node) :visualizer - (setq v (undo-tree-make-visualizer-data))))) - (setf (undo-tree-visualizer-data-marker v) ,val))) - - - -(cl-defstruct - (undo-tree-register-data - (:type vector) - (:constructor nil) - (:constructor undo-tree-make-register-data (buffer node))) - buffer node) - -(defun undo-tree-register-data-p (data) - (and (vectorp data) - (= (length data) 2) - (undo-tree-node-p (undo-tree-register-data-node data)))) - -(defun undo-tree-register-data-print-func (data) - (princ (format "an undo-tree state for buffer %s" - (undo-tree-register-data-buffer data)))) - -(defmacro undo-tree-node-register (node) - `(plist-get (undo-tree-node-meta-data ,node) :register)) - -(gv-define-setter undo-tree-node-register (val node) - `(setf (undo-tree-node-meta-data ,node) - (plist-put (undo-tree-node-meta-data ,node) :register ,val))) - - - - -;;; ===================================================================== -;;; Basic undo-tree data structure functions - -(defun undo-tree-grow (undo) - "Add an UNDO node to current branch of `buffer-undo-tree'." - (let* ((current (undo-tree-current buffer-undo-tree)) - (new (undo-tree-make-node current undo))) - (push new (undo-tree-node-next current)) - (setf (undo-tree-current buffer-undo-tree) new))) - - -(defun undo-tree-grow-backwards (node undo &optional redo) - "Add new node *above* undo-tree NODE, and return new node. -Note that this will overwrite NODE's \"previous\" link, so should -only be used on a detached NODE, never on nodes that are already -part of `buffer-undo-tree'." - (let ((new (undo-tree-make-node-backwards node undo redo))) - (setf (undo-tree-node-previous node) new) - new)) - - -(defun undo-tree-splice-node (node splice) - "Splice NODE into undo tree, below node SPLICE. -Note that this will overwrite NODE's \"next\" and \"previous\" -links, so should only be used on a detached NODE, never on nodes -that are already part of `buffer-undo-tree'." - (setf (undo-tree-node-next node) (undo-tree-node-next splice) - (undo-tree-node-branch node) (undo-tree-node-branch splice) - (undo-tree-node-previous node) splice - (undo-tree-node-next splice) (list node) - (undo-tree-node-branch splice) 0) - (dolist (n (undo-tree-node-next node)) - (setf (undo-tree-node-previous n) node))) - - -(defun undo-tree-snip-node (node) - "Snip NODE out of undo tree." - (let* ((parent (undo-tree-node-previous node)) - position p) - ;; if NODE is only child, replace parent's next links with NODE's - (if (= (length (undo-tree-node-next parent)) 0) - (setf (undo-tree-node-next parent) (undo-tree-node-next node) - (undo-tree-node-branch parent) (undo-tree-node-branch node)) - ;; otherwise... - (setq position (undo-tree-position node (undo-tree-node-next parent))) - (cond - ;; if active branch used do go via NODE, set parent's branch to active - ;; branch of NODE - ((= (undo-tree-node-branch parent) position) - (setf (undo-tree-node-branch parent) - (+ position (undo-tree-node-branch node)))) - ;; if active branch didn't go via NODE, update parent's branch to point - ;; to same node as before - ((> (undo-tree-node-branch parent) position) - (cl-incf (undo-tree-node-branch parent) - (1- (length (undo-tree-node-next node)))))) - ;; replace NODE in parent's next list with NODE's entire next list - (if (= position 0) - (setf (undo-tree-node-next parent) - (nconc (undo-tree-node-next node) - (cdr (undo-tree-node-next parent)))) - (setq p (nthcdr (1- position) (undo-tree-node-next parent))) - (setcdr p (nconc (undo-tree-node-next node) (cddr p))))) - ;; update previous links of NODE's children - (dolist (n (undo-tree-node-next node)) - (setf (undo-tree-node-previous n) parent)))) - - -(defun undo-tree-mapc (--undo-tree-mapc-function-- node) - ;; Apply FUNCTION to NODE and to each node below it. - (let ((stack (list node)) - n) - (while (setq n (pop stack)) - (funcall --undo-tree-mapc-function-- n) - (setq stack (append (undo-tree-node-next n) stack))))) - - -(defmacro undo-tree-num-branches () - "Return number of branches at current undo tree node." - '(length (undo-tree-node-next (undo-tree-current buffer-undo-tree)))) - - -(defun undo-tree-position (node list) - "Find the first occurrence of NODE in LIST. -Return the index of the matching item, or nil of not found. -Comparison is done with `eq'." - (let ((i 0)) - (catch 'found - (while (progn - (when (eq node (car list)) (throw 'found i)) - (cl-incf i) - (setq list (cdr list)))) - nil))) - - -(defvar *undo-tree-id-counter* 0) -(make-variable-buffer-local '*undo-tree-id-counter*) - -(defmacro undo-tree-generate-id () - ;; Generate a new, unique id (uninterned symbol). - ;; The name is made by appending a number to "undo-tree-id". - ;; (Copied from CL package `gensym'.) - `(let ((num (prog1 *undo-tree-id-counter* - (cl-incf *undo-tree-id-counter*)))) - (make-symbol (format "undo-tree-id%d" num)))) - - -(defun undo-tree-decircle (undo-tree) - ;; Nullify PREVIOUS links of UNDO-TREE nodes, to make UNDO-TREE data - ;; structure non-circular. - (undo-tree-mapc - (lambda (node) - (dolist (n (undo-tree-node-next node)) - (setf (undo-tree-node-previous n) nil))) - (undo-tree-root undo-tree))) - - -(defun undo-tree-recircle (undo-tree) - ;; Recreate PREVIOUS links of UNDO-TREE nodes, to restore circular UNDO-TREE - ;; data structure. - (undo-tree-mapc - (lambda (node) - (dolist (n (undo-tree-node-next node)) - (setf (undo-tree-node-previous n) node))) - (undo-tree-root undo-tree))) - - - - -;;; ===================================================================== -;;; Undo list and undo changeset utility functions - -(defmacro undo-list-marker-elt-p (elt) - `(markerp (car-safe ,elt))) - -(defmacro undo-list-GCd-marker-elt-p (elt) - ;; Return t if ELT is a marker element whose marker has been moved to the - ;; object-pool, so may potentially have been garbage-collected. - ;; Note: Valid marker undo elements should be uniquely identified as cons - ;; cells with a symbol in the car (replacing the marker), and a number in - ;; the cdr. However, to guard against future changes to undo element - ;; formats, we perform an additional redundant check on the symbol name. - `(and (car-safe ,elt) - (symbolp (car ,elt)) - (let ((str (symbol-name (car ,elt)))) - (and (> (length str) 12) - (string= (substring str 0 12) "undo-tree-id"))) - (numberp (cdr-safe ,elt)))) - - -(defun undo-tree-move-GC-elts-to-pool (elt) - ;; Move elements that can be garbage-collected into `buffer-undo-tree' - ;; object pool, substituting a unique id that can be used to retrieve them - ;; later. (Only markers require this treatment currently.) - (when (undo-list-marker-elt-p elt) - (let ((id (undo-tree-generate-id))) - (puthash id (car elt) (undo-tree-object-pool buffer-undo-tree)) - (setcar elt id)))) - - -(defun undo-tree-restore-GC-elts-from-pool (elt) - ;; Replace object id's in ELT with corresponding objects from - ;; `buffer-undo-tree' object pool and return modified ELT, or return nil if - ;; any object in ELT has been garbage-collected. - (if (undo-list-GCd-marker-elt-p elt) - (when (setcar elt (gethash (car elt) - (undo-tree-object-pool buffer-undo-tree))) - elt) - elt)) - - -(defun undo-list-clean-GCd-elts (undo-list) - ;; Remove object id's from UNDO-LIST that refer to elements that have been - ;; garbage-collected. UNDO-LIST is modified by side-effect. - (while (undo-list-GCd-marker-elt-p (car undo-list)) - (unless (gethash (caar undo-list) - (undo-tree-object-pool buffer-undo-tree)) - (setq undo-list (cdr undo-list)))) - (let ((p undo-list)) - (while (cdr p) - (when (and (undo-list-GCd-marker-elt-p (cadr p)) - (null (gethash (car (cadr p)) - (undo-tree-object-pool buffer-undo-tree)))) - (setcdr p (cddr p))) - (setq p (cdr p)))) - undo-list) - - -(defun undo-list-found-canary-p (undo-list) - (or (eq (car undo-list) 'undo-tree-canary) - (and (null (car undo-list)) - (eq (cadr undo-list) 'undo-tree-canary)))) - - -(defmacro undo-list-pop-changeset (undo-list &optional discard-pos) - ;; Pop changeset from `undo-list'. If DISCARD-POS is non-nil, discard - ;; any position entries from changeset. - `(when (and ,undo-list (not (undo-list-found-canary-p ,undo-list))) - (let (changeset) - ;; discard initial undo boundary(ies) - (while (null (car ,undo-list)) (setq ,undo-list (cdr ,undo-list))) - ;; pop elements up to next undo boundary, discarding position entries - ;; if DISCARD-POS is non-nil - (while (null changeset) - (while (and ,undo-list (car ,undo-list) - (not (undo-list-found-canary-p ,undo-list))) - (if (and ,discard-pos (integerp (car ,undo-list))) - (setq ,undo-list (cdr ,undo-list)) - (push (pop ,undo-list) changeset) - (undo-tree-move-GC-elts-to-pool (car changeset))))) - (nreverse changeset)))) - - -(defun undo-tree-copy-list (undo-list) - ;; Return a deep copy of first changeset in `undo-list'. Object id's are - ;; replaced by corresponding objects from `buffer-undo-tree' object-pool. - (let (copy p) - ;; if first element contains an object id, replace it with object from - ;; pool, discarding element entirely if it's been GC'd - (while (and undo-list (null copy)) - (setq copy - (undo-tree-restore-GC-elts-from-pool (pop undo-list)))) - (when copy - (setq copy (list copy) - p copy) - ;; copy remaining elements, replacing object id's with objects from - ;; pool, or discarding them entirely if they've been GC'd - (while undo-list - (when (setcdr p (undo-tree-restore-GC-elts-from-pool - (undo-copy-list-1 (pop undo-list)))) - (setcdr p (list (cdr p))) - (setq p (cdr p)))) - copy))) - - -(defvar undo-tree-gc-flag nil) - -(defun undo-tree-post-gc () - (setq undo-tree-gc-flag t)) - - -(defun undo-list-transfer-to-tree () - ;; Transfer entries accumulated in `undo-list' to `buffer-undo-tree'. - - ;; `undo-list-transfer-to-tree' should never be called when undo is disabled - ;; (i.e. `buffer-undo-tree' is t) - (cl-assert (not (eq buffer-undo-tree t))) - - ;; if `buffer-undo-tree' is empty, create initial undo-tree - (when (null buffer-undo-tree) (setq buffer-undo-tree (make-undo-tree))) - - ;; garbage-collect then repeatedly try to deep-copy `buffer-undo-list' until - ;; we succeed without GC running, in an attempt to mitigate race conditions - ;; with garbage collector corrupting undo history (is this even a thing?!) - (unless (or (null buffer-undo-list) - (undo-list-found-canary-p buffer-undo-list)) - (garbage-collect)) - (let (undo-list changeset) - (setq undo-tree-gc-flag t) - (while undo-tree-gc-flag - (setq undo-tree-gc-flag nil - undo-list (copy-tree buffer-undo-list))) - (setq buffer-undo-list '(nil undo-tree-canary)) - - ;; create new node from first changeset in `undo-list', save old - ;; `buffer-undo-tree' current node, and make new node the current node - (when (setq changeset (undo-list-pop-changeset undo-list)) - (let* ((node (undo-tree-make-node nil changeset)) - (splice (undo-tree-current buffer-undo-tree)) - (size (undo-list-byte-size (undo-tree-node-undo node))) - (count 1)) - (setf (undo-tree-current buffer-undo-tree) node) - ;; grow tree fragment backwards using `undo-list' changesets - (while (setq changeset (undo-list-pop-changeset undo-list)) - (setq node (undo-tree-grow-backwards node changeset)) - (cl-incf size (undo-list-byte-size (undo-tree-node-undo node))) - (cl-incf count)) - - ;; if no undo history has been discarded from `undo-list' since last - ;; transfer, splice new tree fragment onto end of old - ;; `buffer-undo-tree' current node - (if (undo-list-found-canary-p undo-list) - (progn - (setf (undo-tree-node-previous node) splice) - (push node (undo-tree-node-next splice)) - (setf (undo-tree-node-branch splice) 0) - (cl-incf (undo-tree-size buffer-undo-tree) size) - (cl-incf (undo-tree-count buffer-undo-tree) count)) - - ;; if undo history has been discarded, replace entire - ;; `buffer-undo-tree' with new tree fragment - (unless (= (undo-tree-size buffer-undo-tree) 0) - (message "Undo history discarded by Emacs (see `undo-limit') - rebuilding undo-tree")) - (setq node (undo-tree-grow-backwards node nil)) - (setf (undo-tree-root buffer-undo-tree) node) - (setf (undo-tree-size buffer-undo-tree) size) - (setf (undo-tree-count buffer-undo-tree) count) - (setq undo-list '(nil undo-tree-canary)))))) - - ;; discard undo history if necessary - (undo-tree-discard-history)) - - -(defun undo-list-byte-size (undo-list) - ;; Return size (in bytes) of UNDO-LIST - (let ((size 0)) - (dolist (elt undo-list) - (cl-incf size 8) ; cons cells use up 8 bytes - (when (stringp (car-safe elt)) - (cl-incf size (string-bytes (car elt))))) - size)) - - - -(defun undo-list-rebuild-from-tree () - "Rebuild `buffer-undo-list' from information in `buffer-undo-tree'." - (unless (eq buffer-undo-list t) - (undo-list-transfer-to-tree) - (setq buffer-undo-list nil) - (when buffer-undo-tree - (let ((stack (list (list (undo-tree-root buffer-undo-tree))))) - (push (sort (mapcar 'identity (undo-tree-node-next (caar stack))) - (lambda (a b) - (time-less-p (undo-tree-node-timestamp a) - (undo-tree-node-timestamp b)))) - stack) - ;; Traverse tree in depth-and-oldest-first order, but add undo records - ;; on the way down, and redo records on the way up. - (while (or (car stack) - (not (eq (car (nth 1 stack)) - (undo-tree-current buffer-undo-tree)))) - (if (car stack) - (progn - (setq buffer-undo-list - (append (undo-tree-node-undo (caar stack)) - buffer-undo-list)) - (undo-boundary) - (push (sort (mapcar 'identity - (undo-tree-node-next (caar stack))) - (lambda (a b) - (time-less-p (undo-tree-node-timestamp a) - (undo-tree-node-timestamp b)))) - stack)) - (pop stack) - (setq buffer-undo-list - (append (undo-tree-node-redo (caar stack)) - buffer-undo-list)) - (undo-boundary) - (pop (car stack)))))))) - - - - -;;; ===================================================================== -;;; History discarding utility functions - -(defun undo-tree-oldest-leaf (node) - ;; Return oldest leaf node below NODE. - (while (undo-tree-node-next node) - (setq node - (car (sort (mapcar 'identity (undo-tree-node-next node)) - (lambda (a b) - (time-less-p (undo-tree-node-timestamp a) - (undo-tree-node-timestamp b))))))) - node) - - -(defun undo-tree-discard-node (node) - ;; Discard NODE from `buffer-undo-tree', and return next in line for - ;; discarding. - - ;; don't discard current node - (unless (eq node (undo-tree-current buffer-undo-tree)) - - ;; discarding root node... - (if (eq node (undo-tree-root buffer-undo-tree)) - (cond - ;; should always discard branches before root - ((> (length (undo-tree-node-next node)) 1) - (error "Trying to discard undo-tree root which still\ - has multiple branches")) - ;; don't discard root if current node is only child - ((eq (car (undo-tree-node-next node)) - (undo-tree-current buffer-undo-tree)) - nil) - ;; discard root - (t - ;; clear any register referring to root - (let ((r (undo-tree-node-register node))) - (when (and r (eq (get-register r) node)) - (set-register r nil))) - ;; make child of root into new root - (setq node (setf (undo-tree-root buffer-undo-tree) - (car (undo-tree-node-next node)))) - ;; update undo-tree size - (cl-decf (undo-tree-size buffer-undo-tree) - (+ (undo-list-byte-size (undo-tree-node-undo node)) - (undo-list-byte-size (undo-tree-node-redo node)))) - (cl-decf (undo-tree-count buffer-undo-tree)) - ;; discard new root's undo data and PREVIOUS link - (setf (undo-tree-node-undo node) nil - (undo-tree-node-redo node) nil - (undo-tree-node-previous node) nil) - ;; if new root has branches, or new root is current node, next node - ;; to discard is oldest leaf, otherwise it's new root - (if (or (> (length (undo-tree-node-next node)) 1) - (eq (car (undo-tree-node-next node)) - (undo-tree-current buffer-undo-tree))) - (undo-tree-oldest-leaf node) - node))) - - ;; discarding leaf node... - (let* ((parent (undo-tree-node-previous node)) - (current (nth (undo-tree-node-branch parent) - (undo-tree-node-next parent)))) - ;; clear any register referring to the discarded node - (let ((r (undo-tree-node-register node))) - (when (and r (eq (get-register r) node)) - (set-register r nil))) - ;; update undo-tree size - (cl-decf (undo-tree-size buffer-undo-tree) - (+ (undo-list-byte-size (undo-tree-node-undo node)) - (undo-list-byte-size (undo-tree-node-redo node)))) - (cl-decf (undo-tree-count buffer-undo-tree)) - ;; discard leaf - (setf (undo-tree-node-next parent) - (delq node (undo-tree-node-next parent)) - (undo-tree-node-branch parent) - (undo-tree-position current (undo-tree-node-next parent))) - ;; if parent has branches, or parent is current node, next node to - ;; discard is oldest leaf, otherwise it's the parent itself - (if (or (eq parent (undo-tree-current buffer-undo-tree)) - (and (undo-tree-node-next parent) - (or (not (eq parent (undo-tree-root buffer-undo-tree))) - (> (length (undo-tree-node-next parent)) 1)))) - (undo-tree-oldest-leaf parent) - parent))))) - - - -(defun undo-tree-discard-history () - "Discard undo history until we're within memory usage limits -set by `undo-limit', `undo-strong-limit' and `undo-outer-limit'." - - (when (> (undo-tree-size buffer-undo-tree) undo-limit) - ;; if there are no branches off root, first node to discard is root; - ;; otherwise it's leaf node at botom of oldest branch - (let ((node (if (> (length (undo-tree-node-next - (undo-tree-root buffer-undo-tree))) 1) - (undo-tree-oldest-leaf (undo-tree-root buffer-undo-tree)) - (undo-tree-root buffer-undo-tree))) - discarded) - - ;; discard nodes until memory use is within `undo-strong-limit' - (while (and node - (> (undo-tree-size buffer-undo-tree) undo-strong-limit)) - (setq node (undo-tree-discard-node node) - discarded t)) - - ;; discard nodes until next node to discard would bring memory use - ;; within `undo-limit' - (while (and node - ;; check first if last discard has brought us within - ;; `undo-limit', in case we can avoid more expensive - ;; `undo-strong-limit' calculation - ;; Note: this assumes undo-strong-limit > undo-limit; - ;; if not, effectively undo-strong-limit = undo-limit - (> (undo-tree-size buffer-undo-tree) undo-limit) - (> (- (undo-tree-size buffer-undo-tree) - ;; if next node to discard is root, the memory we - ;; free-up comes from discarding changesets from its - ;; only child... - (if (eq node (undo-tree-root buffer-undo-tree)) - (+ (undo-list-byte-size - (undo-tree-node-undo - (car (undo-tree-node-next node)))) - (undo-list-byte-size - (undo-tree-node-redo - (car (undo-tree-node-next node))))) - ;; ...otherwise, it comes from discarding changesets - ;; from along with the node itself - (+ (undo-list-byte-size (undo-tree-node-undo node)) - (undo-list-byte-size (undo-tree-node-redo node))) - )) - undo-limit)) - (setq node (undo-tree-discard-node node) - discarded t)) - - (when discarded - (message "Undo history discarded by undo-tree (see `undo-tree-limit')")) - - ;; if we're still over the `undo-outer-limit', discard entire history - (when (and undo-outer-limit - (> (undo-tree-size buffer-undo-tree) undo-outer-limit)) - ;; query first if `undo-ask-before-discard' is set - (if undo-ask-before-discard - (when (yes-or-no-p - (format - "Buffer `%s' undo info is %d bytes long; discard it? " - (buffer-name) (undo-tree-size buffer-undo-tree))) - (setq buffer-undo-tree nil)) - ;; otherwise, discard and display warning - (display-warning - '(undo discard-info) - (concat - (format "Buffer `%s' undo info was %d bytes long.\n" - (buffer-name) (undo-tree-size buffer-undo-tree)) - "The undo info was discarded because it exceeded\ - `undo-outer-limit'. - -This is normal if you executed a command that made a huge change -to the buffer. In that case, to prevent similar problems in the -future, set `undo-outer-limit' to a value that is large enough to -cover the maximum size of normal changes you expect a single -command to make, but not so large that it might exceed the -maximum memory allotted to Emacs. - -If you did not execute any such command, the situation is -probably due to a bug and you should report it. - -You can disable the popping up of this buffer by adding the entry -\(undo discard-info) to the user option `warning-suppress-types', -which is defined in the `warnings' library.\n") - :warning) - (setq buffer-undo-tree nil))) - - ;; if currently displaying the visualizer, redraw it - (when (and buffer-undo-tree - discarded - (or (eq major-mode 'undo-tree-visualizer-mode) - undo-tree-visualizer-parent-buffer - (get-buffer undo-tree-visualizer-buffer-name))) - (let ((undo-tree buffer-undo-tree)) - (with-current-buffer undo-tree-visualizer-buffer-name - (undo-tree-draw-tree undo-tree) - (when undo-tree-visualizer-diff (undo-tree-visualizer-update-diff))))) - ))) - - - - -;;; ===================================================================== -;;; Visualizer utility functions - -(defun undo-tree-compute-widths (node) - "Recursively compute widths for nodes below NODE." - (let ((stack (list node)) - res) - (while stack - ;; try to compute widths for node at top of stack - (if (undo-tree-node-p - (setq res (undo-tree-node-compute-widths (car stack)))) - ;; if computation fails, it returns a node whose widths still need - ;; computing, which we push onto the stack - (push res stack) - ;; otherwise, store widths and remove it from stack - (setf (undo-tree-node-lwidth (car stack)) (aref res 0) - (undo-tree-node-cwidth (car stack)) (aref res 1) - (undo-tree-node-rwidth (car stack)) (aref res 2)) - (pop stack))))) - - -(defun undo-tree-node-compute-widths (node) - ;; Compute NODE's left-, centre-, and right-subtree widths. Returns widths - ;; (in a vector) if successful. Otherwise, returns a node whose widths need - ;; calculating before NODE's can be calculated. - (let ((num-children (length (undo-tree-node-next node))) - (lwidth 0) (cwidth 0) (rwidth 0) p) - (catch 'need-widths - (cond - ;; leaf nodes have 0 width - ((= 0 num-children) - (setf cwidth 1 - (undo-tree-node-lwidth node) 0 - (undo-tree-node-cwidth node) 1 - (undo-tree-node-rwidth node) 0)) - - ;; odd number of children - ((= (mod num-children 2) 1) - (setq p (undo-tree-node-next node)) - ;; compute left-width - (dotimes (_ (/ num-children 2)) - (if (undo-tree-node-lwidth (car p)) - (cl-incf lwidth (+ (undo-tree-node-lwidth (car p)) - (undo-tree-node-cwidth (car p)) - (undo-tree-node-rwidth (car p)))) - ;; if child's widths haven't been computed, return that child - (throw 'need-widths (car p))) - (setq p (cdr p))) - (if (undo-tree-node-lwidth (car p)) - (cl-incf lwidth (undo-tree-node-lwidth (car p))) - (throw 'need-widths (car p))) - ;; centre-width is inherited from middle child - (setf cwidth (undo-tree-node-cwidth (car p))) - ;; compute right-width - (cl-incf rwidth (undo-tree-node-rwidth (car p))) - (setq p (cdr p)) - (dotimes (_ (/ num-children 2)) - (if (undo-tree-node-lwidth (car p)) - (cl-incf rwidth (+ (undo-tree-node-lwidth (car p)) - (undo-tree-node-cwidth (car p)) - (undo-tree-node-rwidth (car p)))) - (throw 'need-widths (car p))) - (setq p (cdr p)))) - - ;; even number of children - (t - (setq p (undo-tree-node-next node)) - ;; compute left-width - (dotimes (_ (/ num-children 2)) - (if (undo-tree-node-lwidth (car p)) - (cl-incf lwidth (+ (undo-tree-node-lwidth (car p)) - (undo-tree-node-cwidth (car p)) - (undo-tree-node-rwidth (car p)))) - (throw 'need-widths (car p))) - (setq p (cdr p))) - ;; centre-width is 0 when number of children is even - (setq cwidth 0) - ;; compute right-width - (dotimes (_ (/ num-children 2)) - (if (undo-tree-node-lwidth (car p)) - (cl-incf rwidth (+ (undo-tree-node-lwidth (car p)) - (undo-tree-node-cwidth (car p)) - (undo-tree-node-rwidth (car p)))) - (throw 'need-widths (car p))) - (setq p (cdr p))))) - - ;; return left-, centre- and right-widths - (vector lwidth cwidth rwidth)))) - - -(defun undo-tree-clear-visualizer-data (tree) - ;; Clear visualizer data below NODE. - (undo-tree-mapc - (lambda (n) (undo-tree-node-clear-visualizer-data n)) - (undo-tree-root tree))) - - -(defun undo-tree-node-unmodified-p (node &optional mtime) - ;; Return non-nil if NODE corresponds to a buffer state that once upon a - ;; time was unmodified. If a file modification time MTIME is specified, - ;; return non-nil if the corresponding buffer state really is unmodified. - (let (changeset ntime) - (setq changeset - (or (undo-tree-node-redo node) - (and (setq changeset (car (undo-tree-node-next node))) - (undo-tree-node-undo changeset))) - ntime - (catch 'found - (dolist (elt changeset) - (when (and (consp elt) (eq (car elt) t) (consp (cdr elt)) - (throw 'found (cdr elt))))))) - (and ntime - (or (null mtime) - ;; high-precision timestamps - (if (listp (cdr ntime)) - (equal ntime mtime) - ;; old-style timestamps - (and (= (car ntime) (car mtime)) - (= (cdr ntime) (cadr mtime)))))))) - - - - -;;; ===================================================================== -;;; Undo-in-region utility functions - -;; `undo-elt-in-region' uses this as a dynamically-scoped variable -(defvar undo-adjusted-markers nil) - - -(defun undo-tree-pull-undo-in-region-branch (start end) - ;; Pull out entries from undo changesets to create a new undo-in-region - ;; branch, which undoes changeset entries lying between START and END first, - ;; followed by remaining entries from the changesets, before rejoining the - ;; existing undo tree history. Repeated calls will, if appropriate, extend - ;; the current undo-in-region branch rather than creating a new one. - - ;; if we're just reverting the last redo-in-region, we don't need to - ;; manipulate the undo tree at all - (if (undo-tree-reverting-redo-in-region-p start end) - t ; return t to indicate success - - ;; We build the `region-changeset' and `delta-list' lists forwards, using - ;; pointers `r' and `d' to the penultimate element of the list. So that we - ;; don't have to treat the first element differently, we prepend a dummy - ;; leading nil to the lists, and have the pointers point to that - ;; initially. - ;; Note: using '(nil) instead of (list nil) in the `let*' results in - ;; errors when the code is byte-compiled, presumably because the - ;; Lisp reader generates a single cons, and that same cons gets used - ;; each call. - (let* ((region-changeset (list nil)) - (r region-changeset) - (delta-list (list nil)) - (d delta-list) - (node (undo-tree-current buffer-undo-tree)) - (repeated-undo-in-region - (undo-tree-repeated-undo-in-region-p start end)) - undo-adjusted-markers ; `undo-elt-in-region' expects this - fragment splice original-fragment original-splice original-current - got-visible-elt undo-list elt) - - ;; --- initialisation --- - (cond - ;; if this is a repeated undo in the same region, start pulling changes - ;; from NODE at which undo-in-region branch is attached, and detatch - ;; the branch, using it as initial FRAGMENT of branch being constructed - (repeated-undo-in-region - (setq original-current node - fragment (car (undo-tree-node-next node)) - splice node) - ;; undo up to node at which undo-in-region branch is attached - ;; (recognizable as first node with more than one branch) - (let ((mark-active nil)) - (while (= (length (undo-tree-node-next node)) 1) - (undo-tree-undo-1) - (setq fragment node - node (undo-tree-current buffer-undo-tree)))) - (when (eq splice node) (setq splice nil)) - ;; detatch undo-in-region branch - (setf (undo-tree-node-next node) - (delq fragment (undo-tree-node-next node)) - (undo-tree-node-previous fragment) nil - original-fragment fragment - original-splice node)) - - ;; if this is a new undo-in-region, initial FRAGMENT is a copy of all - ;; nodes below the current one in the active branch - ((undo-tree-node-next node) - (setq fragment (undo-tree-make-node nil nil) - splice fragment) - (while (setq node (nth (undo-tree-node-branch node) - (undo-tree-node-next node))) - (push (undo-tree-make-node - splice - (undo-copy-list (undo-tree-node-undo node)) - (undo-copy-list (undo-tree-node-redo node))) - (undo-tree-node-next splice)) - (setq splice (car (undo-tree-node-next splice)))) - (setq fragment (car (undo-tree-node-next fragment)) - splice nil - node (undo-tree-current buffer-undo-tree)))) - - - ;; --- pull undo-in-region elements into branch --- - ;; work backwards up tree, pulling out undo elements within region until - ;; we've got one that undoes a visible change (insertion or deletion) - (catch 'abort - (while (and (not got-visible-elt) node (undo-tree-node-undo node)) - ;; we cons a dummy nil element on the front of the changeset so that - ;; we can conveniently remove the first (real) element from the - ;; changeset if we need to; the leading nil is removed once we're - ;; done with this changeset - (setq undo-list (cons nil (undo-copy-list (undo-tree-node-undo node))) - elt (cadr undo-list)) - (if fragment - (progn - (setq fragment (undo-tree-grow-backwards fragment undo-list)) - (unless splice (setq splice fragment))) - (setq fragment (undo-tree-make-node nil undo-list)) - (setq splice fragment)) - - (while elt - (cond - ;; keep elements within region - ((undo-elt-in-region elt start end) - ;; set flag if kept element is visible (insertion or deletion) - (when (and (consp elt) - (or (stringp (car elt)) (integerp (car elt)))) - (setq got-visible-elt t)) - ;; adjust buffer positions in elements previously undone before - ;; kept element, as kept element will now be undone first - (undo-tree-adjust-elements-to-elt splice elt) - ;; move kept element to undo-in-region changeset, adjusting its - ;; buffer position as it will now be undone first - (setcdr r (list (undo-tree-apply-deltas elt (cdr delta-list)))) - (setq r (cdr r)) - (setcdr undo-list (cddr undo-list))) - - ;; discard "was unmodified" elements - ;; FIXME: deal properly with these - ((and (consp elt) (eq (car elt) t)) - (setcdr undo-list (cddr undo-list))) - - ;; if element crosses region, we can't pull any more elements - ((undo-elt-crosses-region elt start end) - ;; if we've found a visible element, it must be earlier in - ;; current node's changeset; stop pulling elements (null - ;; `undo-list' and non-nil `got-visible-elt' cause loop to exit) - (if got-visible-elt - (setq undo-list nil) - ;; if we haven't found a visible element yet, pulling - ;; undo-in-region branch has failed - (setq region-changeset nil) - (throw 'abort t))) - - ;; if rejecting element, add its delta (if any) to the list - (t - (let ((delta (undo-delta elt))) - (when (/= 0 (cdr delta)) - (setcdr d (list delta)) - (setq d (cdr d)))) - (setq undo-list (cdr undo-list)))) - - ;; process next element of current changeset - (setq elt (cadr undo-list))) - - ;; if there are remaining elements in changeset, remove dummy nil - ;; from front - (if (cadr (undo-tree-node-undo fragment)) - (pop (undo-tree-node-undo fragment)) - ;; otherwise, if we've kept all elements in changeset, discard - ;; empty changeset - (when (eq splice fragment) (setq splice nil)) - (setq fragment (car (undo-tree-node-next fragment)))) - ;; process changeset from next node up the tree - (setq node (undo-tree-node-previous node)))) - - ;; pop dummy nil from front of `region-changeset' - (setq region-changeset (cdr region-changeset)) - - - ;; --- integrate branch into tree --- - ;; if no undo-in-region elements were found, restore undo tree - (if (null region-changeset) - (when original-current - (push original-fragment (undo-tree-node-next original-splice)) - (setf (undo-tree-node-branch original-splice) 0 - (undo-tree-node-previous original-fragment) original-splice) - (let ((mark-active nil)) - (while (not (eq (undo-tree-current buffer-undo-tree) - original-current)) - (undo-tree-redo-1))) - nil) ; return nil to indicate failure - - ;; otherwise... - ;; need to undo up to node where new branch will be attached, to - ;; ensure redo entries are populated, and then redo back to where we - ;; started - (let ((mark-active nil) - (current (undo-tree-current buffer-undo-tree))) - (while (not (eq (undo-tree-current buffer-undo-tree) node)) - (undo-tree-undo-1)) - (while (not (eq (undo-tree-current buffer-undo-tree) current)) - (undo-tree-redo-1))) - - (cond - ;; if there's no remaining fragment, just create undo-in-region node - ;; and attach it to parent of last node from which elements were - ;; pulled - ((null fragment) - (setq fragment (undo-tree-make-node node region-changeset)) - (push fragment (undo-tree-node-next node)) - (setf (undo-tree-node-branch node) 0) - ;; set current node to undo-in-region node - (setf (undo-tree-current buffer-undo-tree) fragment)) - - ;; if no splice point has been set, add undo-in-region node to top of - ;; fragment and attach it to parent of last node from which elements - ;; were pulled - ((null splice) - (setq fragment (undo-tree-grow-backwards fragment region-changeset)) - (push fragment (undo-tree-node-next node)) - (setf (undo-tree-node-branch node) 0 - (undo-tree-node-previous fragment) node) - ;; set current node to undo-in-region node - (setf (undo-tree-current buffer-undo-tree) fragment)) - - ;; if fragment contains nodes, attach fragment to parent of last node - ;; from which elements were pulled, and splice in undo-in-region node - (t - (setf (undo-tree-node-previous fragment) node) - (push fragment (undo-tree-node-next node)) - (setf (undo-tree-node-branch node) 0) - ;; if this is a repeated undo-in-region, then we've left the current - ;; node at the original splice-point; we need to set the current - ;; node to the equivalent node on the undo-in-region branch and redo - ;; back to where we started - (when repeated-undo-in-region - (setf (undo-tree-current buffer-undo-tree) - (undo-tree-node-previous original-fragment)) - (let ((mark-active nil)) - (while (not (eq (undo-tree-current buffer-undo-tree) splice)) - (undo-tree-redo-1 nil 'preserve-undo)))) - ;; splice new undo-in-region node into fragment - (setq node (undo-tree-make-node nil region-changeset)) - (undo-tree-splice-node node splice) - ;; set current node to undo-in-region node - (setf (undo-tree-current buffer-undo-tree) node))) - - ;; update undo-tree size - (setq node (undo-tree-node-previous fragment)) - (while (progn - (and (setq node (car (undo-tree-node-next node))) - (not (eq node original-fragment)) - (cl-incf (undo-tree-count buffer-undo-tree)) - (cl-incf (undo-tree-size buffer-undo-tree) - (+ (undo-list-byte-size (undo-tree-node-undo node)) - (undo-list-byte-size (undo-tree-node-redo node))))))) - t) ; indicate undo-in-region branch was successfully pulled - ))) - - - -(defun undo-tree-pull-redo-in-region-branch (start end) - ;; Pull out entries from redo changesets to create a new redo-in-region - ;; branch, which redoes changeset entries lying between START and END first, - ;; followed by remaining entries from the changesets. Repeated calls will, - ;; if appropriate, extend the current redo-in-region branch rather than - ;; creating a new one. - - ;; if we're just reverting the last undo-in-region, we don't need to - ;; manipulate the undo tree at all - (if (undo-tree-reverting-undo-in-region-p start end) - t ; return t to indicate success - - ;; We build the `region-changeset' and `delta-list' lists forwards, using - ;; pointers `r' and `d' to the penultimate element of the list. So that we - ;; don't have to treat the first element differently, we prepend a dummy - ;; leading nil to the lists, and have the pointers point to that - ;; initially. - ;; Note: using '(nil) instead of (list nil) in the `let*' causes bizarre - ;; errors when the code is byte-compiled, where parts of the lists - ;; appear to survive across different calls to this function. An - ;; obscure byte-compiler bug, perhaps? - (let* ((region-changeset (list nil)) - (r region-changeset) - (delta-list (list nil)) - (d delta-list) - (node (undo-tree-current buffer-undo-tree)) - (repeated-redo-in-region - (undo-tree-repeated-redo-in-region-p start end)) - undo-adjusted-markers ; `undo-elt-in-region' expects this - fragment splice got-visible-elt redo-list elt) - - ;; --- inisitalisation --- - (cond - ;; if this is a repeated redo-in-region, detach fragment below current - ;; node - (repeated-redo-in-region - (when (setq fragment (car (undo-tree-node-next node))) - (setf (undo-tree-node-previous fragment) nil - (undo-tree-node-next node) - (delq fragment (undo-tree-node-next node))))) - ;; if this is a new redo-in-region, initial fragment is a copy of all - ;; nodes below the current one in the active branch - ((undo-tree-node-next node) - (setq fragment (undo-tree-make-node nil nil) - splice fragment) - (while (setq node (nth (undo-tree-node-branch node) - (undo-tree-node-next node))) - (push (undo-tree-make-node - splice nil - (undo-copy-list (undo-tree-node-redo node))) - (undo-tree-node-next splice)) - (setq splice (car (undo-tree-node-next splice)))) - (setq fragment (car (undo-tree-node-next fragment))))) - - - ;; --- pull redo-in-region elements into branch --- - ;; work down fragment, pulling out redo elements within region until - ;; we've got one that redoes a visible change (insertion or deletion) - (setq node fragment) - (catch 'abort - (while (and (not got-visible-elt) node (undo-tree-node-redo node)) - ;; we cons a dummy nil element on the front of the changeset so that - ;; we can conveniently remove the first (real) element from the - ;; changeset if we need to; the leading nil is removed once we're - ;; done with this changeset - (setq redo-list (push nil (undo-tree-node-redo node)) - elt (cadr redo-list)) - (while elt - (cond - ;; keep elements within region - ((undo-elt-in-region elt start end) - ;; set flag if kept element is visible (insertion or deletion) - (when (and (consp elt) - (or (stringp (car elt)) (integerp (car elt)))) - (setq got-visible-elt t)) - ;; adjust buffer positions in elements previously redone before - ;; kept element, as kept element will now be redone first - (undo-tree-adjust-elements-to-elt fragment elt t) - ;; move kept element to redo-in-region changeset, adjusting its - ;; buffer position as it will now be redone first - (setcdr r (list (undo-tree-apply-deltas elt (cdr delta-list) -1))) - (setq r (cdr r)) - (setcdr redo-list (cddr redo-list))) - - ;; discard "was unmodified" elements - ;; FIXME: deal properly with these - ((and (consp elt) (eq (car elt) t)) - (setcdr redo-list (cddr redo-list))) - - ;; if element crosses region, we can't pull any more elements - ((undo-elt-crosses-region elt start end) - ;; if we've found a visible element, it must be earlier in - ;; current node's changeset; stop pulling elements (null - ;; `redo-list' and non-nil `got-visible-elt' cause loop to exit) - (if got-visible-elt - (setq redo-list nil) - ;; if we haven't found a visible element yet, pulling - ;; redo-in-region branch has failed - (setq region-changeset nil) - (throw 'abort t))) - - ;; if rejecting element, add its delta (if any) to the list - (t - (let ((delta (undo-delta elt))) - (when (/= 0 (cdr delta)) - (setcdr d (list delta)) - (setq d (cdr d)))) - (setq redo-list (cdr redo-list)))) - - ;; process next element of current changeset - (setq elt (cadr redo-list))) - - ;; if there are remaining elements in changeset, remove dummy nil - ;; from front - (if (cadr (undo-tree-node-redo node)) - (pop (undo-tree-node-undo node)) - ;; otherwise, if we've kept all elements in changeset, discard - ;; empty changeset - (if (eq fragment node) - (setq fragment (car (undo-tree-node-next fragment))) - (undo-tree-snip-node node))) - ;; process changeset from next node in fragment - (setq node (car (undo-tree-node-next node))))) - - ;; pop dummy nil from front of `region-changeset' - (setq region-changeset (cdr region-changeset)) - - - ;; --- integrate branch into tree --- - (setq node (undo-tree-current buffer-undo-tree)) - ;; if no redo-in-region elements were found, restore undo tree - (if (null (car region-changeset)) - (when (and repeated-redo-in-region fragment) - (push fragment (undo-tree-node-next node)) - (setf (undo-tree-node-branch node) 0 - (undo-tree-node-previous fragment) node) - nil) ; return nil to indicate failure - - ;; otherwise, add redo-in-region node to top of fragment, and attach - ;; it below current node - (setq fragment - (if fragment - (undo-tree-grow-backwards fragment nil region-changeset) - (undo-tree-make-node nil nil region-changeset))) - (push fragment (undo-tree-node-next node)) - (setf (undo-tree-node-branch node) 0 - (undo-tree-node-previous fragment) node) - ;; update undo-tree size - (unless repeated-redo-in-region - (setq node fragment) - (while (and (setq node (car (undo-tree-node-next node))) - (cl-incf (undo-tree-count buffer-undo-tree)) - (cl-incf (undo-tree-size buffer-undo-tree) - (undo-list-byte-size - (undo-tree-node-redo node)))))) - (cl-incf (undo-tree-size buffer-undo-tree) - (undo-list-byte-size (undo-tree-node-redo fragment))) - t) ; indicate redo-in-region branch was successfully pulled - ))) - - - -(defun undo-tree-adjust-elements-to-elt (node undo-elt &optional below) - "Adjust buffer positions of undo elements, starting at NODE's -and going up the tree (or down the active branch if BELOW is -non-nil) and through the nodes' undo elements until we reach -UNDO-ELT. UNDO-ELT must appear somewhere in the undo changeset -of either NODE itself or some node above it in the tree." - (let ((delta (list (undo-delta undo-elt))) - (undo-list (undo-tree-node-undo node))) - ;; adjust elements until we reach UNDO-ELT - (while (and (car undo-list) - (not (eq (car undo-list) undo-elt))) - (setcar undo-list - (undo-tree-apply-deltas (car undo-list) delta -1)) - ;; move to next undo element in list, or to next node if we've run out - ;; of elements - (unless (car (setq undo-list (cdr undo-list))) - (if below - (setq node (nth (undo-tree-node-branch node) - (undo-tree-node-next node))) - (setq node (undo-tree-node-previous node))) - (setq undo-list (undo-tree-node-undo node)))))) - - - -(defun undo-tree-apply-deltas (undo-elt deltas &optional sgn) - ;; Apply DELTAS in order to UNDO-ELT, multiplying deltas by SGN - ;; (only useful value for SGN is -1). - (let (position offset) - (dolist (delta deltas) - (setq position (car delta) - offset (* (cdr delta) (or sgn 1))) - (cond - ;; POSITION - ((integerp undo-elt) - (when (>= undo-elt position) - (setq undo-elt (- undo-elt offset)))) - ;; nil (or any other atom) - ((atom undo-elt)) - ;; (TEXT . POSITION) - ((stringp (car undo-elt)) - (let ((text-pos (abs (cdr undo-elt))) - (point-at-end (< (cdr undo-elt) 0))) - (if (>= text-pos position) - (setcdr undo-elt (* (if point-at-end -1 1) - (- text-pos offset)))))) - ;; (BEGIN . END) - ((integerp (car undo-elt)) - (when (>= (car undo-elt) position) - (setcar undo-elt (- (car undo-elt) offset)) - (setcdr undo-elt (- (cdr undo-elt) offset)))) - ;; (nil PROPERTY VALUE BEG . END) - ((null (car undo-elt)) - (let ((tail (nthcdr 3 undo-elt))) - (when (>= (car tail) position) - (setcar tail (- (car tail) offset)) - (setcdr tail (- (cdr tail) offset))))) - )) - undo-elt)) - - - -(defun undo-tree-repeated-undo-in-region-p (start end) - ;; Return non-nil if undo-in-region between START and END is a repeated - ;; undo-in-region - (let ((node (undo-tree-current buffer-undo-tree))) - (and (setq node - (nth (undo-tree-node-branch node) (undo-tree-node-next node))) - (eq (undo-tree-node-undo-beginning node) start) - (eq (undo-tree-node-undo-end node) end)))) - - -(defun undo-tree-repeated-redo-in-region-p (start end) - ;; Return non-nil if undo-in-region between START and END is a repeated - ;; undo-in-region - (let ((node (undo-tree-current buffer-undo-tree))) - (and (eq (undo-tree-node-redo-beginning node) start) - (eq (undo-tree-node-redo-end node) end)))) - - -;; Return non-nil if undo-in-region between START and END is simply -;; reverting the last redo-in-region -(defalias 'undo-tree-reverting-undo-in-region-p - 'undo-tree-repeated-undo-in-region-p) - - -;; Return non-nil if redo-in-region between START and END is simply -;; reverting the last undo-in-region -(defalias 'undo-tree-reverting-redo-in-region-p - 'undo-tree-repeated-redo-in-region-p) - - - - -;;; ===================================================================== -;;; Undo-tree commands - -(defvar undo-tree-timer nil) - -;;;###autoload -(define-minor-mode undo-tree-mode - "Toggle undo-tree mode. -With no argument, this command toggles the mode. -A positive prefix argument turns the mode on. -A negative prefix argument turns it off. - -Undo-tree-mode replaces Emacs' standard undo feature with a more -powerful yet easier to use version, that treats the undo history -as what it is: a tree. - -The following keys are available in `undo-tree-mode': - - \\{undo-tree-map} - -Within the undo-tree visualizer, the following keys are available: - - \\{undo-tree-visualizer-mode-map}" - - nil ; init value - undo-tree-mode-lighter ; lighter - undo-tree-map ; keymap - - (cond - (undo-tree-mode ; enabling `undo-tree-mode' - (set (make-local-variable 'undo-limit) - (if undo-tree-limit - (max undo-limit undo-tree-limit) - most-positive-fixnum)) - (set (make-local-variable 'undo-strong-limit) - (if undo-tree-limit - (max undo-strong-limit undo-tree-strong-limit) - most-positive-fixnum)) - (set (make-local-variable 'undo-outer-limit) ; null `undo-outer-limit' means no limit - (when (and undo-tree-limit undo-outer-limit undo-outer-limit) - (max undo-outer-limit undo-tree-outer-limit))) - (when (null undo-tree-limit) - (setq undo-tree-timer - (run-with-idle-timer 5 'repeat #'undo-list-transfer-to-tree))) - (add-hook 'post-gc-hook #'undo-tree-post-gc nil)) - - (t ; disabling `undo-tree-mode' - ;; rebuild `buffer-undo-list' from tree so Emacs undo can work - (undo-list-rebuild-from-tree) - (setq buffer-undo-tree nil) - (remove-hook 'post-gc-hook #'undo-tree-post-gc 'local) - (when (timerp undo-tree-timer) (cancel-timer undo-tree-timer)) - (kill-local-variable 'undo-limit) - (kill-local-variable 'undo-strong-limit) - (kill-local-variable 'undo-outer-limit)))) - - -(defun turn-on-undo-tree-mode (&optional print-message) - "Enable `undo-tree-mode' in the current buffer, when appropriate. -Some major modes implement their own undo system, which should -not normally be overridden by `undo-tree-mode'. This command does -not enable `undo-tree-mode' in such buffers. If you want to force -`undo-tree-mode' to be enabled regardless, use (undo-tree-mode 1) -instead. - -The heuristic used to detect major modes in which -`undo-tree-mode' should not be used is to check whether either -the `undo' command has been remapped, or the default undo -keybindings (C-/ and C-_) have been overridden somewhere other -than in the global map. In addition, `undo-tree-mode' will not be -enabled if the buffer's `major-mode' appears in -`undo-tree-incompatible-major-modes'." - (interactive "p") - (if (or (key-binding [remap undo]) - (undo-tree-overridden-undo-bindings-p) - (memq major-mode undo-tree-incompatible-major-modes)) - (when print-message - (message "Buffer does not support undo-tree-mode;\ - undo-tree-mode NOT enabled")) - (undo-tree-mode 1))) - - -(defun undo-tree-overridden-undo-bindings-p () - "Returns t if default undo bindings are overridden, nil otherwise. -Checks if either of the default undo key bindings (\"C-/\" or -\"C-_\") are overridden in the current buffer by any keymap other -than the global one. (So global redefinitions of the default undo -key bindings do not count.)" - (let ((binding1 (lookup-key (current-global-map) [?\C-/])) - (binding2 (lookup-key (current-global-map) [?\C-_]))) - (global-set-key [?\C-/] 'undo) - (global-set-key [?\C-_] 'undo) - (unwind-protect - (or (and (key-binding [?\C-/]) - (not (eq (key-binding [?\C-/]) 'undo))) - (and (key-binding [?\C-_]) - (not (eq (key-binding [?\C-_]) 'undo)))) - (global-set-key [?\C-/] binding1) - (global-set-key [?\C-_] binding2)))) - - -;;;###autoload -(define-globalized-minor-mode global-undo-tree-mode - undo-tree-mode turn-on-undo-tree-mode) - - - -(defun undo-tree-undo (&optional arg) - "Undo changes. -Repeat this command to undo more changes. -A numeric ARG serves as a repeat count. - -In Transient Mark mode when the mark is active, only undo changes -within the current region. Similarly, when not in Transient Mark -mode, just \\[universal-argument] as an argument limits undo to -changes within the current region." - (interactive "*P") - (unless undo-tree-mode - (user-error "Undo-tree mode not enabled in buffer")) - ;; throw error if undo is disabled in buffer - (when (eq buffer-undo-list t) - (user-error "No undo information in this buffer")) - (undo-tree-undo-1 arg) - ;; inform user if at branch point - (when (> (undo-tree-num-branches) 1) (message "Undo branch point!"))) - - -(defun undo-tree-undo-1 (&optional arg preserve-redo preserve-timestamps) - ;; Internal undo function. An active mark in `transient-mark-mode', or - ;; non-nil ARG otherwise, enables undo-in-region. Non-nil PRESERVE-REDO - ;; causes the existing redo record to be preserved, rather than replacing it - ;; with the new one generated by undoing. Non-nil PRESERVE-TIMESTAMPS - ;; disables updating of timestamps in visited undo-tree nodes. (This latter - ;; should *only* be used when temporarily visiting another undo state and - ;; immediately returning to the original state afterwards. Otherwise, it - ;; could cause history-discarding errors.) - (let ((undo-in-progress t) - (undo-in-region (and undo-tree-enable-undo-in-region - (or (region-active-p) - (and arg (not (numberp arg)))))) - pos current) - ;; transfer entries accumulated in `buffer-undo-list' to - ;; `buffer-undo-tree' - (undo-list-transfer-to-tree) - - (dotimes (_ (or (and (numberp arg) (prefix-numeric-value arg)) 1)) - ;; check if at top of undo tree - (unless (undo-tree-node-previous (undo-tree-current buffer-undo-tree)) - (user-error "No further undo information")) - - ;; if region is active, or a non-numeric prefix argument was supplied, - ;; try to pull out a new branch of changes affecting the region - (when (and undo-in-region - (not (undo-tree-pull-undo-in-region-branch - (region-beginning) (region-end)))) - (user-error "No further undo information for region")) - - ;; remove any GC'd elements from node's undo list - (setq current (undo-tree-current buffer-undo-tree)) - (cl-decf (undo-tree-size buffer-undo-tree) - (undo-list-byte-size (undo-tree-node-undo current))) - (setf (undo-tree-node-undo current) - (undo-list-clean-GCd-elts (undo-tree-node-undo current))) - (cl-incf (undo-tree-size buffer-undo-tree) - (undo-list-byte-size (undo-tree-node-undo current))) - ;; undo one record from undo tree - (when undo-in-region - (setq pos (set-marker (make-marker) (point))) - (set-marker-insertion-type pos t)) - (primitive-undo 1 (undo-tree-copy-list (undo-tree-node-undo current))) - (undo-boundary) - - ;; if preserving old redo record, discard new redo entries that - ;; `primitive-undo' has added to `buffer-undo-list', and remove any GC'd - ;; elements from node's redo list - (if preserve-redo - (progn - (undo-list-pop-changeset buffer-undo-list) - (cl-decf (undo-tree-size buffer-undo-tree) - (undo-list-byte-size (undo-tree-node-redo current))) - (setf (undo-tree-node-redo current) - (undo-list-clean-GCd-elts (undo-tree-node-redo current))) - (cl-incf (undo-tree-size buffer-undo-tree) - (undo-list-byte-size (undo-tree-node-redo current)))) - ;; otherwise, record redo entries that `primitive-undo' has added to - ;; `buffer-undo-list' in current node's redo record, replacing - ;; existing entry if one already exists - (cl-decf (undo-tree-size buffer-undo-tree) - (undo-list-byte-size (undo-tree-node-redo current))) - (setf (undo-tree-node-redo current) - (undo-list-pop-changeset buffer-undo-list 'discard-pos)) - (cl-incf (undo-tree-size buffer-undo-tree) - (undo-list-byte-size (undo-tree-node-redo current)))) - - ;; rewind current node and update timestamp - (setf (undo-tree-current buffer-undo-tree) - (undo-tree-node-previous (undo-tree-current buffer-undo-tree))) - (unless preserve-timestamps - (setf (undo-tree-node-timestamp (undo-tree-current buffer-undo-tree)) - (current-time))) - - ;; if undoing-in-region, record current node, region and direction so we - ;; can tell if undo-in-region is repeated, and re-activate mark if in - ;; `transient-mark-mode'; if not, erase any leftover data - (if (not undo-in-region) - (undo-tree-node-clear-region-data current) - (goto-char pos) - ;; note: we deliberately want to store the region information in the - ;; node *below* the now current one - (setf (undo-tree-node-undo-beginning current) (region-beginning) - (undo-tree-node-undo-end current) (region-end)) - (set-marker pos nil))) - - ;; undo deactivates mark unless undoing-in-region - (setq deactivate-mark (not undo-in-region)))) - - - -(defun undo-tree-redo (&optional arg) - "Redo changes. A numeric ARG serves as a repeat count. - -In Transient Mark mode when the mark is active, only redo changes -within the current region. Similarly, when not in Transient Mark -mode, just \\[universal-argument] as an argument limits redo to -changes within the current region." - (interactive "*P") - (unless undo-tree-mode - (user-error "Undo-tree mode not enabled in buffer")) - ;; throw error if undo is disabled in buffer - (when (eq buffer-undo-list t) - (user-error "No undo information in this buffer")) - (undo-tree-redo-1 arg) - ;; inform user if at branch point - (when (> (undo-tree-num-branches) 1) (message "Undo branch point!"))) - - -(defun undo-tree-redo-1 (&optional arg preserve-undo preserve-timestamps) - ;; Internal redo function. An active mark in `transient-mark-mode', or - ;; non-nil ARG otherwise, enables undo-in-region. Non-nil PRESERVE-UNDO - ;; causes the existing redo record to be preserved, rather than replacing it - ;; with the new one generated by undoing. Non-nil PRESERVE-TIMESTAMPS - ;; disables updating of timestamps in visited undo-tree nodes. (This latter - ;; should *only* be used when temporarily visiting another undo state and - ;; immediately returning to the original state afterwards. Otherwise, it - ;; could cause history-discarding errors.) - (let ((undo-in-progress t) - (redo-in-region (and undo-tree-enable-undo-in-region - (or (region-active-p) - (and arg (not (numberp arg)))))) - pos current) - ;; transfer entries accumulated in `buffer-undo-list' to - ;; `buffer-undo-tree' - (undo-list-transfer-to-tree) - - (dotimes (_ (or (and (numberp arg) (prefix-numeric-value arg)) 1)) - ;; check if at bottom of undo tree - (when (null (undo-tree-node-next (undo-tree-current buffer-undo-tree))) - (user-error "No further redo information")) - - ;; if region is active, or a non-numeric prefix argument was supplied, - ;; try to pull out a new branch of changes affecting the region - (when (and redo-in-region - (not (undo-tree-pull-redo-in-region-branch - (region-beginning) (region-end)))) - (user-error "No further redo information for region")) - - ;; get next node (but DON'T advance current node in tree yet, in case - ;; redoing fails) - (setq current (undo-tree-current buffer-undo-tree) - current (nth (undo-tree-node-branch current) - (undo-tree-node-next current))) - ;; remove any GC'd elements from node's redo list - (cl-decf (undo-tree-size buffer-undo-tree) - (undo-list-byte-size (undo-tree-node-redo current))) - (setf (undo-tree-node-redo current) - (undo-list-clean-GCd-elts (undo-tree-node-redo current))) - (cl-incf (undo-tree-size buffer-undo-tree) - (undo-list-byte-size (undo-tree-node-redo current))) - ;; redo one record from undo tree - (when redo-in-region - (setq pos (set-marker (make-marker) (point))) - (set-marker-insertion-type pos t)) - (primitive-undo 1 (undo-tree-copy-list (undo-tree-node-redo current))) - (undo-boundary) - ;; advance current node in tree - (setf (undo-tree-current buffer-undo-tree) current) - - ;; if preserving old undo record, discard new undo entries that - ;; `primitive-undo' has added to `buffer-undo-list', and remove any GC'd - ;; elements from node's redo list - (if preserve-undo - (progn - (undo-list-pop-changeset buffer-undo-list) - (cl-decf (undo-tree-size buffer-undo-tree) - (undo-list-byte-size (undo-tree-node-undo current))) - (setf (undo-tree-node-undo current) - (undo-list-clean-GCd-elts (undo-tree-node-undo current))) - (cl-incf (undo-tree-size buffer-undo-tree) - (undo-list-byte-size (undo-tree-node-undo current)))) - ;; otherwise, record undo entries that `primitive-undo' has added to - ;; `buffer-undo-list' in current node's undo record, replacing - ;; existing entry if one already exists - (cl-decf (undo-tree-size buffer-undo-tree) - (undo-list-byte-size (undo-tree-node-undo current))) - (setf (undo-tree-node-undo current) - (undo-list-pop-changeset buffer-undo-list 'discard-pos)) - (cl-incf (undo-tree-size buffer-undo-tree) - (undo-list-byte-size (undo-tree-node-undo current)))) - - ;; update timestamp - (unless preserve-timestamps - (setf (undo-tree-node-timestamp current) (current-time))) - - ;; if redoing-in-region, record current node, region and direction so we - ;; can tell if redo-in-region is repeated, and re-activate mark if in - ;; `transient-mark-mode' - (if (not redo-in-region) - (undo-tree-node-clear-region-data current) - (goto-char pos) - (setf (undo-tree-node-redo-beginning current) (region-beginning) - (undo-tree-node-redo-end current) (region-end)) - (set-marker pos nil))) - - ;; redo deactivates the mark unless redoing-in-region - (setq deactivate-mark (not redo-in-region)))) - - - -(defun undo-tree-switch-branch (branch) - "Switch to a different BRANCH of the undo tree. -This will affect which branch to descend when *redoing* changes -using `undo-tree-redo'." - (interactive (list (or (and prefix-arg (prefix-numeric-value prefix-arg)) - (and (not (eq buffer-undo-list t)) - (undo-list-transfer-to-tree) - (let ((b (undo-tree-node-branch - (undo-tree-current - buffer-undo-tree)))) - (cond - ;; switch to other branch if only 2 - ((= (undo-tree-num-branches) 2) (- 1 b)) - ;; prompt if more than 2 - ((> (undo-tree-num-branches) 2) - (read-number - (format "Branch (0-%d, on %d): " - (1- (undo-tree-num-branches)) b))) - )))))) - (unless undo-tree-mode - (user-error "Undo-tree mode not enabled in buffer")) - ;; throw error if undo is disabled in buffer - (when (eq buffer-undo-list t) - (user-error "No undo information in this buffer")) - ;; sanity check branch number - (when (<= (undo-tree-num-branches) 1) - (user-error "Not at undo branch point")) - (when (or (< branch 0) (> branch (1- (undo-tree-num-branches)))) - (user-error "Invalid branch number")) - ;; transfer entries accumulated in `buffer-undo-list' to `buffer-undo-tree' - (undo-list-transfer-to-tree) - ;; switch branch - (setf (undo-tree-node-branch (undo-tree-current buffer-undo-tree)) - branch) - (message "Switched to branch %d" branch)) - - -(defun undo-tree-set (node &optional preserve-timestamps) - ;; Set buffer to state corresponding to NODE. Returns intersection point - ;; between path back from current node and path back from selected NODE. - ;; Non-nil PRESERVE-TIMESTAMPS disables updating of timestamps in visited - ;; undo-tree nodes. (This should *only* be used when temporarily visiting - ;; another undo state and immediately returning to the original state - ;; afterwards. Otherwise, it could cause history-discarding errors.) - (let ((path (make-hash-table :test 'eq)) - (n node)) - (puthash (undo-tree-root buffer-undo-tree) t path) - ;; build list of nodes leading back from selected node to root, updating - ;; branches as we go to point down to selected node - (while (progn - (puthash n t path) - (when (undo-tree-node-previous n) - (setf (undo-tree-node-branch (undo-tree-node-previous n)) - (undo-tree-position - n (undo-tree-node-next (undo-tree-node-previous n)))) - (setq n (undo-tree-node-previous n))))) - ;; work backwards from current node until we intersect path back from - ;; selected node - (setq n (undo-tree-current buffer-undo-tree)) - (while (not (gethash n path)) - (setq n (undo-tree-node-previous n))) - ;; ascend tree until intersection node - (while (not (eq (undo-tree-current buffer-undo-tree) n)) - (undo-tree-undo-1 nil nil preserve-timestamps)) - ;; descend tree until selected node - (while (not (eq (undo-tree-current buffer-undo-tree) node)) - (undo-tree-redo-1 nil nil preserve-timestamps)) - n)) ; return intersection node - - - -(defun undo-tree-save-state-to-register (register) - "Store current undo-tree state to REGISTER. -The saved state can be restored using -`undo-tree-restore-state-from-register'. -Argument is a character, naming the register." - (interactive "cUndo-tree state to register: ") - (unless undo-tree-mode - (user-error "Undo-tree mode not enabled in buffer")) - ;; throw error if undo is disabled in buffer - (when (eq buffer-undo-list t) - (user-error "No undo information in this buffer")) - ;; transfer entries accumulated in `buffer-undo-list' to `buffer-undo-tree' - (undo-list-transfer-to-tree) - ;; save current node to REGISTER - (set-register - register (registerv-make - (undo-tree-make-register-data - (current-buffer) (undo-tree-current buffer-undo-tree)) - :print-func 'undo-tree-register-data-print-func)) - ;; record REGISTER in current node, for visualizer - (setf (undo-tree-node-register (undo-tree-current buffer-undo-tree)) - register)) - - - -(defun undo-tree-restore-state-from-register (register) - "Restore undo-tree state from REGISTER. -The state must be saved using `undo-tree-save-state-to-register'. -Argument is a character, naming the register." - (interactive "*cRestore undo-tree state from register: ") - (unless undo-tree-mode - (user-error "Undo-tree mode not enabled in buffer")) - ;; throw error if undo is disabled in buffer, or if register doesn't contain - ;; an undo-tree node - (let ((data (registerv-data (get-register register)))) - (cond - ((eq buffer-undo-list t) - (user-error "No undo information in this buffer")) - ((not (undo-tree-register-data-p data)) - (user-error "Register doesn't contain undo-tree state")) - ((not (eq (current-buffer) (undo-tree-register-data-buffer data))) - (user-error "Register contains undo-tree state for a different buffer"))) - ;; transfer entries accumulated in `buffer-undo-list' to `buffer-undo-tree' - (undo-list-transfer-to-tree) - ;; restore buffer state corresponding to saved node - (undo-tree-set (undo-tree-register-data-node data)))) - - - - -;;; ===================================================================== -;;; Undo-tree menu bar - -(defvar undo-tree-old-undo-menu-item nil) - -(defun undo-tree-update-menu-bar () - "Update `undo-tree-mode' Edit menu items." - (if undo-tree-mode - (progn - ;; save old undo menu item, and install undo/redo menu items - (setq undo-tree-old-undo-menu-item - (cdr (assq 'undo (lookup-key global-map [menu-bar edit])))) - (define-key (lookup-key global-map [menu-bar edit]) - [undo] '(menu-item "Undo" undo-tree-undo - :enable (and undo-tree-mode - (not buffer-read-only) - (not (eq t buffer-undo-list)) - (not (eq nil buffer-undo-tree)) - (undo-tree-node-previous - (undo-tree-current buffer-undo-tree))) - :help "Undo last operation")) - (define-key-after (lookup-key global-map [menu-bar edit]) - [redo] '(menu-item "Redo" undo-tree-redo - :enable (and undo-tree-mode - (not buffer-read-only) - (not (eq t buffer-undo-list)) - (not (eq nil buffer-undo-tree)) - (undo-tree-node-next - (undo-tree-current buffer-undo-tree))) - :help "Redo last operation") - 'undo)) - ;; uninstall undo/redo menu items - (define-key (lookup-key global-map [menu-bar edit]) - [undo] undo-tree-old-undo-menu-item) - (define-key (lookup-key global-map [menu-bar edit]) - [redo] nil))) - -(add-hook 'menu-bar-update-hook 'undo-tree-update-menu-bar) - - - - -;;; ===================================================================== -;;; Persistent storage commands - -(defun undo-tree-make-history-save-file-name (file) - "Create the undo history file name for FILE. -Normally this is the file's name with \".\" prepended and -\".~undo-tree~\" appended. - -A match for FILE is sought in `undo-tree-history-directory-alist' -\(see the documentation of that variable for details\). If the -directory for the backup doesn't exist, it is created." - (let* ((backup-directory-alist undo-tree-history-directory-alist) - (name (make-backup-file-name-1 file))) - (concat (file-name-directory name) "." (file-name-nondirectory name) - ".~undo-tree~"))) - - -(defun undo-tree-save-history (&optional filename overwrite) - "Store undo-tree history to file. - -If optional argument FILENAME is omitted, default save file is -\"..~undo-tree\" if buffer is visiting a file. -Otherwise, prompt for one. - -If OVERWRITE is non-nil, any existing file will be overwritten -without asking for confirmation." - (interactive) - (unless undo-tree-mode - (user-error "Undo-tree mode not enabled in buffer")) - (when (eq buffer-undo-list t) - (user-error "No undo information in this buffer")) - (undo-list-transfer-to-tree) - (when (and buffer-undo-tree (not (eq buffer-undo-tree t))) - (undo-tree-kill-visualizer) - ;; should be cleared already by killing the visualizer, but writes - ;; unreasable data if not for some reason, so just in case... - (undo-tree-clear-visualizer-data buffer-undo-tree) - (let ((buff (current-buffer)) - tree) - ;; get filename - (unless filename - (setq filename - (if buffer-file-name - (undo-tree-make-history-save-file-name buffer-file-name) - (expand-file-name (read-file-name "File to save in: ") nil)))) - (when (or (not (file-exists-p filename)) - overwrite - (yes-or-no-p (format "Overwrite \"%s\"? " filename))) - ;; transform undo-tree into non-circular structure, and make tmp copy - (setq tree (undo-tree-copy buffer-undo-tree)) - (undo-tree-decircle tree) - ;; discard undo-tree object pool before saving - (setf (undo-tree-object-pool tree) nil) - ;; run pre-save transformer functions - (when undo-tree-pre-save-element-functions - (undo-tree-mapc - (lambda (node) - (let ((changeset (undo-tree-node-undo node))) - (run-hook-wrapped - 'undo-tree-pre-save-element-functions - (lambda (fun) - (setq changeset (delq nil (mapcar fun changeset))))) - (setf (undo-tree-node-undo node) changeset)) - (let ((changeset (undo-tree-node-redo node))) - (run-hook-wrapped - 'undo-tree-pre-save-element-functions - (lambda (fun) - (setq changeset (delq nil (mapcar fun changeset))))) - (setf (undo-tree-node-redo node) changeset))) - (undo-tree-root tree))) - ;; print undo-tree to file - ;; NOTE: We use `with-temp-buffer' instead of `with-temp-file' to - ;; allow `auto-compression-mode' to take effect, in case user - ;; has overridden or advised the default - ;; `undo-tree-make-history-save-file-name' to add a compressed - ;; file extension. - (with-auto-compression-mode - (with-temp-buffer - (prin1 (sha1 buff) (current-buffer)) - (terpri (current-buffer)) - (let ((print-circle t)) (prin1 tree (current-buffer))) - (write-region nil nil filename))))))) - - - -(defun undo-tree-load-history (&optional filename noerror) - "Load undo-tree history from file, for the current buffer. - -If optional argument FILENAME is null, default load file is -\"..~undo-tree\" if buffer is visiting a file. -Otherwise, prompt for one. - -If optional argument NOERROR is non-nil, return nil instead of -signaling an error if file is not found. - -Note this will overwrite any existing undo history." - (interactive) - (unless undo-tree-mode - (user-error "Undo-tree mode not enabled in buffer")) - ;; get filename - (unless filename - (setq filename - (if buffer-file-name - (undo-tree-make-history-save-file-name buffer-file-name) - (expand-file-name (read-file-name "File to load from: ") nil)))) - - ;; attempt to read undo-tree from FILENAME - (catch 'load-error - (unless (file-exists-p filename) - (if noerror - (throw 'load-error nil) - (error "File \"%s\" does not exist; could not load undo-tree history" - filename))) - (let (buff hash tree) - (setq buff (current-buffer)) - (with-auto-compression-mode - (with-temp-buffer - (insert-file-contents filename) - (goto-char (point-min)) - (condition-case nil - (setq hash (read (current-buffer))) - (error - (kill-buffer nil) - (funcall (if noerror #'message #'user-error) - "Error reading undo-tree history from \"%s\"" filename) - (throw 'load-error nil))) - (unless (string= (sha1 buff) hash) - (kill-buffer nil) - (funcall (if noerror 'message 'user-error) - "Buffer has been modified; could not load undo-tree history") - (throw 'load-error nil)) - (condition-case nil - (setq tree (read (current-buffer))) - (error - (kill-buffer nil) - (funcall (if noerror #'message #'error) - "Error reading undo-tree history from \"%s\"" filename) - (throw 'load-error nil))) - (kill-buffer nil))) - ;; run post-load transformer functions - (when undo-tree-post-load-element-functions - (undo-tree-mapc - (lambda (node) - (let ((changeset (undo-tree-node-undo node))) - (run-hook-wrapped - 'undo-tree-post-load-element-functions - (lambda (fun) - (setq changeset (delq nil (mapcar fun changeset))))) - (setf (undo-tree-node-undo node) changeset)) - (let ((changeset (undo-tree-node-redo node))) - (run-hook-wrapped - 'undo-tree-post-load-element-functions - (lambda (fun) - (setq changeset (delq nil (mapcar fun changeset))))) - (setf (undo-tree-node-redo node) changeset))) - (undo-tree-root tree))) ;; initialise empty undo-tree object pool - (setf (undo-tree-object-pool tree) - (make-hash-table :test 'eq :weakness 'value)) - ;; restore circular undo-tree data structure - (undo-tree-recircle tree) - ;; create undo-tree object pool - (setf (undo-tree-object-pool tree) - (make-hash-table :test 'eq :weakness 'value)) - (setq buffer-undo-tree tree - buffer-undo-list '(nil undo-tree-canary))))) - - - -;; Versions of save/load functions for use in hooks -(defun undo-tree-save-history-from-hook () - (when (and undo-tree-mode undo-tree-auto-save-history - (not (eq buffer-undo-list t)) - buffer-file-name - (file-writable-p - (undo-tree-make-history-save-file-name buffer-file-name))) - (undo-tree-save-history nil 'overwrite) nil)) - -(define-obsolete-function-alias - 'undo-tree-save-history-hook 'undo-tree-save-history-from-hook - "`undo-tree-save-history-hook' is obsolete since undo-tree - version 0.6.6. Use `undo-tree-save-history-from-hook' instead.") - - -(defun undo-tree-load-history-from-hook () - (when (and undo-tree-mode undo-tree-auto-save-history - (not (eq buffer-undo-list t)) - (not revert-buffer-in-progress-p)) - (undo-tree-load-history nil 'noerror))) - -(define-obsolete-function-alias - 'undo-tree-load-history-hook 'undo-tree-load-history-from-hook - "`undo-tree-load-history-hook' is obsolete since undo-tree - version 0.6.6. Use `undo-tree-load-history-from-hook' instead.") - - -;; install history-auto-save hooks -(add-hook 'write-file-functions #'undo-tree-save-history-from-hook) -(add-hook 'kill-buffer-hook #'undo-tree-save-history-from-hook) -(add-hook 'find-file-hook #'undo-tree-load-history-from-hook) - - - - -;;; ===================================================================== -;;; Visualizer drawing functions - -(defun undo-tree-visualize () - "Visualize the current buffer's undo tree." - (interactive "*") - (unless undo-tree-mode - (user-error "Undo-tree mode not enabled in buffer")) - (deactivate-mark) - ;; throw error if undo is disabled in buffer - (when (eq buffer-undo-list t) - (user-error "No undo information in this buffer")) - ;; transfer entries accumulated in `buffer-undo-list' to `buffer-undo-tree' - (undo-list-transfer-to-tree) - ;; add hook to kill visualizer buffer if original buffer is changed - (add-hook 'before-change-functions 'undo-tree-kill-visualizer nil t) - ;; prepare *undo-tree* buffer, then draw tree in it - (let ((undo-tree buffer-undo-tree) - (buff (current-buffer)) - (display-buffer-mark-dedicated 'soft)) - (switch-to-buffer-other-window - (get-buffer-create undo-tree-visualizer-buffer-name)) - (setq undo-tree-visualizer-parent-buffer buff) - (setq undo-tree-visualizer-parent-mtime - (and (buffer-file-name buff) - (nth 5 (file-attributes (buffer-file-name buff))))) - (setq undo-tree-visualizer-initial-node (undo-tree-current undo-tree)) - (setq undo-tree-visualizer-spacing - (undo-tree-visualizer-calculate-spacing)) - (make-local-variable 'undo-tree-visualizer-timestamps) - (make-local-variable 'undo-tree-visualizer-diff) - (setq buffer-undo-tree undo-tree) - (undo-tree-visualizer-mode) - ;; FIXME; don't know why `undo-tree-visualizer-mode' clears this - (setq buffer-undo-tree undo-tree) - (set (make-local-variable 'undo-tree-visualizer-lazy-drawing) - (or (eq undo-tree-visualizer-lazy-drawing t) - (and (numberp undo-tree-visualizer-lazy-drawing) - (>= (undo-tree-count undo-tree) - undo-tree-visualizer-lazy-drawing)))) - (when undo-tree-visualizer-diff (undo-tree-visualizer-show-diff)) - (let ((inhibit-read-only t)) (undo-tree-draw-tree undo-tree)))) - - -(defun undo-tree-kill-visualizer (&rest _dummy) - ;; Kill visualizer. Added to `before-change-functions' hook of original - ;; buffer when visualizer is invoked. - (unless (or undo-tree-inhibit-kill-visualizer - (null (get-buffer undo-tree-visualizer-buffer-name))) - (with-current-buffer undo-tree-visualizer-buffer-name - (undo-tree-visualizer-quit)))) - - - -(defun undo-tree-draw-tree (undo-tree) - ;; Draw undo-tree in current buffer starting from NODE (or root if nil). - (let ((inhibit-read-only t) - (node (if undo-tree-visualizer-lazy-drawing - (undo-tree-current undo-tree) - (undo-tree-root undo-tree)))) - (erase-buffer) - (setq undo-tree-visualizer-needs-extending-down nil - undo-tree-visualizer-needs-extending-up nil) - (undo-tree-clear-visualizer-data undo-tree) - (undo-tree-compute-widths node) - ;; lazy drawing starts vertically centred and displaced horizontally to - ;; the left (window-width/4), since trees will typically grow right - (if undo-tree-visualizer-lazy-drawing - (progn - (undo-tree-move-down (/ (window-height) 2)) - (undo-tree-move-forward (max 2 (/ (window-width) 4)))) ; left margin - ;; non-lazy drawing starts in centre at top of buffer - (undo-tree-move-down 1) ; top margin - (undo-tree-move-forward - (max (/ (window-width) 2) - (+ (undo-tree-node-char-lwidth node) - ;; add space for left part of left-most time-stamp - (if undo-tree-visualizer-timestamps - (/ (- undo-tree-visualizer-spacing 4) 2) - 0) - 2)))) ; left margin - ;; link starting node to its representation in visualizer - (setf (undo-tree-node-marker node) (make-marker)) - (set-marker-insertion-type (undo-tree-node-marker node) nil) - (move-marker (undo-tree-node-marker node) (point)) - ;; draw undo-tree - (let ((undo-tree-insert-face 'undo-tree-visualizer-default-face) - node-list) - (if (not undo-tree-visualizer-lazy-drawing) - (undo-tree-extend-down node t) - (undo-tree-extend-down node) - (undo-tree-extend-up node) - (setq node-list undo-tree-visualizer-needs-extending-down - undo-tree-visualizer-needs-extending-down nil) - (while node-list (undo-tree-extend-down (pop node-list))))) - ;; highlight active branch - (let ((undo-tree-insert-face 'undo-tree-visualizer-active-branch-face)) - (undo-tree-highlight-active-branch - (or undo-tree-visualizer-needs-extending-up - (undo-tree-root undo-tree)))) - ;; highlight current node - (undo-tree-draw-node (undo-tree-current undo-tree) 'current))) - - -(defun undo-tree-extend-down (node &optional bottom) - ;; Extend tree downwards starting from NODE and point. If BOTTOM is t, - ;; extend all the way down to the leaves. If BOTTOM is a node, extend down - ;; as far as that node. If BOTTOM is an integer, extend down as far as that - ;; line. Otherwise, only extend visible portion of tree. NODE is assumed to - ;; already have a node marker. Returns non-nil if anything was actually - ;; extended. - (let ((extended nil) - (cur-stack (list node)) - next-stack) - ;; don't bother extending if BOTTOM specifies an already-drawn node - (unless (and (undo-tree-node-p bottom) (undo-tree-node-marker bottom)) - ;; draw nodes layer by layer - (while (or cur-stack - (prog1 (setq cur-stack next-stack) - (setq next-stack nil))) - (setq node (pop cur-stack)) - ;; if node is within range being drawn... - (if (or (eq bottom t) - (and (undo-tree-node-p bottom) - (not (eq (undo-tree-node-previous node) bottom))) - (and (integerp bottom) - (>= bottom (line-number-at-pos - (undo-tree-node-marker node)))) - (and (null bottom) - (pos-visible-in-window-p (undo-tree-node-marker node) - nil t))) - ;; ...draw one layer of node's subtree (if not already drawn) - (progn - (unless (and (undo-tree-node-next node) - (undo-tree-node-marker - (nth (undo-tree-node-branch node) - (undo-tree-node-next node)))) - (goto-char (undo-tree-node-marker node)) - (undo-tree-draw-subtree node) - (setq extended t)) - (setq next-stack - (append (undo-tree-node-next node) next-stack))) - ;; ...otherwise, postpone drawing until later - (push node undo-tree-visualizer-needs-extending-down)))) - extended)) - - -(defun undo-tree-extend-up (node &optional top) - ;; Extend tree upwards starting from NODE. If TOP is t, extend all the way - ;; to root. If TOP is a node, extend up as far as that node. If TOP is an - ;; integer, extend up as far as that line. Otherwise, only extend visible - ;; portion of tree. NODE is assumed to already have a node marker. Returns - ;; non-nil if anything was actually extended. - (let ((extended nil) parent) - ;; don't bother extending if TOP specifies an already-drawn node - (unless (and (undo-tree-node-p top) (undo-tree-node-marker top)) - (while node - (setq parent (undo-tree-node-previous node)) - ;; if we haven't reached root... - (if parent - ;; ...and node is within range being drawn... - (if (or (eq top t) - (and (undo-tree-node-p top) (not (eq node top))) - (and (integerp top) - (< top (line-number-at-pos - (undo-tree-node-marker node)))) - (and (null top) - ;; NOTE: we check point in case window-start is outdated - (< (min (line-number-at-pos (point)) - (line-number-at-pos (window-start))) - (line-number-at-pos - (undo-tree-node-marker node))))) - ;; ...and it hasn't already been drawn - (when (not (undo-tree-node-marker parent)) - ;; link parent node to its representation in visualizer - (undo-tree-compute-widths parent) - (undo-tree-move-to-parent node) - (setf (undo-tree-node-marker parent) (make-marker)) - (set-marker-insertion-type - (undo-tree-node-marker parent) nil) - (move-marker (undo-tree-node-marker parent) (point)) - ;; draw subtree beneath parent - (setq undo-tree-visualizer-needs-extending-down - (nconc (delq node (undo-tree-draw-subtree parent)) - undo-tree-visualizer-needs-extending-down)) - (setq extended t)) - ;; ...otherwise, postpone drawing for later and exit - (setq undo-tree-visualizer-needs-extending-up (when parent node) - parent nil)) - - ;; if we've reached root, stop extending and add top margin - (setq undo-tree-visualizer-needs-extending-up nil) - (goto-char (undo-tree-node-marker node)) - (undo-tree-move-up 1) ; top margin - (delete-region (point-min) (line-beginning-position))) - ;; next iteration - (setq node parent))) - extended)) - - -(defun undo-tree-expand-down (from &optional to) - ;; Expand tree downwards. FROM is the node to start expanding from. Stop - ;; expanding at TO if specified. Otherwise, just expand visible portion of - ;; tree and highlight active branch from FROM. - (when undo-tree-visualizer-needs-extending-down - (let ((inhibit-read-only t) - node-list extended) - ;; extend down as far as TO node - (when to - (setq extended (undo-tree-extend-down from to)) - (goto-char (undo-tree-node-marker to)) - (redisplay t)) ; force redisplay to scroll buffer if necessary - ;; extend visible portion of tree downwards - (setq node-list undo-tree-visualizer-needs-extending-down - undo-tree-visualizer-needs-extending-down nil) - (when node-list - (dolist (n node-list) - (when (undo-tree-extend-down n) (setq extended t))) - ;; highlight active branch in newly-extended-down portion, if any - (when extended - (let ((undo-tree-insert-face - 'undo-tree-visualizer-active-branch-face)) - (undo-tree-highlight-active-branch from))))))) - - -(defun undo-tree-expand-up (from &optional to) - ;; Expand tree upwards. FROM is the node to start expanding from, TO is the - ;; node to stop expanding at. If TO node isn't specified, just expand visible - ;; portion of tree and highlight active branch down to FROM. - (when undo-tree-visualizer-needs-extending-up - (let ((inhibit-read-only t) - extended node-list) - ;; extend up as far as TO node - (when to - (setq extended (undo-tree-extend-up from to)) - (goto-char (undo-tree-node-marker to)) - ;; simulate auto-scrolling if close to top of buffer - (when (<= (line-number-at-pos (point)) scroll-margin) - (undo-tree-move-up (if (= scroll-conservatively 0) - (/ (window-height) 2) 3)) - (when (undo-tree-extend-up to) (setq extended t)) - (goto-char (undo-tree-node-marker to)) - (unless (= scroll-conservatively 0) (recenter scroll-margin)))) - ;; extend visible portion of tree upwards - (and undo-tree-visualizer-needs-extending-up - (undo-tree-extend-up undo-tree-visualizer-needs-extending-up) - (setq extended t)) - ;; extend visible portion of tree downwards - (setq node-list undo-tree-visualizer-needs-extending-down - undo-tree-visualizer-needs-extending-down nil) - (dolist (n node-list) (undo-tree-extend-down n)) - ;; highlight active branch in newly-extended-up portion, if any - (when extended - (let ((undo-tree-insert-face - 'undo-tree-visualizer-active-branch-face)) - (undo-tree-highlight-active-branch - (or undo-tree-visualizer-needs-extending-up - (undo-tree-root buffer-undo-tree)) - from)))))) - - - -(defun undo-tree-highlight-active-branch (node &optional end) - ;; Draw highlighted active branch below NODE in current buffer. Stop - ;; highlighting at END node if specified. - (let ((stack (list node))) - ;; draw active branch - (while stack - (setq node (pop stack)) - (unless (or (eq node end) - (memq node undo-tree-visualizer-needs-extending-down)) - (goto-char (undo-tree-node-marker node)) - (setq node (undo-tree-draw-subtree node 'active) - stack (nconc stack node)))))) - - -(defun undo-tree-draw-node (node &optional current) - ;; Draw symbol representing NODE in visualizer. If CURRENT is non-nil, node - ;; is current node. - (goto-char (undo-tree-node-marker node)) - (when undo-tree-visualizer-timestamps - (undo-tree-move-backward (/ undo-tree-visualizer-spacing 2))) - - (let* ((undo-tree-insert-face (and undo-tree-insert-face - (or (and (consp undo-tree-insert-face) - undo-tree-insert-face) - (list undo-tree-insert-face)))) - (register (undo-tree-node-register node)) - (unmodified (if undo-tree-visualizer-parent-mtime - (undo-tree-node-unmodified-p - node undo-tree-visualizer-parent-mtime) - (undo-tree-node-unmodified-p node))) - node-string) - ;; check node's register (if any) still stores appropriate undo-tree state - (unless (and register - (undo-tree-register-data-p - (registerv-data (get-register register))) - (eq node (undo-tree-register-data-node - (registerv-data (get-register register))))) - (setq register nil)) - ;; represent node by different symbols, depending on whether it's the - ;; current node, is saved in a register, or corresponds to an unmodified - ;; buffer - (setq node-string - (cond - (undo-tree-visualizer-timestamps - (undo-tree-timestamp-to-string - (undo-tree-node-timestamp node) - undo-tree-visualizer-relative-timestamps - current register)) - (register (char-to-string register)) - (unmodified "s") - (current "x") - (t "o")) - undo-tree-insert-face - (nconc - (cond - (current '(undo-tree-visualizer-current-face)) - (unmodified '(undo-tree-visualizer-unmodified-face)) - (register '(undo-tree-visualizer-register-face))) - undo-tree-insert-face)) - ;; draw node and link it to its representation in visualizer - (undo-tree-insert node-string) - (undo-tree-move-backward (if undo-tree-visualizer-timestamps - (1+ (/ undo-tree-visualizer-spacing 2)) - 1)) - (move-marker (undo-tree-node-marker node) (point)) - (put-text-property (point) (1+ (point)) 'undo-tree-node node))) - - -(defun undo-tree-draw-subtree (node &optional active-branch) - ;; Draw subtree rooted at NODE. The subtree will start from point. - ;; If ACTIVE-BRANCH is non-nil, just draw active branch below NODE. Returns - ;; list of nodes below NODE. - (let ((num-children (length (undo-tree-node-next node))) - node-list pos trunk-pos n) - ;; draw node itself - (undo-tree-draw-node node) - - (cond - ;; if we're at a leaf node, we're done - ((= num-children 0)) - - ;; if node has only one child, draw it (not strictly necessary to deal - ;; with this case separately, but as it's by far the most common case - ;; this makes the code clearer and more efficient) - ((= num-children 1) - (undo-tree-move-down 1) - (undo-tree-insert ?|) - (undo-tree-move-backward 1) - (undo-tree-move-down 1) - (undo-tree-insert ?|) - (undo-tree-move-backward 1) - (undo-tree-move-down 1) - (setq n (car (undo-tree-node-next node))) - ;; link next node to its representation in visualizer - (unless (markerp (undo-tree-node-marker n)) - (setf (undo-tree-node-marker n) (make-marker)) - (set-marker-insertion-type (undo-tree-node-marker n) nil)) - (move-marker (undo-tree-node-marker n) (point)) - ;; add next node to list of nodes to draw next - (push n node-list)) - - ;; if node has multiple children, draw branches - (t - (undo-tree-move-down 1) - (undo-tree-insert ?|) - (undo-tree-move-backward 1) - (move-marker (setq trunk-pos (make-marker)) (point)) - ;; left subtrees - (undo-tree-move-backward - (- (undo-tree-node-char-lwidth node) - (undo-tree-node-char-lwidth - (car (undo-tree-node-next node))))) - (move-marker (setq pos (make-marker)) (point)) - (setq n (cons nil (undo-tree-node-next node))) - (dotimes (_ (/ num-children 2)) - (setq n (cdr n)) - (when (or (null active-branch) - (eq (car n) - (nth (undo-tree-node-branch node) - (undo-tree-node-next node)))) - (undo-tree-move-forward 2) - (undo-tree-insert ?_ (- trunk-pos pos 2)) - (goto-char pos) - (undo-tree-move-forward 1) - (undo-tree-move-down 1) - (undo-tree-insert ?/) - (undo-tree-move-backward 2) - (undo-tree-move-down 1) - ;; link node to its representation in visualizer - (unless (markerp (undo-tree-node-marker (car n))) - (setf (undo-tree-node-marker (car n)) (make-marker)) - (set-marker-insertion-type (undo-tree-node-marker (car n)) nil)) - (move-marker (undo-tree-node-marker (car n)) (point)) - ;; add node to list of nodes to draw next - (push (car n) node-list)) - (goto-char pos) - (undo-tree-move-forward - (+ (undo-tree-node-char-rwidth (car n)) - (undo-tree-node-char-lwidth (cadr n)) - undo-tree-visualizer-spacing 1)) - (move-marker pos (point))) - ;; middle subtree (only when number of children is odd) - (when (= (mod num-children 2) 1) - (setq n (cdr n)) - (when (or (null active-branch) - (eq (car n) - (nth (undo-tree-node-branch node) - (undo-tree-node-next node)))) - (undo-tree-move-down 1) - (undo-tree-insert ?|) - (undo-tree-move-backward 1) - (undo-tree-move-down 1) - ;; link node to its representation in visualizer - (unless (markerp (undo-tree-node-marker (car n))) - (setf (undo-tree-node-marker (car n)) (make-marker)) - (set-marker-insertion-type (undo-tree-node-marker (car n)) nil)) - (move-marker (undo-tree-node-marker (car n)) (point)) - ;; add node to list of nodes to draw next - (push (car n) node-list)) - (goto-char pos) - (undo-tree-move-forward - (+ (undo-tree-node-char-rwidth (car n)) - (if (cadr n) (undo-tree-node-char-lwidth (cadr n)) 0) - undo-tree-visualizer-spacing 1)) - (move-marker pos (point))) - ;; right subtrees - (move-marker trunk-pos (1+ trunk-pos)) - (dotimes (_ (/ num-children 2)) - (setq n (cdr n)) - (when (or (null active-branch) - (eq (car n) - (nth (undo-tree-node-branch node) - (undo-tree-node-next node)))) - (goto-char trunk-pos) - (undo-tree-insert ?_ (- pos trunk-pos 1)) - (goto-char pos) - (undo-tree-move-backward 1) - (undo-tree-move-down 1) - (undo-tree-insert ?\\) - (undo-tree-move-down 1) - ;; link node to its representation in visualizer - (unless (markerp (undo-tree-node-marker (car n))) - (setf (undo-tree-node-marker (car n)) (make-marker)) - (set-marker-insertion-type (undo-tree-node-marker (car n)) nil)) - (move-marker (undo-tree-node-marker (car n)) (point)) - ;; add node to list of nodes to draw next - (push (car n) node-list)) - (when (cdr n) - (goto-char pos) - (undo-tree-move-forward - (+ (undo-tree-node-char-rwidth (car n)) - (if (cadr n) (undo-tree-node-char-lwidth (cadr n)) 0) - undo-tree-visualizer-spacing 1)) - (move-marker pos (point)))) - )) - ;; return list of nodes to draw next - (nreverse node-list))) - - -(defun undo-tree-node-char-lwidth (node) - ;; Return left-width of NODE measured in characters. - (if (= (length (undo-tree-node-next node)) 0) 0 - (- (* (+ undo-tree-visualizer-spacing 1) (undo-tree-node-lwidth node)) - (if (= (undo-tree-node-cwidth node) 0) - (1+ (/ undo-tree-visualizer-spacing 2)) 0)))) - - -(defun undo-tree-node-char-rwidth (node) - ;; Return right-width of NODE measured in characters. - (if (= (length (undo-tree-node-next node)) 0) 0 - (- (* (+ undo-tree-visualizer-spacing 1) (undo-tree-node-rwidth node)) - (if (= (undo-tree-node-cwidth node) 0) - (1+ (/ undo-tree-visualizer-spacing 2)) 0)))) - - -(defun undo-tree-insert (str &optional arg) - ;; Insert character or string STR ARG times, overwriting, and using - ;; `undo-tree-insert-face'. - (unless arg (setq arg 1)) - (when (characterp str) - (setq str (make-string arg str)) - (setq arg 1)) - (dotimes (_ arg) (insert str)) - (setq arg (* arg (length str))) - (undo-tree-move-forward arg) - ;; make sure mark isn't active, otherwise `backward-delete-char' might - ;; delete region instead of single char if transient-mark-mode is enabled - (setq mark-active nil) - (backward-delete-char arg) - (when undo-tree-insert-face - (put-text-property (- (point) arg) (point) 'face undo-tree-insert-face))) - - -(defun undo-tree-move-down (&optional arg) - ;; Move down, extending buffer if necessary. - (let ((row (line-number-at-pos)) - (col (current-column)) - line) - (unless arg (setq arg 1)) - (forward-line arg) - (setq line (line-number-at-pos)) - ;; if buffer doesn't have enough lines, add some - (when (/= line (+ row arg)) - (cond - ((< arg 0) - (insert (make-string (- line row arg) ?\n)) - (forward-line (+ arg (- row line)))) - (t (insert (make-string (- arg (- line row)) ?\n))))) - (undo-tree-move-forward col))) - - -(defun undo-tree-move-up (&optional arg) - ;; Move up, extending buffer if necessary. - (unless arg (setq arg 1)) - (undo-tree-move-down (- arg))) - - -(defun undo-tree-move-forward (&optional arg) - ;; Move forward, extending buffer if necessary. - (unless arg (setq arg 1)) - (let (n) - (cond - ((>= arg 0) - (setq n (- (line-end-position) (point))) - (if (> n arg) - (forward-char arg) - (end-of-line) - (insert (make-string (- arg n) ? )))) - ((< arg 0) - (setq arg (- arg)) - (setq n (- (point) (line-beginning-position))) - (when (< (- n 2) arg) ; -2 to create left-margin - ;; no space left - shift entire buffer contents right! - (let ((pos (move-marker (make-marker) (point)))) - (set-marker-insertion-type pos t) - (goto-char (point-min)) - (while (not (eobp)) - (insert-before-markers (make-string (- arg -2 n) ? )) - (forward-line 1)) - (goto-char pos))) - (backward-char arg))))) - - -(defun undo-tree-move-backward (&optional arg) - ;; Move backward, extending buffer if necessary. - (unless arg (setq arg 1)) - (undo-tree-move-forward (- arg))) - - -(defun undo-tree-move-to-parent (node) - ;; Move to position of parent of NODE, extending buffer if necessary. - (let* ((parent (undo-tree-node-previous node)) - (n (undo-tree-node-next parent)) - (l (length n)) p) - (goto-char (undo-tree-node-marker node)) - (unless (= l 1) - ;; move horizontally - (setq p (undo-tree-position node n)) - (cond - ;; node in centre subtree: no horizontal movement - ((and (= (mod l 2) 1) (= p (/ l 2)))) - ;; node in left subtree: move right - ((< p (/ l 2)) - (setq n (nthcdr p n)) - (undo-tree-move-forward - (+ (undo-tree-node-char-rwidth (car n)) - (/ undo-tree-visualizer-spacing 2) 1)) - (dotimes (_ (- (/ l 2) p 1)) - (setq n (cdr n)) - (undo-tree-move-forward - (+ (undo-tree-node-char-lwidth (car n)) - (undo-tree-node-char-rwidth (car n)) - undo-tree-visualizer-spacing 1))) - (when (= (mod l 2) 1) - (setq n (cdr n)) - (undo-tree-move-forward - (+ (undo-tree-node-char-lwidth (car n)) - (/ undo-tree-visualizer-spacing 2) 1)))) - (t ;; node in right subtree: move left - (setq n (nthcdr (/ l 2) n)) - (when (= (mod l 2) 1) - (undo-tree-move-backward - (+ (undo-tree-node-char-rwidth (car n)) - (/ undo-tree-visualizer-spacing 2) 1)) - (setq n (cdr n))) - (dotimes (_ (- p (/ l 2) (mod l 2))) - (undo-tree-move-backward - (+ (undo-tree-node-char-lwidth (car n)) - (undo-tree-node-char-rwidth (car n)) - undo-tree-visualizer-spacing 1)) - (setq n (cdr n))) - (undo-tree-move-backward - (+ (undo-tree-node-char-lwidth (car n)) - (/ undo-tree-visualizer-spacing 2) 1))))) - ;; move vertically - (undo-tree-move-up 3))) - - -(defun undo-tree-timestamp-to-string - (timestamp &optional relative current register) - ;; Convert TIMESTAMP to string (either absolute or RELATVE time), indicating - ;; if it's the CURRENT node and/or has an associated REGISTER. - (if relative - ;; relative time - (let ((time (floor (float-time - (time-subtract (current-time) timestamp)))) - n) - (setq time - ;; years - (if (> (setq n (/ time 315360000)) 0) - (if (> n 999) "-ages" (format "-%dy" n)) - (setq time (% time 315360000)) - ;; days - (if (> (setq n (/ time 86400)) 0) - (format "-%dd" n) - (setq time (% time 86400)) - ;; hours - (if (> (setq n (/ time 3600)) 0) - (format "-%dh" n) - (setq time (% time 3600)) - ;; mins - (if (> (setq n (/ time 60)) 0) - (format "-%dm" n) - ;; secs - (format "-%ds" (% time 60))))))) - (setq time (concat - (if current "*" " ") - time - (if register (concat "[" (char-to-string register) "]") - " "))) - (setq n (length time)) - (if (< n 9) - (concat (make-string (- 9 n) ? ) time) - time)) - ;; absolute time - (concat (if current " *" " ") - (format-time-string "%H:%M:%S" timestamp) - (if register - (concat "[" (char-to-string register) "]") - " ")))) - - - - -;;; ===================================================================== -;;; Visualizer modes - -(define-derived-mode - undo-tree-visualizer-mode special-mode "undo-tree-visualizer" - "Major mode used in undo-tree visualizer. - -The undo-tree visualizer can only be invoked from a buffer in -which `undo-tree-mode' is enabled. The visualizer displays the -undo history tree graphically, and allows you to browse around -the undo history, undoing or redoing the corresponding changes in -the parent buffer. - -Within the undo-tree visualizer, the following keys are available: - - \\{undo-tree-visualizer-mode-map}" - :syntax-table nil - :abbrev-table nil - (setq truncate-lines t) - (setq cursor-type nil) - (setq undo-tree-visualizer-selected-node nil)) - - -(define-minor-mode undo-tree-visualizer-selection-mode - "Toggle mode to select nodes in undo-tree visualizer." - :lighter "Select" - :keymap undo-tree-visualizer-selection-mode-map - :group undo-tree - (cond - ;; enable selection mode - (undo-tree-visualizer-selection-mode - (setq cursor-type 'box) - (setq undo-tree-visualizer-selected-node - (undo-tree-current buffer-undo-tree)) - ;; erase diff (if any), as initially selected node is identical to current - (when undo-tree-visualizer-diff - (let ((buff (get-buffer undo-tree-diff-buffer-name)) - (inhibit-read-only t)) - (when buff (with-current-buffer buff (erase-buffer)))))) - (t ;; disable selection mode - (setq cursor-type nil) - (setq undo-tree-visualizer-selected-node nil) - (goto-char (undo-tree-node-marker (undo-tree-current buffer-undo-tree))) - (when undo-tree-visualizer-diff (undo-tree-visualizer-update-diff))) - )) - - - - -;;; ===================================================================== -;;; Visualizer commands - -(defun undo-tree-visualize-undo (&optional arg) - "Undo changes. A numeric ARG serves as a repeat count." - (interactive "p") - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (let ((old (undo-tree-current buffer-undo-tree)) - current) - ;; undo in parent buffer - (switch-to-buffer-other-window undo-tree-visualizer-parent-buffer) - (deactivate-mark) - (unwind-protect - (let ((undo-tree-inhibit-kill-visualizer t)) (undo-tree-undo-1 arg)) - (setq current (undo-tree-current buffer-undo-tree)) - (switch-to-buffer-other-window undo-tree-visualizer-buffer-name) - ;; unhighlight old current node - (let ((undo-tree-insert-face 'undo-tree-visualizer-active-branch-face) - (inhibit-read-only t)) - (undo-tree-draw-node old)) - ;; when using lazy drawing, extend tree upwards as required - (when undo-tree-visualizer-lazy-drawing - (undo-tree-expand-up old current)) - ;; highlight new current node - (let ((inhibit-read-only t)) (undo-tree-draw-node current 'current)) - ;; update diff display, if any - (when undo-tree-visualizer-diff (undo-tree-visualizer-update-diff))))) - - -(defun undo-tree-visualize-redo (&optional arg) - "Redo changes. A numeric ARG serves as a repeat count." - (interactive "p") - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (let ((old (undo-tree-current buffer-undo-tree)) - current) - ;; redo in parent buffer - (switch-to-buffer-other-window undo-tree-visualizer-parent-buffer) - (deactivate-mark) - (unwind-protect - (let ((undo-tree-inhibit-kill-visualizer t)) (undo-tree-redo-1 arg)) - (setq current (undo-tree-current buffer-undo-tree)) - (switch-to-buffer-other-window undo-tree-visualizer-buffer-name) - ;; unhighlight old current node - (let ((undo-tree-insert-face 'undo-tree-visualizer-active-branch-face) - (inhibit-read-only t)) - (undo-tree-draw-node old)) - ;; when using lazy drawing, extend tree downwards as required - (when undo-tree-visualizer-lazy-drawing - (undo-tree-expand-down old current)) - ;; highlight new current node - (let ((inhibit-read-only t)) (undo-tree-draw-node current 'current)) - ;; update diff display, if any - (when undo-tree-visualizer-diff (undo-tree-visualizer-update-diff))))) - - -(defun undo-tree-visualize-switch-branch-right (arg) - "Switch to next branch of the undo tree. -This will affect which branch to descend when *redoing* changes -using `undo-tree-redo' or `undo-tree-visualizer-redo'." - (interactive "p") - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - ;; un-highlight old active branch below current node - (goto-char (undo-tree-node-marker (undo-tree-current buffer-undo-tree))) - (let ((undo-tree-insert-face 'undo-tree-visualizer-default-face) - (inhibit-read-only t)) - (undo-tree-highlight-active-branch (undo-tree-current buffer-undo-tree))) - ;; increment branch - (let ((branch (undo-tree-node-branch (undo-tree-current buffer-undo-tree)))) - (setf (undo-tree-node-branch (undo-tree-current buffer-undo-tree)) - (cond - ((>= (+ branch arg) (undo-tree-num-branches)) - (1- (undo-tree-num-branches))) - ((<= (+ branch arg) 0) 0) - (t (+ branch arg)))) - (let ((inhibit-read-only t)) - ;; highlight new active branch below current node - (goto-char (undo-tree-node-marker (undo-tree-current buffer-undo-tree))) - (let ((undo-tree-insert-face 'undo-tree-visualizer-active-branch-face)) - (undo-tree-highlight-active-branch (undo-tree-current buffer-undo-tree))) - ;; re-highlight current node - (undo-tree-draw-node (undo-tree-current buffer-undo-tree) 'current)))) - - -(defun undo-tree-visualize-switch-branch-left (arg) - "Switch to previous branch of the undo tree. -This will affect which branch to descend when *redoing* changes -using `undo-tree-redo' or `undo-tree-visualizer-redo'." - (interactive "p") - (undo-tree-visualize-switch-branch-right (- arg))) - - -(defun undo-tree-visualizer-quit () - "Quit the undo-tree visualizer." - (interactive) - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (undo-tree-clear-visualizer-data buffer-undo-tree) - ;; remove kill visualizer hook from parent buffer - (unwind-protect - (with-current-buffer undo-tree-visualizer-parent-buffer - (remove-hook 'before-change-functions 'undo-tree-kill-visualizer t)) - ;; kill diff buffer, if any - (when undo-tree-visualizer-diff (undo-tree-visualizer-hide-diff)) - (let ((parent undo-tree-visualizer-parent-buffer) - window) - ;; kill visualizer buffer - (kill-buffer nil) - ;; switch back to parent buffer - (unwind-protect - (if (setq window (get-buffer-window parent)) - (select-window window) - (switch-to-buffer parent)))))) - - -(defun undo-tree-visualizer-abort () - "Quit the undo-tree visualizer and return buffer to original state." - (interactive) - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (let ((node undo-tree-visualizer-initial-node)) - (undo-tree-visualizer-quit) - (undo-tree-set node))) - - -(defun undo-tree-visualizer-set (&optional pos) - "Set buffer to state corresponding to undo tree node -at POS, or point if POS is nil." - (interactive) - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (unless pos (setq pos (point))) - (let ((node (get-text-property pos 'undo-tree-node))) - (when node - ;; set parent buffer to state corresponding to node at POS - (switch-to-buffer-other-window undo-tree-visualizer-parent-buffer) - (let ((undo-tree-inhibit-kill-visualizer t)) (undo-tree-set node)) - (switch-to-buffer-other-window undo-tree-visualizer-buffer-name) - ;; re-draw undo tree - (let ((inhibit-read-only t)) (undo-tree-draw-tree buffer-undo-tree)) - (when undo-tree-visualizer-diff (undo-tree-visualizer-update-diff))))) - - -(defun undo-tree-visualizer-mouse-set (pos) - "Set buffer to state corresponding to undo tree node -at mouse event POS." - (interactive "@e") - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (undo-tree-visualizer-set (event-start (nth 1 pos)))) - - -(defun undo-tree-visualize-undo-to-x (&optional x) - "Undo to last branch point, register, or saved state. -If X is the symbol `branch', undo to last branch point. If X is -the symbol `register', undo to last register. If X is the symbol -`saved', undo to last saved state. If X is null, undo to first of -these that's encountered. - -Interactively, a single \\[universal-argument] specifies -`branch', a double \\[universal-argument] \\[universal-argument] -specifies `saved', and a negative prefix argument specifies -`register'." - (interactive "P") - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (when (and (called-interactively-p 'any) x) - (setq x (prefix-numeric-value x) - x (cond - ((< x 0) 'register) - ((<= x 4) 'branch) - (t 'saved)))) - (let ((current (if undo-tree-visualizer-selection-mode - undo-tree-visualizer-selected-node - (undo-tree-current buffer-undo-tree))) - (diff undo-tree-visualizer-diff) - r) - (undo-tree-visualizer-hide-diff) - (while (and (undo-tree-node-previous current) - (or (if undo-tree-visualizer-selection-mode - (progn - (undo-tree-visualizer-select-previous) - (setq current undo-tree-visualizer-selected-node)) - (undo-tree-visualize-undo) - (setq current (undo-tree-current buffer-undo-tree))) - t) - ;; branch point - (not (or (and (or (null x) (eq x 'branch)) - (> (undo-tree-num-branches) 1)) - ;; register - (and (or (null x) (eq x 'register)) - (setq r (undo-tree-node-register current)) - (undo-tree-register-data-p - (setq r (registerv-data (get-register r)))) - (eq current (undo-tree-register-data-node r))) - ;; saved state - (and (or (null x) (eq x 'saved)) - (undo-tree-node-unmodified-p current)) - )))) - ;; update diff display, if any - (when diff - (undo-tree-visualizer-show-diff - (when undo-tree-visualizer-selection-mode - undo-tree-visualizer-selected-node))))) - - -(defun undo-tree-visualize-redo-to-x (&optional x) - "Redo to last branch point, register, or saved state. -If X is the symbol `branch', redo to last branch point. If X is -the symbol `register', redo to last register. If X is the sumbol -`saved', redo to last saved state. If X is null, redo to first of -these that's encountered. - -Interactively, a single \\[universal-argument] specifies -`branch', a double \\[universal-argument] \\[universal-argument] -specifies `saved', and a negative prefix argument specifies -`register'." - (interactive "P") - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (when (and (called-interactively-p 'any) x) - (setq x (prefix-numeric-value x) - x (cond - ((< x 0) 'register) - ((<= x 4) 'branch) - (t 'saved)))) - (let ((current (if undo-tree-visualizer-selection-mode - undo-tree-visualizer-selected-node - (undo-tree-current buffer-undo-tree))) - (diff undo-tree-visualizer-diff) - r) - (undo-tree-visualizer-hide-diff) - (while (and (undo-tree-node-next current) - (or (if undo-tree-visualizer-selection-mode - (progn - (undo-tree-visualizer-select-next) - (setq current undo-tree-visualizer-selected-node)) - (undo-tree-visualize-redo) - (setq current (undo-tree-current buffer-undo-tree))) - t) - ;; branch point - (not (or (and (or (null x) (eq x 'branch)) - (> (undo-tree-num-branches) 1)) - ;; register - (and (or (null x) (eq x 'register)) - (setq r (undo-tree-node-register current)) - (undo-tree-register-data-p - (setq r (registerv-data (get-register r)))) - (eq current (undo-tree-register-data-node r))) - ;; saved state - (and (or (null x) (eq x 'saved)) - (undo-tree-node-unmodified-p current)) - )))) - ;; update diff display, if any - (when diff - (undo-tree-visualizer-show-diff - (when undo-tree-visualizer-selection-mode - undo-tree-visualizer-selected-node))))) - - -(defun undo-tree-visualizer-toggle-timestamps () - "Toggle display of time-stamps." - (interactive) - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (setq undo-tree-visualizer-timestamps (not undo-tree-visualizer-timestamps)) - (setq undo-tree-visualizer-spacing (undo-tree-visualizer-calculate-spacing)) - ;; redraw tree - (let ((inhibit-read-only t)) (undo-tree-draw-tree buffer-undo-tree))) - - -(defun undo-tree-visualizer-scroll-left (&optional arg) - (interactive "p") - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (scroll-left (or arg 1) t)) - - -(defun undo-tree-visualizer-scroll-right (&optional arg) - (interactive "p") - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (scroll-right (or arg 1) t)) - - -(defun undo-tree-visualizer-scroll-up (&optional arg) - (interactive "P") - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (if (or (and (numberp arg) (< arg 0)) (eq arg '-)) - (undo-tree-visualizer-scroll-down arg) - ;; scroll up and expand newly-visible portion of tree - (unwind-protect - (scroll-up-command arg) - (undo-tree-expand-down - (nth (undo-tree-node-branch (undo-tree-current buffer-undo-tree)) - (undo-tree-node-next (undo-tree-current buffer-undo-tree))))) - ;; signal error if at eob - (when (and (not undo-tree-visualizer-needs-extending-down) (eobp)) - (scroll-up)))) - - -(defun undo-tree-visualizer-scroll-down (&optional arg) - (interactive "P") - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (if (or (and (numberp arg) (< arg 0)) (eq arg '-)) - (undo-tree-visualizer-scroll-up arg) - ;; ensure there's enough room at top of buffer to scroll - (let ((scroll-lines - (or arg (- (window-height) next-screen-context-lines))) - (window-line (1- (line-number-at-pos (window-start))))) - (when (and undo-tree-visualizer-needs-extending-up - (< window-line scroll-lines)) - (let ((inhibit-read-only t)) - (goto-char (point-min)) - (undo-tree-move-up (- scroll-lines window-line))))) - ;; scroll down and expand newly-visible portion of tree - (unwind-protect - (scroll-down-command arg) - (undo-tree-expand-up - (undo-tree-node-previous (undo-tree-current buffer-undo-tree)))) - ;; signal error if at bob - (when (and (not undo-tree-visualizer-needs-extending-down) (bobp)) - (scroll-down)))) - - - - -;;; ===================================================================== -;;; Visualizer selection mode commands - -(defun undo-tree-visualizer-select-previous (&optional arg) - "Move to previous node." - (interactive "p") - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (let ((node undo-tree-visualizer-selected-node)) - (catch 'top - (dotimes (_ (or arg 1)) - (unless (undo-tree-node-previous node) (throw 'top t)) - (setq node (undo-tree-node-previous node)))) - ;; when using lazy drawing, extend tree upwards as required - (when undo-tree-visualizer-lazy-drawing - (undo-tree-expand-up undo-tree-visualizer-selected-node node)) - ;; update diff display, if any - (when (and undo-tree-visualizer-diff - (not (eq node undo-tree-visualizer-selected-node))) - (undo-tree-visualizer-update-diff node)) - ;; move to selected node - (goto-char (undo-tree-node-marker node)) - (setq undo-tree-visualizer-selected-node node))) - - -(defun undo-tree-visualizer-select-next (&optional arg) - "Move to next node." - (interactive "p") - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (let ((node undo-tree-visualizer-selected-node)) - (catch 'bottom - (dotimes (_ (or arg 1)) - (unless (nth (undo-tree-node-branch node) (undo-tree-node-next node)) - (throw 'bottom t)) - (setq node - (nth (undo-tree-node-branch node) (undo-tree-node-next node))))) - ;; when using lazy drawing, extend tree downwards as required - (when undo-tree-visualizer-lazy-drawing - (undo-tree-expand-down undo-tree-visualizer-selected-node node)) - ;; update diff display, if any - (when (and undo-tree-visualizer-diff - (not (eq node undo-tree-visualizer-selected-node))) - (undo-tree-visualizer-update-diff node)) - ;; move to selected node - (goto-char (undo-tree-node-marker node)) - (setq undo-tree-visualizer-selected-node node))) - - -(defun undo-tree-visualizer-select-right (&optional arg) - "Move right to a sibling node." - (interactive "p") - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (let ((node undo-tree-visualizer-selected-node) - end) - (goto-char (undo-tree-node-marker undo-tree-visualizer-selected-node)) - (setq end (line-end-position)) - (catch 'end - (dotimes (_ arg) - (while (or (null node) (eq node undo-tree-visualizer-selected-node)) - (forward-char) - (setq node (get-text-property (point) 'undo-tree-node)) - (when (= (point) end) (throw 'end t))))) - (goto-char (undo-tree-node-marker - (or node undo-tree-visualizer-selected-node))) - (when (and undo-tree-visualizer-diff node - (not (eq node undo-tree-visualizer-selected-node))) - (undo-tree-visualizer-update-diff node)) - (when node (setq undo-tree-visualizer-selected-node node)))) - - -(defun undo-tree-visualizer-select-left (&optional arg) - "Move left to a sibling node." - (interactive "p") - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (let ((node (get-text-property (point) 'undo-tree-node)) - beg) - (goto-char (undo-tree-node-marker undo-tree-visualizer-selected-node)) - (setq beg (line-beginning-position)) - (catch 'beg - (dotimes (_ arg) - (while (or (null node) (eq node undo-tree-visualizer-selected-node)) - (backward-char) - (setq node (get-text-property (point) 'undo-tree-node)) - (when (= (point) beg) (throw 'beg t))))) - (goto-char (undo-tree-node-marker - (or node undo-tree-visualizer-selected-node))) - (when (and undo-tree-visualizer-diff node - (not (eq node undo-tree-visualizer-selected-node))) - (undo-tree-visualizer-update-diff node)) - (when node (setq undo-tree-visualizer-selected-node node)))) - - -(defun undo-tree-visualizer-select (pos) - (let ((node (get-text-property pos 'undo-tree-node))) - (when node - ;; select node at POS - (goto-char (undo-tree-node-marker node)) - ;; when using lazy drawing, extend tree up and down as required - (when undo-tree-visualizer-lazy-drawing - (undo-tree-expand-up undo-tree-visualizer-selected-node node) - (undo-tree-expand-down undo-tree-visualizer-selected-node node)) - ;; update diff display, if any - (when (and undo-tree-visualizer-diff - (not (eq node undo-tree-visualizer-selected-node))) - (undo-tree-visualizer-update-diff node)) - ;; update selected node - (setq undo-tree-visualizer-selected-node node) - ))) - - -(defun undo-tree-visualizer-mouse-select (pos) - "Select undo tree node at mouse event POS." - (interactive "@e") - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (undo-tree-visualizer-select (event-start (nth 1 pos)))) - - - - -;;; ===================================================================== -;;; Visualizer diff display - -(defun undo-tree-visualizer-toggle-diff () - "Toggle diff display in undo-tree visualizer." - (interactive) - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (if undo-tree-visualizer-diff - (undo-tree-visualizer-hide-diff) - (undo-tree-visualizer-show-diff))) - - -(defun undo-tree-visualizer-selection-toggle-diff () - "Toggle diff display in undo-tree visualizer selection mode." - (interactive) - (unless (eq major-mode 'undo-tree-visualizer-mode) - (user-error "Undo-tree mode not enabled in buffer")) - (if undo-tree-visualizer-diff - (undo-tree-visualizer-hide-diff) - (let ((node (get-text-property (point) 'undo-tree-node))) - (when node (undo-tree-visualizer-show-diff node))))) - - -(defun undo-tree-visualizer-show-diff (&optional node) - ;; show visualizer diff display - (setq undo-tree-visualizer-diff t) - (let ((buff (with-current-buffer undo-tree-visualizer-parent-buffer - (undo-tree-diff node))) - (display-buffer-mark-dedicated 'soft) - win) - (setq win (split-window)) - (set-window-buffer win buff) - (shrink-window-if-larger-than-buffer win))) - - -(defun undo-tree-visualizer-hide-diff () - ;; hide visualizer diff display - (setq undo-tree-visualizer-diff nil) - (let ((win (get-buffer-window undo-tree-diff-buffer-name))) - (when win (with-selected-window win (kill-buffer-and-window))))) - - -(defun undo-tree-diff (&optional node) - ;; Create diff between NODE and current state (or previous state and current - ;; state, if NODE is null). Returns buffer containing diff. - (let (tmpfile buff) - ;; generate diff - (let ((undo-tree-inhibit-kill-visualizer t) - (current (undo-tree-current buffer-undo-tree))) - (undo-tree-set (or node (undo-tree-node-previous current) current) - 'preserve-timestamps) - (setq tmpfile (diff-file-local-copy (current-buffer))) - (undo-tree-set current 'preserve-timestamps)) - (setq buff (diff-no-select - tmpfile (current-buffer) nil 'noasync - (get-buffer-create undo-tree-diff-buffer-name))) - ;; delete process messages and useless headers from diff buffer - (let ((inhibit-read-only t)) - (with-current-buffer buff - (goto-char (point-min)) - (delete-region (point) (1+ (line-end-position 3))) - (goto-char (point-max)) - (forward-line -2) - (delete-region (point) (point-max)) - (setq cursor-type nil) - (setq buffer-read-only t))) - buff)) - - -(defun undo-tree-visualizer-update-diff (&optional node) - ;; update visualizer diff display to show diff between current state and - ;; NODE (or previous state, if NODE is null) - (with-current-buffer undo-tree-visualizer-parent-buffer - (undo-tree-diff node)) - (let ((win (get-buffer-window undo-tree-diff-buffer-name))) - (when win - (balance-windows) - (shrink-window-if-larger-than-buffer win)))) - -;;;; ChangeLog: - -;; 2020-01-28 Toby S. Cubitt -;; -;; Undo-tree bug-fix release. -;; -;; 2020-01-26 Toby S. Cubitt -;; -;; Undo-tree point release. -;; -;; 2020-01-11 Toby S. Cubitt -;; -;; Undo-tree bug-fix release. -;; -;; 2020-01-09 Toby S. Cubitt -;; -;; Bump undo-tree version number. -;; -;; 2020-01-09 Toby S. Cubitt -;; -;; Undo-tree bug-fix release. -;; -;; 2020-01-06 Toby S. Cubitt -;; -;; New undo-tree package release. -;; -;; 2014-05-01 Barry O'Reilly -;; -;; Fix bug that caused undo-tree to hang when undoing in region -;; (bug#16377). -;; -;; 2013-12-28 Toby S. Cubitt -;; -;; * undo-tree: Update to version 0.6.5. -;; -;; 2012-12-05 Toby S. Cubitt -;; -;; Update undo-tree to version 0.6.3 -;; -;; * undo-tree.el: Implement lazy tree drawing to significantly speed up -;; visualization of large trees + various more minor improvements. -;; -;; 2012-09-25 Toby S. Cubitt -;; -;; Updated undo-tree package to version 0.5.5. -;; -;; Small bug-fix to avoid hooks triggering an error when trying to save -;; undo history in a buffer where undo is disabled. -;; -;; 2012-09-11 Toby S. Cubitt -;; -;; Updated undo-tree package to version 0.5.4 -;; -;; Bug-fixes and improvements to persistent history storage. -;; -;; 2012-07-18 Toby S. Cubitt -;; -;; Update undo-tree to version 0.5.3 -;; -;; * undo-tree.el: Cope gracefully with undo boundaries being deleted -;; (cf. bug#11774). Allow customization of directory to which undo history -;; is -;; saved. -;; -;; 2012-05-24 Toby S. Cubitt -;; -;; updated undo-tree package to version 0.5.2 -;; -;; * undo-tree.el: add diff view feature in undo-tree visualizer. -;; -;; 2012-05-02 Toby S. Cubitt -;; -;; undo-tree.el: Update package to version 0.4 -;; -;; 2012-04-20 Toby S. Cubitt -;; -;; undo-tree.el: Update package to version 0.3.4 -;; -;; * undo-tree.el (undo-list-pop-changeset): fix pernicious bug causing -;; undo history to be lost. -;; (buffer-undo-tree): set permanent-local property. -;; (undo-tree-enable-undo-in-region): add new customization option allowing -;; undo-in-region to be disabled. -;; -;; 2012-01-26 Toby S. Cubitt -;; -;; undo-tree.el: Fixed copyright attribution and Emacs status. -;; -;; 2012-01-26 Toby S. Cubitt -;; -;; undo-tree.el: Update package to version 0.3.3 -;; -;; 2011-09-17 Stefan Monnier -;; -;; Add undo-tree.el -;; - - - - -(provide 'undo-tree) - -;;; undo-tree.el ends here diff --git a/localelpa/xr-1.14.el b/localelpa/xr-1.14.el deleted file mode 100644 index f80dba24be..0000000000 --- a/localelpa/xr-1.14.el +++ /dev/null @@ -1,1371 +0,0 @@ -;;; xr.el --- Convert string regexp to rx notation -*- lexical-binding: t -*- - -;; Copyright (C) 2019 Free Software Foundation, Inc. - -;; Author: Mattias EngdegĂ„rd -;; Version: 1.14 -;; URL: https://github.com/mattiase/xr -;; Keywords: lisp, maint, regexps - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; This is an inverse companion to the rx package for translating -;; regexps in string form to the rx notation. Its chief uses are: -;; -;; - Migrating existing code to rx form, for better readability and -;; maintainability -;; - Understanding complex regexp strings and finding errors in them -;; -;; Please refer to `rx' for more information about the notation. -;; -;; In addition to Emacs regexps, this package can also parse and -;; find mistakes in skip set strings, which are arguments to -;; `skip-chars-forward' and `skip-chars-backward'. -;; -;; The exported functions are: -;; -;; Regexps: -;; `xr' - returns the converted rx expression -;; `xr-pp' - converts to rx and pretty-prints -;; `xr-lint' - finds mistakes in a regexp string -;; -;; Skip sets: -;; `xr-skip-set' - return the converted rx expression -;; `xr-skip-set-pp' - converts to rx and pretty-prints -;; `xr-skip-set-lint' - finds mistakes in a skip set string -;; -;; General: -;; `xr-pp-rx-to-str' - pretty-prints an rx expression to a string -;; -;; Example (regexp found in compile.el): -;; -;; (xr-pp "\\`\\(?:[^^]\\|\\^\\(?: \\*\\|\\[\\)\\)") -;; => -;; (seq bos -;; (or (not (any "^")) -;; (seq "^" -;; (or " *" "[")))) -;; -;; The rx notation admits many synonyms. The user is encouraged to -;; edit the result for maximum readability, consistency and personal -;; preference when replacing existing regexps in elisp code. -;; -;; Related work: -;; -;; The `lex' package, a lexical analyser generator, provides the -;; `lex-parse-re' function which translates regexps to rx, but does -;; not attempt to handle all the edge cases of Elisp's regexp syntax -;; or pretty-print the result. -;; -;; The `pcre2el' package, a regexp syntax converter and interactive -;; regexp explainer, could also be used for translating regexps to rx. -;; `xr' is narrower in scope but more accurate for the purpose of -;; parsing Emacs regexps and printing the results in rx form. -;; -;; Neither of these packages parse skip-set strings or provide -;; mistake-finding functions. - -;;; News: - -;; Version 1.14: -;; - Warn about repetition of grouped repetition -;; Version 1.13: -;; - More robust pretty-printing, especially for characters -;; - Generate (category CHAR) for unknown categories -;; Version 1.12: -;; - Warn about branch subsumption, like [AB]\|A -;; Version 1.11: -;; - Warn about repetition of empty-matching expressions -;; - Detect `-' not first or last in char alternatives or skip-sets -;; - Stronger ad-hoc [...] check in skip-sets -;; Version 1.10: -;; - Warn about [[:class:]] in skip-sets -;; - Warn about two-character ranges like [*-+] in regexps -;; Version 1.9: -;; - Don't complain about [z-a] and [^z-a] specifically -;; - Improved skip set checks -;; Version 1.8: -;; - Improved skip set checks -;; Version 1.7: -;; - Parse skip-sets, adding `xr-skip-set', `xr-skip-set-pp' and -;; `xr-skip-set-lint' -;; - Ad-hoc check for misplaced `]' in regexps -;; Version 1.6: -;; - Detect duplicated branches like A\|A -;; Version 1.5: -;; - Add dialect option to `xr' and `xr-pp' -;; - Negative empty sets, [^z-a], now become `anything' -;; Version 1.4: -;; - Detect overlap in character alternatives -;; Version 1.3: -;; - Improved xr-lint warnings -;; Version 1.2: -;; - `xr-lint' added - -;;; Code: - -(require 'rx) -(require 'cl-lib) - -(defun xr--report (warnings position message) - "Add the report MESSAGE at POSITION to WARNINGS." - (when warnings - (push (cons (1- position) message) (car warnings)))) - -(defun xr--parse-char-alt (negated warnings) - (let ((start-pos (point)) - (intervals nil) - (classes nil)) - (cond - ;; Initial ]-x range - ((looking-at (rx "]-" (not (any "]")))) - (let ((end (aref (match-string 0) 2))) - (if (>= end ?\]) - (push (vector ?\] end (point)) intervals) - (xr--report warnings (point) - (format "Reversed range `%s' matches nothing" - (xr--escape-string (match-string 0) nil)))) - (when (eq end ?^) - (xr--report warnings (point) - (format "Two-character range `%s'" - (xr--escape-string (match-string 0) nil))))) - (goto-char (match-end 0))) - ;; Initial ] - ((looking-at "]") - (push (vector ?\] ?\] (point)) intervals) - (forward-char 1))) - - (while (not (looking-at "]")) - (cond - ;; character class - ((looking-at (rx "[:" (group (*? anything)) ":]")) - (let ((sym (intern (match-string 1)))) - (unless (memq sym - '(ascii alnum alpha blank cntrl digit graph - lower multibyte nonascii print punct space - unibyte upper word xdigit)) - (error "No character class `%s'" (match-string 0))) - (if (memq sym classes) - (xr--report warnings (point) - (format "Duplicated character class `[:%s:]'" sym)) - (push sym classes)) - (goto-char (match-end 0)))) - ;; character range - ((looking-at (rx (group (not (any "]"))) "-" (group (not (any "]"))))) - (let ((start (string-to-char (match-string 1))) - (end (string-to-char (match-string 2)))) - (cond - ((<= start end) - (push (vector start end (point)) intervals)) - ;; It's unlikely that anyone writes z-a by mistake; don't complain. - ((and (eq start ?z) (eq end ?a))) - (t - (xr--report warnings (point) - (format "Reversed range `%s' matches nothing" - (xr--escape-string (match-string 0) nil))))) - ;; Suppress warnings about ranges between adjacent digits, - ;; like [0-1], as they are common and harmless. - (when (and (= end (1+ start)) (not (<= ?0 start end ?9))) - (xr--report warnings (point) - (format "Two-character range `%s'" - (xr--escape-string (match-string 0) nil)))) - (goto-char (match-end 0)))) - ((looking-at (rx eos)) - (error "Unterminated character alternative")) - ;; plain character (including ^ or -) - (t - (let ((ch (following-char))) - (when (and (eq ch ?\[) - ;; Ad-hoc pattern attempting to catch mistakes - ;; on the form [...[...]...] - ;; where we are ^here - (looking-at (rx "[" - (zero-or-more (not (any "[]"))) - "]" - (zero-or-more (not (any "[]"))) - (not (any "[\\")) - "]")) - ;; Only if the alternative didn't start with ] - (not (and intervals - (eq (aref (car (last intervals)) 0) ?\])))) - (xr--report warnings (point) - "Suspect `[' in char alternative")) - (when (and (looking-at (rx "-" (not (any "]")))) - (> (point) start-pos)) - (xr--report - warnings (point) - "Literal `-' not first or last in character alternative")) - (push (vector ch ch (point)) intervals)) - (forward-char 1)))) - - (forward-char 1) ; eat the ] - - ;; Detect duplicates and overlapping intervals. - (let* ((sorted - (sort (nreverse intervals) - (lambda (a b) (< (aref a 0) (aref b 0))))) - (s sorted)) - (while (cdr s) - (let ((this (car s)) - (next (cadr s))) - (when (>= (aref this 1) (aref next 0)) - (let ((message - (cond - ;; Duplicate character: drop it and warn. - ((and (eq (aref this 0) (aref this 1)) - (eq (aref next 0) (aref next 1))) - (setcdr s (cddr s)) - (format "Duplicated `%c' inside character alternative" - (aref this 0))) - ;; Duplicate range: drop it and warn. - ((and (eq (aref this 0) (aref next 0)) - (eq (aref this 1) (aref next 1))) - (setcdr s (cddr s)) - (format "Duplicated `%c-%c' inside character alternative" - (aref this 0) (aref this 1))) - ;; Character in range: drop it and warn. - ((eq (aref this 0) (aref this 1)) - (setcar s next) - (setcdr s (cddr s)) - (format "Character `%c' included in range `%c-%c'" - (aref this 0) (aref next 0) (aref next 1))) - ;; Same but other way around. - ((eq (aref next 0) (aref next 1)) - (setcdr s (cddr s)) - (format "Character `%c' included in range `%c-%c'" - (aref next 0) (aref this 0) (aref this 1))) - ;; Overlapping ranges: merge and warn. - (t - (let ((this-end (aref this 1))) - (aset this 1 (max (aref this 1) (aref next 1))) - (setcdr s (cddr s)) - (format "Ranges `%c-%c' and `%c-%c' overlap" - (aref this 0) this-end - (aref next 0) (aref next 1))))))) - (xr--report warnings (max (aref this 2) (aref next 2)) - (xr--escape-string message nil))))) - (setq s (cdr s))) - - ;; Gather ranges and single characters separately. - ;; We make no attempts at merging adjacent intervals/characters, - ;; nor at splitting short intervals such as "a-b"; if the user - ;; wrote it that way, there was probably a reason for it. - (let ((ranges nil) - (chars nil)) - (dolist (interv sorted) - (if (eq (aref interv 0) (aref interv 1)) - (push (aref interv 0) chars) - (push (string (aref interv 0) ?- (aref interv 1)) - ranges))) - - ;; Note that we return (any) for non-negated empty sets, - ;; such as [z-a]. (any) is not accepted by rx but at least we - ;; are not hiding potential bugs from the user. - (cond - ;; Negated empty set, like [^z-a]: anything. - ((and negated - (null chars) - (null ranges) - (null classes)) - 'anything) - ;; Non-negated single-char set, like [$]: make a string. - ((and (= (length chars) 1) - (not negated) - (null ranges) - (null classes)) - (string (car chars))) - ;; Single named class, like [[:space:]]: use the symbol. - ((and (= (length classes) 1) - (null chars) - (null ranges)) - (if negated - (list 'not (car classes)) - (car classes))) - ;; Anything else: produce (any ...) - (t - ;; Put dash last of all single characters. - (when (memq ?- chars) - (setq chars (cons ?- (delq ?- chars)))) - (let* ((set (cons 'any - (append - (and ranges - (list (apply #'concat (nreverse ranges)))) - (and chars - (list (apply #'string (nreverse chars)))) - (nreverse classes))))) - (if negated - (list 'not set) - set)))))))) - -(defun xr--rev-join-seq (sequence) - "Reverse a sequence, flatten any (seq ...) inside, and concatenate -adjacent strings." - (let ((result nil)) - (while sequence - (let ((elem (car sequence)) - (rest (cdr sequence))) - (cond ((and (consp elem) (eq (car elem) 'seq)) - (setq sequence (append (reverse (cdr elem)) rest))) - ((and (stringp elem) (stringp (car result))) - (setq result (cons (concat elem (car result)) (cdr result))) - (setq sequence rest)) - (t - (setq result (cons elem result)) - (setq sequence rest))))) - result)) - -(defun xr--char-category (negated category-code) - (let* ((sym (assq category-code - '((?\s . space-for-indent) - (?. . base) - (?0 . consonant) - (?1 . base-vowel) - (?2 . upper-diacritical-mark) - (?3 . lower-diacritical-mark) - (?4 . tone-mark) - (?5 . symbol) - (?6 . digit) - (?7 . vowel-modifying-diacritical-mark) - (?8 . vowel-sign) - (?9 . semivowel-lower) - (?< . not-at-end-of-line) - (?> . not-at-beginning-of-line) - (?A . alpha-numeric-two-byte) - (?C . chinese-two-byte) - (?G . greek-two-byte) - (?H . japanese-hiragana-two-byte) - (?I . indian-two-byte) - (?K . japanese-katakana-two-byte) - (?L . strong-left-to-right) - (?N . korean-hangul-two-byte) - (?R . strong-right-to-left) - (?Y . cyrillic-two-byte) - (?^ . combining-diacritic) - (?a . ascii) - (?b . arabic) - (?c . chinese) - (?e . ethiopic) - (?g . greek) - (?h . korean) - (?i . indian) - (?j . japanese) - (?k . japanese-katakana) - (?l . latin) - (?o . lao) - (?q . tibetan) - (?r . japanese-roman) - (?t . thai) - (?v . vietnamese) - (?w . hebrew) - (?y . cyrillic) - (?| . can-break)))) - (item (list 'category (if sym (cdr sym) category-code)))) - (if negated (list 'not item) item))) - -(defun xr--char-syntax (negated syntax-code) - (let ((sym (assq syntax-code - '((?- . whitespace) - (?\s . whitespace) - (?. . punctuation) - (?w . word) - (?W . word) ; undocumented - (?_ . symbol) - (?\( . open-parenthesis) - (?\) . close-parenthesis) - (?' . expression-prefix) - (?\" . string-quote) - (?$ . paired-delimiter) - (?\\ . escape) - (?/ . character-quote) - (?< . comment-start) - (?> . comment-end) - (?| . string-delimiter) - (?! . comment-delimiter))))) - (when (not sym) - (error "Unknown syntax code `%s'" - (xr--escape-string (char-to-string syntax-code) nil))) - (let ((item (list 'syntax (cdr sym)))) - (if negated (list 'not item) item)))) - -(defun xr--postfix (operator operand) - ;; We use verbose names for the common *, + and ? operators for readability, - ;; even though these names are affected by the rx-greedy-flag. - ;; For the (less common) non-greedy operators we might want to - ;; consider using minimal-match/maximal-match instead, but - ;; this would complicate the implementation. - (let* ((sym (cdr (assoc operator '(("*" . zero-or-more) - ("+" . one-or-more) - ("?" . opt) - ("*?" . *?) - ("+?" . +?) - ("??" . ??))))) - ;; Simplify when the operand is (seq ...) - (body (if (and (listp operand) (eq (car operand) 'seq)) - (cdr operand) - (list operand)))) - (cons sym body))) - -(defun xr--repeat (lower upper operand) - "Apply a repetition of {LOWER,UPPER} to OPERAND. -UPPER may be nil, meaning infinity." - (when (and upper (> lower upper)) - (error "Invalid repetition interval")) - ;; rx does not accept (= 0 ...) or (>= 0 ...), so we use - ;; (repeat 0 0 ...) and (zero-or-more ...), respectively. - ;; Note that we cannot just delete the operand if LOWER=UPPER=0, - ;; since doing so may upset the group numbering. - (let* ((operator (cond ((null upper) - (if (zerop lower) - '(zero-or-more) - (list '>= lower))) - ((and (= lower upper) (> lower 0)) - (list '= lower)) - (t - (list 'repeat lower upper)))) - ;; Simplify when the operand is (seq ...). - (body (if (and (listp operand) (eq (car operand) 'seq)) - (cdr operand) - (list operand)))) - (append operator body))) - -(defconst xr--zero-width-assertions - '(bol eol bos eos bow eow word-boundary not-word-boundary - symbol-start symbol-end point)) - -(defun xr--matches-empty-p (rx) - "Whether RX can match the empty string regardless of context." - (pcase rx - (`(,(or 'seq 'one-or-more 'group) . ,body) - (cl-every #'xr--matches-empty-p body)) - (`(or . ,body) - (cl-some #'xr--matches-empty-p body)) - (`(group-n ,_ . ,body) - (cl-every #'xr--matches-empty-p body)) - (`(,(or 'opt 'zero-or-more) . ,_) - t) - (`(repeat ,from ,_ . ,body) - (or (= from 0) - (cl-every #'xr--matches-empty-p body))) - (`(,(or '= '>=) ,_ . ,body) - (cl-every #'xr--matches-empty-p body)) - ("" t))) - -(defun xr--parse-seq (warnings) - (let ((sequence nil)) ; reversed - (while (not (looking-at (rx (or "\\|" "\\)" eos)))) - (cond - ;; ^ - only special at beginning of sequence - ((looking-at (rx "^")) - (forward-char 1) - (if (null sequence) - (push 'bol sequence) - (xr--report warnings (match-beginning 0) "Unescaped literal `^'") - (push "^" sequence))) - - ;; $ - only special at end of sequence - ((looking-at (rx "$")) - (forward-char 1) - (if (looking-at (rx (or "\\|" "\\)" eos))) - (push 'eol sequence) - (xr--report warnings (match-beginning 0) "Unescaped literal `$'") - (push "$" sequence))) - - ;; * ? + (and non-greedy variants) - ;; - not special at beginning of sequence or after ^ - ((looking-at (rx (group (any "*?+")) (opt "?"))) - (if (and sequence - (not (and (eq (car sequence) 'bol) (eq (preceding-char) ?^)))) - (let ((operator (match-string 0)) - (operand (car sequence))) - (when warnings - (cond - ;; (* (* X)), for any repetitions * - ((and (consp operand) - (memq (car operand) - '(opt zero-or-more one-or-more +? *? ??))) - (xr--report warnings (match-beginning 0) - "Repetition of repetition")) - ;; (* (group (* X))), for any repetitions * - ((and (consp operand) - (eq (car operand) 'group) - (null (cddr operand)) - (let ((inner (cadr operand))) - (and (consp inner) - (memq (car inner) - '(opt zero-or-more one-or-more +? *? ??)) - ;; Except (? (group (+ X))), since that may - ;; be legitimate. - (not (and (equal operator "?") - (memq (car inner) - '(one-or-more +?))))))) - (xr--report warnings (match-beginning 0) - "Repetition of repetition")) - ((memq operand xr--zero-width-assertions) - (xr--report warnings (match-beginning 0) - "Repetition of zero-width assertion")) - ((and (xr--matches-empty-p operand) - ;; Rejecting repetition of the empty string - ;; suppresses some false positives. - (not (equal operand ""))) - (xr--report - warnings (match-beginning 0) - "Repetition of expression matching an empty string")))) - (goto-char (match-end 0)) - (setq sequence (cons (xr--postfix operator operand) - (cdr sequence)))) - (let ((literal (match-string 1))) - (goto-char (match-end 1)) - (xr--report warnings (match-beginning 0) - (format "Unescaped literal `%s'" literal)) - (push literal sequence)))) - - ;; \{..\} - not special at beginning of sequence or after ^ - ((and (looking-at (rx "\\{")) - sequence - (not (and (eq (car sequence) 'bol) (eq (preceding-char) ?^)))) - (forward-char 2) - (let ((operand (car sequence))) - (when warnings - (cond - ;; (** N M (* X)), for any repetition * - ((and (consp operand) - (memq (car operand) - '(opt zero-or-more one-or-more +? *? ??))) - (xr--report warnings (match-beginning 0) - "Repetition of repetition")) - ;; (** N M (group (* X))), for any repetition * - ((and (consp operand) - (eq (car operand) 'group) - (null (cddr operand)) - (let ((inner (cadr operand))) - (and (consp inner) - (memq (car inner) - '(opt zero-or-more one-or-more +? *? ??))))) - (xr--report warnings (match-beginning 0) - "Repetition of repetition")) - ((memq operand xr--zero-width-assertions) - (xr--report warnings (match-beginning 0) - "Repetition of zero-width assertion")) - ((and (xr--matches-empty-p operand) - ;; Rejecting repetition of the empty string - ;; suppresses some false positives. - (not (equal operand ""))) - (xr--report - warnings (match-beginning 0) - "Repetition of expression matching an empty string")))) - (if (looking-at (rx (opt (group (one-or-more digit))) - (opt (group ",") - (opt (group (one-or-more digit)))) - "\\}")) - (let ((lower (if (match-string 1) - (string-to-number (match-string 1)) - 0)) - (comma (match-string 2)) - (upper (and (match-string 3) - (string-to-number (match-string 3))))) - (unless (or (match-beginning 1) (match-string 3)) - (xr--report warnings (- (match-beginning 0) 2) - (if comma - "Uncounted repetition" - "Implicit zero repetition"))) - (goto-char (match-end 0)) - (setq sequence (cons (xr--repeat lower - (if comma upper lower) - operand) - (cdr sequence)))) - (error "Invalid \\{\\} syntax")))) - - ;; nonspecial character - ((looking-at (rx (not (any "\\.[")))) - (forward-char 1) - (push (match-string 0) sequence)) - - ;; character alternative - ((looking-at (rx "[" (opt (group "^")))) - (goto-char (match-end 0)) - (let ((negated (match-string 1))) - (push (xr--parse-char-alt negated warnings) sequence))) - - ;; group - ((looking-at (rx "\\(" (opt (group "?") - (opt (opt (group (any "1-9") - (zero-or-more digit))) - (group ":"))))) - (let ((question (match-string 1)) - (number (match-string 2)) - (colon (match-string 3))) - (when (and question (not colon)) - (error "Invalid \\(? syntax")) - (goto-char (match-end 0)) - (let* ((group (xr--parse-alt warnings)) - ;; simplify - group has an implicit seq - (operand (if (and (listp group) (eq (car group) 'seq)) - (cdr group) - (list group)))) - (when (not (looking-at (rx "\\)"))) - (error "Missing \\)")) - (forward-char 2) - (let ((item (cond (number ; numbered group - (append (list 'group-n (string-to-number number)) - operand)) - (question ; shy group - group) - (t ; plain group - (cons 'group operand))))) - (push item sequence))))) - - ;; back-reference - ((looking-at (rx "\\" (group (any "1-9")))) - (forward-char 2) - (push (list 'backref (string-to-number (match-string 1))) - sequence)) - - ;; various simple substitutions - ((looking-at (rx (or "." "\\w" "\\W" "\\`" "\\'" "\\=" - "\\b" "\\B" "\\<" "\\>"))) - (goto-char (match-end 0)) - (let ((sym (cdr (assoc - (match-string 0) - '(("." . nonl) - ("\\w" . wordchar) ("\\W" . not-wordchar) - ("\\`" . bos) ("\\'" . eos) - ("\\=" . point) - ("\\b" . word-boundary) ("\\B" . not-word-boundary) - ("\\<" . bow) ("\\>" . eow)))))) - (push sym sequence))) - - ;; symbol-start, symbol-end - ((looking-at (rx "\\_" (opt (group (any "<>"))))) - (let ((arg (match-string 1))) - (unless arg - (error "Invalid \\_ sequence")) - (forward-char 3) - (push (if (string-equal arg "<") 'symbol-start 'symbol-end) - sequence))) - - ;; character syntax - ((looking-at (rx "\\" (group (any "sS")) (opt (group anything)))) - (let ((negated (string-equal (match-string 1) "S")) - (syntax-code (match-string 2))) - (unless syntax-code - (error "Incomplete \\%s sequence" (match-string 1))) - (goto-char (match-end 0)) - (push (xr--char-syntax negated (string-to-char syntax-code)) - sequence))) - - ;; character categories - ((looking-at (rx "\\" (group (any "cC")) (opt (group anything)))) - (let ((negated (string-equal (match-string 1) "C")) - (category-code (match-string 2))) - (unless category-code - (error "Incomplete \\%s sequence" (match-string 1))) - (goto-char (match-end 0)) - (push (xr--char-category negated (string-to-char category-code)) - sequence))) - - ;; Escaped character. Only \*+?.^$[ really need escaping, but we accept - ;; any not otherwise handled character after the backslash since - ;; such sequences are found in the wild. - ((looking-at (rx "\\" (group (or (any "\\*+?.^$[]") - (group anything))))) - (forward-char 2) - (push (match-string 1) sequence) - (when (match-beginning 2) - ;; Note that we do not warn about \\], since the symmetry with \\[ - ;; makes it unlikely to be a serious error. - (xr--report warnings (match-beginning 0) - (format "Escaped non-special character `%s'" - (xr--escape-string (match-string 2) nil))))) - - (t (error "Backslash at end of regexp")))) - - (let ((item-seq (xr--rev-join-seq sequence))) - (cond ((null item-seq) - "") - ((null (cdr item-seq)) - (car item-seq)) - (t - (cons 'seq item-seq)))))) - -(defun xr--range-string-to-items (str) - "Convert a string of ranges to a list of pairs of their endpoints." - (let ((len (length str)) - (ranges nil) - (i 0)) - (while (< i len) - (push (cons (aref str i) (aref str (+ i 2))) - ranges) - (setq i (+ i 3))) - ranges)) - -(defun xr--any-arg-to-items (arg) - "Convert an `any' argument to a list of characters, ranges (as pairs), -and classes (symbols)." - ;; We know (since we built it) that x is either a symbol or - ;; a string, and that the string does not mix ranges and chars. - (cond ((symbolp arg) (list arg)) - ((and (>= (length arg) 3) - (eq (aref arg 1) ?-)) - (xr--range-string-to-items arg)) - (t (string-to-list arg)))) - -(defun xr--any-item-superset-p (a b) - "Whether A is a superset of B, both being `any' items: a character, -a range (pair of chars), or a class (symbol)." - (cond - ((symbolp a) - (cond ((symbolp b) (eq a b)) - ((eq b ?\n) - (memq a '(alnum alpha blank digit graph - lower multibyte nonascii print punct space - upper word xdigit))))) - ((consp a) - (or (and (characterp b) - (<= (car a) b (cdr a))) - (and (consp b) - (<= (car a) (car b) (cdr b) (cdr a))))) - (t - (and (characterp b) (eq a b))))) - -(defun xr--any-item-may-intersect-p (a b) - "Whether A intersects B, both being `any' items: a character, -a range (pair of chars), or a class (symbol). If in doubt, return t." - (cond - ((symbolp a) - (cond ((eq b ?\n) - (not (memq a '(alnum alpha blank digit graph - lower multibyte nonascii print punct space - upper word xdigit)))) - (t t))) - ((consp a) - (or (and (characterp b) - (<= (car a) b (cdr a))) - (and (consp b) - (<= (car a) (cdr b)) - (<= (car b) (cdr a))) - (symbolp b))) - ;; Now a must be a character. - ((characterp b) (eq a b)) - (t (xr--any-item-may-intersect-p b a)))) - -(defun xr--char-superset-of-char-set-p (a-sets negated b-sets) - "Whether A-SETS, possibly NEGATED, is a superset of B-SETS. -A-SETS and B-SETS are arguments to `any'." - (let ((a-items (mapcan #'xr--any-arg-to-items a-sets)) - (b-items (mapcan #'xr--any-arg-to-items b-sets))) - (cl-every (lambda (b-item) - (if negated - (not (cl-some - (lambda (a-item) - (xr--any-item-may-intersect-p b-item a-item)) - a-items)) - (cl-some (lambda (a-item) - (xr--any-item-superset-p a-item b-item)) - a-items))) - b-items))) - -(defun xr--char-superset-of-rx-p (sets negated rx) - "Whether SETS, possibly NEGATED, is a superset of RX." - (pcase rx - (`(any . ,b-sets) - (xr--char-superset-of-char-set-p sets negated b-sets)) - (`(not (any . ,b-sets)) - (and negated - (xr--char-superset-of-char-set-p b-sets nil sets))) - ((or 'ascii 'alnum 'alpha 'blank 'cntrl 'digit 'graph - 'lower 'multibyte 'nonascii 'print 'punct 'space - 'unibyte 'upper 'word 'xdigit) - (xr--char-superset-of-char-set-p sets negated `(any ,rx))) - ((pred stringp) - (and (= (length rx) 1) - (xr--char-superset-of-char-set-p sets negated (list rx)))))) - -(defun xr--single-non-newline-char-p (rx) - "Whether RX only matches single characters none of which is newline." - (pcase rx - ((or 'nonl 'wordchar) t) - (`(category ,_) t) - (`(syntax ,s) (not (eq s ?>))) ; comment-end often matches newline - (_ (xr--char-superset-of-rx-p '("\n") t rx)))) - -(defun xr--syntax-superset-of-rx-p (syntax negated rx) - "Whether SYNTAX, possibly NEGATED, is a superset of RX." - ;; Syntax tables vary, but we make a (quite conservative) guess. - (let* ((always-set - ;; Characters we think always will be in the syntax set. - '((whitespace " \t") - (word "A-Za-z0-9") - (open-parenthesis "([") - (close-parenthesis "])"))) - (never-set - ;; Characters we think never will be in the syntax set. - '((whitespace "!-~") - (punctuation "A-Za-z0-9") - (open-parenthesis "\x00- A-Za-z0-9") - (close-parenthesis "\x00- A-Za-z0-9"))) - (set (assq syntax (if negated never-set always-set)))) - (and set - (xr--char-superset-of-rx-p (cdr set) nil rx)))) - -(defun xr--string-to-chars (str) - (mapcar #'char-to-string (string-to-list str))) - -(defun xr--expand-strings (rx) - "If RX is a string or a seq of strings, convert them to seqs of -single-character strings." - (cond ((consp rx) - (if (eq (car rx) 'seq) - (cons 'seq (mapcan (lambda (x) - (if (and (stringp x) - (> (length x) 1)) - (xr--string-to-chars x) - (list x))) - (cdr rx))) - rx)) - ((and (stringp rx) - (> (length rx) 1)) - (cons 'seq (xr--string-to-chars rx))) - (t rx))) - -(defun xr--superset-seq-p (a b) - "Whether A matches all that B matches, both lists of expressions." - (while (and a b (xr--superset-p (car a) (car b))) - (setq a (cdr a)) - (setq b (cdr b))) - (and (not b) - (or (not a) - (xr--matches-empty-p (cons 'seq a))))) - -(defun xr--make-seq (body) - (if (> (length body) 1) - (cons 'seq body) - (car body))) - -(defun xr--superset-p (a b) - "Whether A matches all that B matches." - (setq a (xr--expand-strings a)) - (setq b (xr--expand-strings b)) - - (pcase b - (`(or . ,b-body) - (cl-every (lambda (b-expr) (xr--superset-p a b-expr)) b-body)) - (_ - (pcase a - (`(any . ,sets) - (xr--char-superset-of-rx-p sets nil b)) - (`(not (any . ,sets)) - (xr--char-superset-of-rx-p sets t b)) - ('nonl (xr--single-non-newline-char-p b)) - - (`(seq . ,a-body) - (pcase b - (`(seq . ,b-body) - (xr--superset-seq-p a-body b-body)) - (_ - (xr--superset-seq-p a-body (list b))))) - (`(or . ,a-body) - (cl-some (lambda (a-expr) (xr--superset-p a-expr b)) a-body)) - - (`(zero-or-more . ,a-body) - (pcase b - (`(,(or 'opt 'zero-or-more 'one-or-more) . ,b-body) - (xr--superset-p (xr--make-seq a-body) (xr--make-seq b-body))) - (_ (xr--superset-p (xr--make-seq a-body) b)))) - (`(one-or-more . ,a-body) - (pcase b - (`(one-or-more . ,b-body) - (xr--superset-p (xr--make-seq a-body) (xr--make-seq b-body))) - (_ (xr--superset-p (xr--make-seq a-body) b)))) - (`(opt . ,a-body) - (pcase b - (`(opt . ,b-body) - (xr--superset-p (xr--make-seq a-body) (xr--make-seq b-body))) - (_ (xr--superset-p (xr--make-seq a-body) b)))) - (`(repeat ,lo ,_ . ,a-body) - (if (<= lo 1) - (xr--superset-p (xr--make-seq a-body) b) - (equal a b))) - - ;; We do not expand through groups on the subset (b) side to - ;; avoid false positives; "\\(a\\)\\|." should be without warning. - (`(group . ,body) - (xr--superset-p (xr--make-seq body) b)) - (`(group-n ,_ . ,body) - (xr--superset-p (xr--make-seq body) b)) - - (`(syntax ,syn) - (or (equal a b) (xr--syntax-superset-of-rx-p syn nil b))) - (`(not (syntax ,syn)) - (or (equal a b) (xr--syntax-superset-of-rx-p syn t b))) - - ((or `(category ,_) `(not (category ,_))) - (or (equal a b) - (and (stringp b) - (string-match-p (rx-to-string a) b)))) - - (_ (equal a b)))))) - -(defun xr--parse-alt (warnings) - (let ((alternatives nil)) ; reversed - (push (xr--parse-seq warnings) alternatives) - (while (not (looking-at (rx (or "\\)" eos)))) - (forward-char 2) ; skip \| - (let ((pos (point)) - (seq (xr--parse-seq warnings))) - (when warnings - (cond - ((member seq alternatives) - (xr--report warnings pos "Duplicated alternative branch")) - ((cl-some (lambda (branch) (xr--superset-p seq branch)) - alternatives) - (xr--report warnings pos - "Branch matches superset of a previous branch")) - ((cl-some (lambda (branch) (xr--superset-p branch seq)) - alternatives) - (xr--report warnings pos - "Branch matches subset of a previous branch")))) - (push seq alternatives))) - (if (cdr alternatives) - ;; Simplify (or nonl "\n") to anything - (if (or (equal alternatives '(nonl "\n")) - (equal alternatives '("\n" nonl))) - 'anything - (cons 'or (reverse alternatives))) - (car alternatives)))) - -(defun xr--parse (re-string warnings) - (with-temp-buffer - (set-buffer-multibyte t) - (insert re-string) - (goto-char (point-min)) - (let* ((case-fold-search nil) - (rx (xr--parse-alt warnings))) - (when (looking-at (rx "\\)")) - (error "Unbalanced \\)")) - rx))) - -;; Grammar for skip-set strings: -;; -;; skip-set ::= `^'? item* dangling? -;; item ::= range | single -;; range ::= single `-' endpoint -;; single ::= {any char but `\'} -;; | `\' {any char} -;; endpoint ::= single | `\' -;; dangling ::= `\' -;; -;; Ambiguities in the above are resolved greedily left-to-right. - -(defun xr--parse-skip-set-buffer (warnings) - - ;; An ad-hoc check, but one that catches lots of mistakes. - (when (and (looking-at (rx "[" (one-or-more anything) "]" - (opt (any "+" "*" "?") - (opt "?")) - eos)) - (not (looking-at (rx "[:" (one-or-more anything) ":]" eos)))) - (xr--report warnings (point) "Suspect skip set framed in `[...]'")) - - (let ((negated (looking-at (rx "^"))) - (start-pos (point)) - (ranges nil) - (classes nil)) - (when negated - (forward-char 1) - (setq start-pos (point))) - (while (not (eobp)) - (cond - ((looking-at (rx "[:" (group (*? anything)) ":]")) - (let ((sym (intern (match-string 1)))) - (unless (memq sym - '(ascii alnum alpha blank cntrl digit graph - lower multibyte nonascii print punct space - unibyte upper word xdigit)) - (error "No character class `%s'" (match-string 0))) - ;; Another useful ad-hoc check. - (when (and (eq (char-before) ?\[) - (eq (char-after (match-end 0)) ?\])) - (xr--report warnings (1- (point)) - "Suspect character class framed in `[...]'")) - (when (memq sym classes) - (xr--report warnings (point) - (format "Duplicated character class `%s'" - (match-string 0)))) - (push sym classes))) - - ((looking-at (rx (or (seq "\\" (group anything)) - (group (not (any "\\")))) - (opt "-" - (or (seq "\\" (group anything)) - (group anything))))) - (let ((start (string-to-char (or (match-string 1) - (match-string 2)))) - (end (or (and (match-beginning 3) - (string-to-char (match-string 3))) - (and (match-beginning 4) - (string-to-char (match-string 4)))))) - (when (and (match-beginning 1) - (not (memq start '(?^ ?- ?\\)))) - (xr--report warnings (point) - (xr--escape-string - (format "Unnecessarily escaped `%c'" start) nil))) - (when (and (match-beginning 3) - (not (memq end '(?^ ?- ?\\)))) - (xr--report warnings (1- (match-beginning 3)) - (xr--escape-string - (format "Unnecessarily escaped `%c'" end) nil))) - (when (and (eq start ?-) - (not end) - (match-beginning 2) - (< start-pos (point) (1- (point-max)))) - (xr--report warnings (point) - "Literal `-' not first or last")) - (if (and end (> start end)) - (xr--report warnings (point) - (xr--escape-string - (format "Reversed range `%c-%c'" start end) nil)) - (cond - ((eq start end) - (xr--report warnings (point) - (xr--escape-string - (format "Single-element range `%c-%c'" start end) - nil))) - ((eq (1+ start) end) - (xr--report warnings (point) - (xr--escape-string - (format "Two-element range `%c-%c'" start end) - nil)))) - (let ((tail ranges)) - (while tail - (let ((range (car tail))) - (if (and (<= (car range) (or end start)) - (<= start (cdr range))) - (let ((msg - (cond - ((and end (< start end) - (< (car range) (cdr range))) - (format "Ranges `%c-%c' and `%c-%c' overlap" - (car range) (cdr range) start end)) - ((and end (< start end)) - (format "Range `%c-%c' includes character `%c'" - start end (car range))) - ((< (car range) (cdr range)) - (format - "Character `%c' included in range `%c-%c'" - start (car range) (cdr range))) - (t - (format "Duplicated character `%c'" - start))))) - (xr--report warnings (point) - (xr--escape-string msg nil)) - ;; Expand previous interval to include this range. - (setcar range (min (car range) start)) - (setcdr range (max (cdr range) (or end start))) - (setq start nil) - (setq tail nil)) - (setq tail (cdr tail)))))) - (when start - (push (cons start (or end start)) ranges))))) - - ((looking-at (rx "\\" eos)) - (xr--report warnings (point) - "Stray `\\' at end of string"))) - - (goto-char (match-end 0))) - - (when (and (null ranges) (null classes)) - (xr--report warnings (point-min) - (if negated - "Negated empty set matches anything" - "Empty set matches nothing"))) - - (cond - ;; Single non-negated character, like "-": make a string. - ((and (not negated) - (null classes) - (= (length ranges) 1) - (eq (caar ranges) (cdar ranges))) - (regexp-quote (char-to-string (caar ranges)))) - ;; Negated empty set, like "^": anything. - ((and negated - (null classes) - (null ranges)) - 'anything) - ;; Single named class, like "[:nonascii:]": use the symbol. - ((and (= (length classes) 1) - (null ranges)) - (if negated - (list 'not (car classes)) - (car classes))) - ;; Anything else: produce (any ...) - (t - (let ((intervals nil) - (chars nil)) - (dolist (range ranges) - (if (eq (car range) (cdr range)) - (push (car range) chars) - (push (string (car range) ?- (cdr range)) intervals))) - ;; Put a single `-' last. - (when (memq ?- chars) - (setq chars (append (delq ?- chars) (list ?-)))) - (let ((set (cons 'any - (append - (and intervals - (list (apply #'concat intervals))) - (and chars - (list (apply #'string chars))) - (nreverse classes))))) - (if negated - (list 'not set) - set))))))) - -(defun xr--parse-skip-set (skip-string warnings) - (with-temp-buffer - (set-buffer-multibyte t) - (insert skip-string) - (goto-char (point-min)) - (xr--parse-skip-set-buffer warnings))) - -(defun xr--substitute-keywords (head-alist body-alist rx) - "Substitute keywords in RX using HEAD-ALIST and BODY-ALIST in the -head and body positions, respectively." - (cond - ((symbolp rx) - (or (cdr (assq rx body-alist)) rx)) - ((consp rx) - (cons (or (cdr (assq (car rx) head-alist)) - (car rx)) - (mapcar (lambda (elem) (xr--substitute-keywords - head-alist body-alist elem)) - (cdr rx)))) - (t rx))) - -(defconst xr--keywords - '((medium . nil) - (brief . (((zero-or-more . 0+) - (one-or-more . 1+)) - . nil)) - (terse . (((seq . :) - (or . |) - (any . in) - (zero-or-more . *) - (one-or-more . +) - (opt . ? ) - (repeat . **)) - . nil)) - (verbose . (((opt . zero-or-one)) - . - ((nonl . not-newline) - (bol . line-start) - (eol . line-end) - (bos . string-start) - (eos . string-end) - (bow . word-start) - (eow . word-end))))) - "Alist mapping keyword dialect to (HEAD-ALIST . BODY-ALIST), -or to nil if no translation should take place. -The alists are mapping from the default choice.") - -(defun xr--in-dialect (rx dialect) - (let ((keywords (assq (or dialect 'medium) xr--keywords))) - (unless keywords - (error "Unknown dialect `%S'" dialect)) - (if (cdr keywords) - (xr--substitute-keywords (cadr keywords) (cddr keywords) rx) - rx))) - -;;;###autoload -(defun xr (re-string &optional dialect) - "Convert a regexp string to rx notation; the inverse of `rx'. -Passing the returned value to `rx' (or `rx-to-string') yields a regexp string -equivalent to RE-STRING. DIALECT controls the choice of keywords, -and is one of: -`verbose' -- verbose keywords -`brief' -- short keywords -`terse' -- very short keywords -`medium' or nil -- a compromise (the default)" - (xr--in-dialect (xr--parse re-string nil) dialect)) - -;;;###autoload -(defun xr-skip-set (skip-set-string &optional dialect) - "Convert a skip set string argument to rx notation. -SKIP-SET-STRING is interpreted according to the syntax of -`skip-chars-forward' and `skip-chars-backward' and converted to -a character class on `rx' form. -If desired, `rx' can then be used to convert the result to an -ordinary regexp. -See `xr' for a description of the DIALECT argument." - (xr--in-dialect (xr--parse-skip-set skip-set-string nil) dialect)) - -;;;###autoload -(defun xr-lint (re-string) - "Detect dubious practices and possible mistakes in RE-STRING. -This includes uses of tolerated but discouraged constructs. -Outright regexp syntax violations are signalled as errors. -Return a list of (OFFSET . COMMENT) where COMMENT applies at OFFSET -in RE-STRING." - (let ((warnings (list nil))) - (xr--parse re-string warnings) - (sort (car warnings) #'car-less-than-car))) - -;;;###autoload -(defun xr-skip-set-lint (skip-set-string) - "Detect dubious practices and possible mistakes in SKIP-SET-STRING. -This includes uses of tolerated but discouraged constructs. -Outright syntax violations are signalled as errors. -The argument is interpreted according to the syntax of -`skip-chars-forward' and `skip-chars-backward'. -Return a list of (OFFSET . COMMENT) where COMMENT applies at OFFSET -in SKIP-SET-STRING." - (let ((warnings (list nil))) - (xr--parse-skip-set skip-set-string warnings) - (sort (car warnings) #'car-less-than-car))) - -(defun xr--escape-string (string escape-printable) - "Escape non-printing characters in a string for maximum readability. -If ESCAPE-PRINTABLE, also escape \\ and \", otherwise don't." - (replace-regexp-in-string - "[\x00-\x1f\"\\\x7f\x80-\xff][[:xdigit:]]?" - (lambda (s) - (let* ((c (logand (string-to-char s) #xff)) - (xdigit (substring s 1)) - (transl (assq c - '((?\b . "\\b") - (?\t . "\\t") - (?\n . "\\n") - (?\v . "\\v") - (?\f . "\\f") - (?\r . "\\r") - (?\e . "\\e"))))) - ;; We prefer hex escapes (\xHH) because that is what most users - ;; want today, but use octal (\OOO) if the following character - ;; is a legitimate hex digit. - (concat - (cond (transl (cdr transl)) - ((memq c '(?\\ ?\")) - (if escape-printable (string ?\\ c) (string c))) - ((zerop (length xdigit)) (format "\\x%02x" c)) - (t (format (format "\\%03o" c)))) - xdigit))) - string 'fixedcase 'literal)) - -(defun xr--take (n list) - "The N first elements of LIST." - (butlast list (- (length list) n))) - -(defun xr--rx-list-to-string (rx plain-prefix) - "Print the list `rx' to a string, unformatted. -The first PLAIN-PREFIX elements are formatted using `prin1-to-string'; -the rest with `xr--rx-to-string'." - (concat "(" - (mapconcat #'identity - (append - (mapcar #'prin1-to-string (xr--take plain-prefix rx)) - (mapcar #'xr--rx-to-string (nthcdr plain-prefix rx))) - " ") - ")")) - -(defun xr--rx-to-string (rx) - "Print an rx expression to a string, unformatted." - (cond - ((eq rx '*?) "*?") ; Avoid unnecessary \ in symbol. - ((eq rx '+?) "+?") - ((eq rx '\??) "\\??") - ((stringp rx) (concat "\"" (xr--escape-string rx t) "\"")) - ((characterp rx) - (let ((esc (assq rx '((?\( . ?\() - (?\) . ?\)) - (?\[ . ?\[) - (?\] . ?\]) - (?\\ . ?\\) - (?\; . ?\;) - (?\" . ?\") - (?\s . ?s) - (?\n . ?n) - (?\r . ?r) - (?\t . ?t) - (?\e . ?e) - (?\b . ?b) - (?\f . ?f) - (?\v . ?v))))) - (cond (esc (format "?\\%c" (cdr esc))) - ;; Only base characters are displayed as ?char; this excludes - ;; controls, combining, surrogates, noncharacters etc. - ((aref (char-category-set rx) ?.) (format "?%c" rx)) - (t (format "#x%02x" rx))))) - ((atom rx) (prin1-to-string rx)) - ((nlistp (cdr rx)) - (format "(%s . %s)" - (xr--rx-to-string (car rx)) - (xr--rx-to-string (cdr rx)))) - ((or (eq (car rx) '**) - (and (eq (car rx) 'repeat) (> (length rx) 3))) - ;; First 2 args are integers. - (xr--rx-list-to-string rx 3)) - ((memq (car rx) '(= >= repeat group-n backref)) - ;; First arg is integer. - (xr--rx-list-to-string rx 2)) - (t - ;; Render the space character as ? when first in a list. - ;; Elsewhere, it's a character or integer. - (let ((first (if (eq (car rx) ?\s) - "?" - (xr--rx-to-string (car rx)))) - (rest (mapcar #'xr--rx-to-string (cdr rx)))) - (concat "(" (mapconcat #'identity (cons first rest) " ") ")"))))) - -(defun xr-pp-rx-to-str (rx) - "Pretty-print the regexp RX (in rx notation) to a string. -It does a slightly better job than standard `pp' for rx purposes." - (with-temp-buffer - (insert (xr--rx-to-string rx) "\n") - (pp-buffer) - - ;; Remove the line break after short operator names for - ;; readability and compactness. - (goto-char (point-min)) - (while (re-search-forward - (rx "(" (** 1 4 (any "a-z0-9" "+?:|*=>")) - (group "\n" (zero-or-more blank))) - nil t) - (replace-match " " t t nil 1)) - - ;; Reindent the buffer in case line breaks have been removed. - (goto-char (point-min)) - (indent-sexp) - - (buffer-string))) - -;;;###autoload -(defun xr-pp (re-string &optional dialect) - "Convert to `rx' notation and output the pretty-printed result. -This function uses `xr' to translate RE-STRING into DIALECT. -It is intended for use from an interactive elisp session. -See `xr' for a description of the DIALECT argument." - (insert (xr-pp-rx-to-str (xr re-string dialect)))) - -;;;###autoload -(defun xr-skip-set-pp (skip-set-string &optional dialect) - "Convert a skip set string to `rx' notation and pretty-print. -This function uses `xr-skip-set' to translate SKIP-SET-STRING -into DIALECT. -It is intended for use from an interactive elisp session. -See `xr' for a description of the DIALECT argument." - (insert (xr-pp-rx-to-str (xr-skip-set skip-set-string dialect)))) - -(provide 'xr) - -;;; xr.el ends here