From 2eec932f44a3bdc4d2f331a5e48c15dc8632bb31 Mon Sep 17 00:00:00 2001 From: Matt McCutchen Date: Thu, 10 Sep 2020 13:39:47 -0400 Subject: [PATCH 1/4] SuperbChemistry 3.0 - Update for compatibility with modern versions of OpenOffice and LibreOffice. One of the regular expressions contained an unescaped [ in a character class, which no longer works because it now denotes a named character class. Also, work around a bug in LibreOffice undo. - Put all actions taken during a formatting pass in an undo context so they can be undone and redone as a unit. - Change the file extension to oxt and add full extension metadata and a menu item. - Add support for formatting a selection rather than the entire document, with a minor limitation. - Make the rules for recognizing sequences slightly more conservative. --- .gitignore | 1 + Makefile | 11 + README.md | 61 +++++ SuperbChemistry-test.odt | Bin 8247 -> 9223 bytes extension/Addons.xcu | 20 ++ extension/META-INF/manifest.xml | 3 +- extension/SuperbChemistry/Main.xba | 363 ++++++++++++++++----------- extension/SuperbChemistry/dialog.xlb | 2 +- extension/SuperbChemistry/script.xlb | 2 +- extension/description.xml | 16 ++ extension/extension-description.txt | 1 + 11 files changed, 334 insertions(+), 146 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 extension/Addons.xcu create mode 100644 extension/description.xml create mode 100644 extension/extension-description.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7cc7f81 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/SuperbChemistry.oxt diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..59b2de9 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +OXT:=SuperbChemistry.oxt + +all: $(OXT) + +# No dependencies declared. Clean in order to make it again. +$(OXT): + rm -f $@ + cd extension && zip -r ../$(OXT) * + +clean: + rm -f $(OXT) diff --git a/README.md b/README.md new file mode 100644 index 0000000..38e420c --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# SuperbChemistry + +This is an extension for [OpenOffice](https://www.openoffice.org/) and +[LibreOffice](https://www.libreoffice.org/) that applies superscript and +subscript formatting to chemical formulas in bulk in Writer documents. See [the +web page](https://mattmccutchen.net/superbchemistry/) for user documentation. + +The rest of this document refers to LibreOffice, but the same remarks apply to +OpenOffice. + +## Development + +Run `make clean all` to generate `SuperbChemistry.oxt`, which you can install in +LibreOffice via "Tools" -> "Extension Manager". + +By default, the library is installed read-only to prevent users from making +changes that would be lost without warning if they uninstall the extension. For +development, you can change `library:readonly="true"` to +`library:readonly="false"` in `extension/SuperbChemistry/*.xlb` so you can edit +the library in LibreOffice. Then you can copy the edited `Main.xba` from your +profile (a typical location is +`~/.config/libreoffice/4/user/uno_packages/cache/uno_packages/*.tmp_/SuperbChemistry.oxt/SuperbChemistry/Main.xba`, +but this will depend on your operating system and application version) back to +this working tree. + +`SuperbChemistry-test.odt` is a test suite that exercises most cases of the +formatting rules. + +## Formatting rules + +- An _item_ is an element symbol (`[A-Z][a-z]?`) or text that starts or ends + with a group symbol `()[]{}` (depending on which side of the item we're + looking at). +- A _charge symbol_ is `+`, `-` (U+002D hyphen-minus), or `−` (U+2212 minus + sign). +- We recognize the following sequences: + - An item followed by one or more digits and one of the following: another + item, space or tab, closing group symbol `)]}`, sentence punctuation + `.,:;?!'"`, or end of line. + - An item followed by zero or more digits, a charge symbol, and any of the + things allowed in the previous case except for another item (to avoid + matching compound words like `A-B`). This means we won't match something + like `Na+Cl-` , but formulas are rarely written that way. +- A recognized charge symbol of `-` is replaced with `−`. +- In a recognized sequence with no charge symbol, all of the digits are treated + as a quantity. +- In a recognized sequence with a charge symbol, the last digit is treated as a + charge amount and the rest are treated as a quantity. For example, in `Fe3+`, + the digit should be a charge amount. Exception: if there is only one digit, + it is treated as a quantity in two cases: + - If the item is `H`, `O`, `F`, `Cl`, `Br`, or `I`, because these elements + often occur in quantities greater than 1 and rarely have a charge greater + than 1. + - If the item ends in a closing group symbol, because it probably wouldn't + have been enclosed in group symbols if its quantity were 1. + +## License + +I, Matt McCutchen, the sole author of SuperbChemistry, waive my copyright to it, +placing it in the public domain. SuperbChemistry comes with absolutely no +warranty. diff --git a/SuperbChemistry-test.odt b/SuperbChemistry-test.odt index 9740bbcee3d638ece2330fbfff69a246e690c881..33f2e1c65ece9047516c3fc361e54ff27de5629d 100644 GIT binary patch literal 9223 zcmd6Nby$?m_xFm@rF4gaG%TGW-Q6jjvP&%`p@M{fAl+Tk-5t`g(%rB$3P=jP;P30B zKCgbS_xIm>_S(7U+P%-`o|!Xq=FIurO0swF;Q#TQ)XKuM=M8WTRStbsjZ0<$P5f&hM2iRlzs!d(f>z? zZfc^oU~?-ACx`Daj%+LtTU#4DBe0nb%ir|~{-9@WV`~I4bNF9s-89n4*yz7${i%(1 zwsuZ-x2pfkr``7LfB!T`h!Moe@uwaV64H0G{4GA>B%Yv@_5oO);?J&^mQ&^dR~KQUnXueo1yY_TG#kP0mX)gtz$g)QfQqOAxOw% z??kM=u6hvh4lW~`YbrywQ{TItP&^5m_Iy`*JoZSK}=;ihq*`3T1Qp#D;2$=Cqnj2ne^jVL%aADM=1xGae^dNi1Zw`3aN~I zug#Ln^WyO&0vvY93kzvGK{E&-5=+%ZS~L?q!&XMoVyx-VtBsBR=gq8v)*5~B^v>S# zItotk*7U;H`SI_L{ zmcxoA(7`?@c@hHLDYOGSoU`t>x{-<5MR}nenYb6m=p1- z4AndA)@NJ=^P}QO(8^VrO0PsdR_%GtjGY+bM=Q335@sih^bS8Vv(=qn1SLnOk;Nr#wX317-}Mmpq! zLWf24S@*UYC|9yVyO0y6H{mc;UK7miwH964ZlykPAjg~Y|9D~~?Z1@oAG&bSvwHn~ zsTUK7l3w3Tn)G}BZPMJXIY%=H#0qTTcsp?#b#z^(dG32H7=*RE%k0hBg&z^9=;&ba z2*sCsVjlyhyNSr9F+T6RP*KPugXzqVk%7m-9PwvOwWnL{jOvI(h6Q4t`n+B)YPkfdHWLNrUc83$VArfqUI^Ool-p>Abi5a>(cb*i1m?WNr9|?gW=)O@^+2SZw z>&ryushXi<&ldDid+{{QWwvs*dpl|iGzt}RMqEZlTr4}sR{Hw-tWRB>7#=pwZkVxX z;DH5s`S|jgy_#o9y+HLPYs~F!(F+D~>zNMaW$o4%+?sujjDi?hKBF`1hHg^~^+nL| z=R;W8EE@SXFZN&-O`k5q?y$BLl%01Cu%An&k!gZMit=p5ZCI+%A9YclC_e_5DSz%c z`?7iA(Du}aM3IZ-;`DT9|8OqzgobBVSVR!v-CL+h$#WvV0F*aHvpf;)U_PBXu$}c1 z1~zTDAXYD!=?aKDl{SjpPvQ2wU;qOznbLy$a93_#W<1(DDQtdnK1R{`X|KovzvuN8 z#HCa`%)!A6(vYvl;E!@l&LF%q|A=2|7uPQUvxQXDl&(Hx&`5{iC4cgqO9wlNA%%9t+?w*<$N%tH2j&Q$F#0ebET|m2Zns>^pH!1eBZIK@D7C)QC&;P zkyKZOgcJhMk<=I(CK7mF8UTBVQAo_(giw~=95Y#ShOoS}8|Q9M$=#+*_L&A}p1J~} z@5A-Tgk6ibd;j&2k|Al{$>V0l1ohAG{=NlT8b1CLMTTi}6fY@|>T2F_zn{I28{=vl zQx}lZiQuU3+DRQXOT>!KJn*Qg$nvgy8Fm7qxK0M@Qth3m76HA5hYBkMiTEMpmpV4@ZQ&GFV?%{TXb z4M;-#v=gvSy&fA+%fA%qIyOxh)`GD(D(OY%);`TDeK57EK;U2>fx_K#AwcTARJ!>p zFO{0=W+UO#cMY^@LB5Xq$nF3B2o`;{7`U98{@NvS}9doAvY? zscWD35RF4b89>coCwt;3@A`3u0+K~gZbiY}l3k%5MxRUulNjRYHEvuKm_XUQ34Om- zQ!e*$+8R5OaSC)6>03Zs*Lbd_esp+i-Fu7M`rs^Kv9pQ*QZe82gdQU+a#;w7|Aijqg82pTDRv z-u#lKFp)7y<13m5j!^}r@Ro4SwS0!zF)VtFO)sg95MH5{uIvWF&v+g@$uJn@t9O5% zvawJdsHrxh=u4kj;h=v4;nF6* z)!~&)DT>~{T0(j9NUT&p6)+l-y1MM0CM1$2g?tb1a6mNsZAV$q{sOgeYi`=m!!|UF zD1uV7j1P&BL{8U*0o+OV2H&vmF=j9I(@wRo12aQo=_hTrS9fp2B-HaA)fxl{#yAl(FBfCZm3hntItlAM}22M33+u&}+oy{D(AkB?7qaBxIKL}FrMMn*l%F4?2_V&@y(bd(}4aW8LHG+Mg1psh&N?J@<&2@TrdN**J z2Mu9y%IX_sUm4Z*)xJ~IU`Yv(ai=)feIC!4Upfu53ahV2vf(4_E*h`}@JkVV-2+YI z-`fYS2oG&lX=%T42WyzBe+4%K+gUaXqmMsaj%36r6jsg?zsnvb8ou*!V$ELcl+fr87AzVVI^WaQuwF)Fq} z*mkGq5S?I-t9EyUFrqM@YzS%L3c*Kat!cgp#@3LP zQH3$a75**;m%SjV=7RlL2tJN_UKnm@(Bn;07EBu1-Z2`t7<}UbWh{zB-FLh}=5TE) z;h)m?Y(m_#WMVaJ#}64-aD8hN&QV#N{W=E==XiIHZ6>{{9H0X2osPXu`@Q26wI^aW z*Vb(pCRhwh5RZW3K_*0%lXAkgAhjb%=R|6#=XokoQ1+nq$tSet&d!;S)MeTadxMg$)zcX%PHr!107yl@4EE# z8hsHy?abpzMvj?RvL3kN>1Xvd#yW`?cd)J1CpW){n|$A89cn+Y%=Y4~oPk;KaBhFJ zix|0+9{<*5v^PhZj8j$e*E-tJfM%5Tr})*Wi&__-(mw_l7A{i0WyXAxkHX`R0)IYI)k}iWWPID@X?>_dqMH;<|O(X*P2k zAEcLhbe!xij!qKDjB}G$l3d>P!FOA5IPaBG$TKD+A98J)ar0`T)LD2{+E*tQY7t9v zQuHyj@qt?-Zqv$&7ZhT1tZ5A1j3V9^h%pk6S6tVIS$Z+9WJ-6xlpkd{QNk0vXcfwK z=RAs#8Z_6t2xlGVY2e2A_UaA-f#Lt}MdHyJ@z7;LKbk5i?Pj?Hq{S7)ibM>(|L4g4 zr^DN=%W;Ib*_eHg!gqCKLsz)4n)ZuQOGiApAw!4UY6aRXk6?^WwHcmhT!y&~y*$qb zJw~q53sO!;l|s$zkL00UXZB{L242C+5@`H!HR9wS;2?#)g_-f&AZgU>>c?~GkB6*@ zUsaFeaHpmmtHR(SGs`}FSmJMO6K4|-Rn-(`v@zt-S6}bko9zlzS0N0QSjnLmwHeZZ z44AqPH;O2e{7DRn=`~DzhAv+8qH_o;`7&@hYH{ zl_j)v97|b7G8!|6&I>s_FY62ns|=gc>Nj(ZTbFjqn4RV1j}cF==x18`SLCnBas5e= z7B}U2>ONS^I*Lzue?y}*xKA-qgurmri_GaC#$nKvB4l}evS|{tlNVc;5wMHfr1r(y zVG6SG4A^H(XR^L4bKL!p7_cJYqpx#N?I)06$eVP#|d8oJTJ>5^q$@R!)~ z1p5h6H)6Kv5gm>v5eBtOwVD-tpE(XD*ST|3w^+@1juscNvu_1(ec+|7^oS=jxkk#u42%Pf}P z3i0{@!=gGVh+*SxVY9evI_xEtVwgH>@-_tOCwT@akdNW?@q-{@^Usnwsy z+*LGj0e4^!9c19l#($CfzBww@VwloA%~g#WtwK@AG#1>wRXeSr1n-OJj1~y>uBQTZ zIu8@r^x_|Wd||d76jX>aeCMhtw?;s!{+*Y(&vmOMUS$0Qfv&MQ>K)O9%Vp9yn*I5? zW@p{$nu;e%R_+w^Ax8x%4E+=869xz#t(S@m@XQPs`ex76YKRBW56tlIJ$2uSUtA+S zD~&LL21<7cwSV~71Pyn-`oP3wJR85qA>#g+W{u>tCnR1 zJ)Fy=+MUC%>a{lH9&;-ZjdB}J9nRn@kTq-a!Xxwu;&wu)KwBj$bB^kC3OGI{M4oKN z(0jA6LEF$l?Gk%WGv_I^b+*W5q_uel*He~JYP?e)xfty{CPEBfs5Lp(a!~;tqs6NV zQAPj02T6v~n(kFB#lWcSU6hdOKvJo;Xu(!Xv0js2nqi$Ld7b=f>FqtxaNWy`y-#P*-P>vh+0c18l41# z^h@0E8&uFiisdrZ=-zADVPDwPk26@J+ZP#L=@+_x>q326fXq~de~Ms?xtDEWXS*MH zt%fVj0jS#;D_HGj|CU^pe8D^%LPoGa4h=9Jtuc3+6cEi^KHV&`FIR(hI$-N_=VNaB z*O!`q)$t?dWK<5(r*SYn4}Bx@pnQA2dmJEs_?S5XY%;pcG$UVVc|~*X@x5Z7G1U`qr;$+dW;k^~0~AG&!z|`i z5-L|E7Ut+s|7*~g|I6+!uc1_Y#ds?DUdnlG={IDLArcL`WnmT5$IFuc-?iK6hNVy#A%Ml=^mB}$e zixcNpeRoF=eyr&J^02;j5+XrRqWRcX>3d|dq(nKpue)I0G?gas=lroP*R`0*uA#{t zO8E(*i48G{2MfLkJ%S2$VjRMX!9`7lgxgsPE^G}wrrv#X2zgqemYCOGF8r7=9Ml13 zG7$x9E#XpjsqeKZ0*vXqrDW7o+go@HB-{PdB2dcsRj7D=J0;9X|>XoqU!0@ z2y7KmnSsP@KSAdI7``sDMFcu9%I`PipWi~y8NVX3OGr|Yx9LHcSuf)4yV_y7Tdpf= zVw7)M$P*~8s3K;=Akoo6%-MrR%zh|xFx*Ys5veMwJjs6??>xC+y@FqeC;haH1^cPB zJ2q-uQT3|CCk;4#m>QetV-h(yI4otfcj>t}Zl4)bnSfLR9z|4f9PHG1;s_u#N6Q#j zQB*#xXIyRbv92_ehD5~#Yhg*6a9LYfvVL>&wM#QOjicks&&x_|L$PMfEyU|mhxuB8 zbOufYHMKVxIEEeyI40K`e6Y%>{T_5m$=ezM|2jjcN*N@*5qv*A=85Ve+Cr=O8Emgg z|G4Ubr}h$kBx5yM6mFer|HIuUA#$OgXxK61?^ulHleHmf&J5A$LryatlR%8R6W(F` zh5LZ0Rupbj)>(KZIg!AJL=K#dAA0k(o%3w5-^iXMX9bDyrijb1!Jf&HAn}seaq^jY zIBqu4)GEP>)5hwyAvgpmGL{Hg0C76>I-l-149XKlv9hx6lBm~?;koTq>a?ikqk(i^ z@yZ21J?*yjb-XxK&#jm!!RH|!;gT6SdJn2$nVNA@u?%Q1r=(xwP*=Lzbv+20*qGKy z#+JsCJ}_&%gSsBB?UfqH>0hrYX1Y4=J%D?64H%g_8k|>k=1yOTDgsTSD))(RPagTI zQ1Vu>Z_aCh+~5p6%d@CjNslvP#(sXq4?EhK17u}XGl)l!Wz}$B&%E;u;2Q7`n~o<2 z#w9wu5G*%|4FL`qx8Wutj+rNmHcF_KQi?}M&iqhH{*QH$fE0+2vVrUVU2Pu z6HbTP?PmnEQ~9PK0bToc>2?x^HUNQy!=Nzi>> zUS6aajq^-Mc76T^2t^!{bVU-_uNCAj7E8els`b-ghtRk{rCetHo*1?GiQ|{c8lH0r!gV zow!f?WJS&7c;Se)5CV*x4Ty9bMo7y ziFF7by!%aB#0l%a`7GQUpCvD@Cd?$QAi?qvM|Z;)6h_M5a&*2YyzmEAZQ$}eMk&0g z7v4vJB0_9gbDJ}oht zw>VQcu9qJ}9z_vZ9ZB!rH>!!|lqRdi6-Eq6_i4#k9lyh+k&OX7Sh)0n)M=o;VycxQ zM!n43))hpiODK83>S7R_>azH?gc_|{#tPXZ4am34^^ru_>aH311V$)CZ6ZUnT>5zD z@r1xV%L>|+DPj3HjuL$j0+LG7R?s%s_}&rvlyIVv<3)iZ$j#cLk?C56m?Nk)@0CIPG{hVq0DGhlhJ!f(VVU;J)IY;)LW$WlA2 z(*kV;GNXd*R!O5#I{zm);^T>>uJYHgc38sw{e^)n@W-of8qQB|4e%hn7GCg%rwRMc z)7*i>0sNhUzp=tE@`I39lKoxlms|BL7Uy|2HL*te|X zPcgdz`^Tr;@cutK^kbBMWir3N!SIi2=1pGq=B1SHl=r{2e$58|{=6yp-#O18nt#g{ z|MNIN-wn{;a>qYVezf}6VDL7(^HYo-{F>|ex8AP~`8HJiDRP9r1&;q#{MC`%`s-hW b>@VJ1Nf!PlUk(5~xcS)K_@CF`t^9ug?LdZ@ literal 8247 zcma)B2{@GB_eYT>dqql?$WmipCrik_CZdImF&N9tn8h9m385&mh3s4QC2Po%wJZtE zM3yYsvv2UX>Bg{eo4njdu0&oNbW&sCd>>)6;02<(us5{&o-!Z!$p9K?aZV2B+AF9wbj1Y?j$ zf_7(nyF)~Jnnwsm6Oj;Zc>1?F%=poRVlD09AT(sp7y|yFF@Ku`0*=5S@D9NIoW$R7 zr~jf2frMKjA*kP`|2O2{>?A1LciR7oLc`&9|BYh%i_2bR}E;#+gJf@q1pAM_VywC|Wh58rw>A%p>-1NcTRQE^pAu8sLAt&a^0fN}T9)m`Kk>Tt=kk7b7V+KkTb^rrHa zEyxE~zZz=c)@~9SRgZjpE5YY0Wz`wAuSjbrRS&)ej>gbtP_QVcAOFdPdm~S~bN$b- z+;mjVkVT!4984+Xyg16Am8Y8TJOt~z8{L`y`lb;m$Twc1KH`(1GtbEz$Oo?1%#^Fm z^T1*|sF9qf{Pz`du`suR(#Cpz@*~~~W{$SC>Otow$h>hNn7rXl93U9=eydA zi-?_DRGg@yKvV zbMnFBx8!(YfulrTO)3#klT{#HzA`)IH%IDlXy)W+wwbSQqbycWYIS(iw56Mh{9un|&1yQsy0vr_eG#Jh`N*1q9K|0zo4654?Ed%vktLKh>#XY2z;XTQ_ zN*Q~lm~Cy=p=NZ6ulwZ&?lCdo-Zd_sdy1AmDr_8-84PJy>JtW%GV%ycsm50_9c?d8 zTfYT7T>uoyg~`vslbv?D-Z0y9FCTCB?KkBQjU7Hw$i0yptV#OVNK8RR*MxLzr70|~ zuK$CVix;UW#jUJNYU6h$`H}4B#r-<9^3=98@~YPLI0nfnmHq8HJ(D|w70An|+OhFv zXGQbf=(C2q5GB}^RL0Lmr>xxbGjHibXuzkd7v*kMPML|hmR}LG7G^0N+sVxvoTz$k zogGv~ywxg--MQ{cfn{|aWDunYYJ#00?Y7n6G8yF^=1VX3dgwx=zF^O@Xh|5Ne=$&NAe+P1UpQyA`^2wd?DPi9&JZ z9;tV=-j;-ve8y0{1vAI`)fr0R$`S8hn~Z2nJ9V$4{a%vkylcS1%29~!Qt$BU=HVS; zLQqYsv!ZG_L_}muyBAdd7ON@oF#v^jv4fxp;kfO(X2<{`1Q;>%_n)$F*~rH_qpaT_ zc{K8ltCFnn@=YyXefIRC*xR2u_%sT+`(9@50){5cjYRxf-peJ%dFI3E0dpaqMuJEntn<@8% z4#%V=>x92x|8xqVG@TsV9As>z)Rv93&VFl3D=tx{TI?+AZkc)dMk>SXeU?)u51`gn z^h!Z10SQ5u{F|tKnNQC# z);@^T(*EKgGhZhC-kDb}@s#2hNYyzc!aBDhJ1Zk~+Dt{p-c2#g!kA|4zM-7XtTs@l zQW9z%4USzJv5<42QrwuGj=oPN!Ul|ZFH3322^DsZ)VWy5q}Z(&jP{$Hm?S^2B>Jg(X16GBnimiocA%?@bH3yIxpBp}?p9 z%+-Cg)Q~phQ;Gk3epl5+OuC9^;OWKp7xS1ERPO4UUQGR#8{pC4t5;oUh;=Q zOdtJmbk;|b)jcQ%k_7vDN~d%^sQLN^Lk&$@++HGJ+yWv|mrYC7ReTQ@YiK95MD|x2 zoYbkO@$ys_>(6;HAr<^p*!P=b6RrKL`wREtSs0TjV=f6^ihT6_`~@P>RMyYWyF5g! zLupJte(~NaMweGyBR0>CVeL7IWOek8q;$Sw=5L=>t0w*Uy=rN9trm$z0w2B3@dC~| z50XGIfa3m4vKZHGH-A13vReZBLRJhDOY|JPy8h{QV_Xa`8@#_WCxDgOl@pGF*Fn9L zWKOo?$((d#uuxkb-NTIU1`01EoP+r&C5;4JL=Wcq%@M}CD@09-J#oSMkbt=sv68-!FZ*uNNX`}_P z%A|EB3gFP%Lh_>To1fJqo~E0glCFGp53O{=9ecKCpILv+rocFi&DQok(tpF`ef7C| zD7g8t^o&b>+YJrn^nAvrOm^gMDDq5(vmAz>>dgU3L)Akk48ZJ^b-p64cP8Gb1z_Kf z2qE(stLgOF0mYZC#ztU{{mK4y6s(?~>#EfAv=VXm2E!V2^@(VgwF2{#q@ejZN2#omS#jMFOuBXP`4{rr z&yUp&q!`Flf4~4;bcRHN$x_#;#Uj6stZA`CI=EO$ZUs}CkfBbF#*F*jk&MbIBCo>gAu7#hO@{sZA3mV1uP)Jt$|1GFI-U)H^|Y_ z%^T}J&E3mR(czGnQ+Fqf);z94 zAGT*_%O+Kdl37lUnPyavP0HBoI|Cx(q8sJ*|RiPZ~`2?nBdbRgIA6Vum%Eaa9O z6)r~6m$kLBe8CkN;y4^xDAYmJrOo+|5NDEsHgQ0{&CT4h9=s%Thy2T+B>N3UazF804aq0XWi1P*_@8T5xxi^QS5c@Gm|D25Gm;WdRn%=R$-83?(QmAS}4Y zOR(^NHHyIdyGd|3{Ffra9DYo3SDlEEkeJ|)|D5bUz4Dv;ggNE*d_@o{C$lTs3Ih9) zt;y-)rRnba@`p45F)l4A4CBoH=fG`za%2MmqXi|TgzSYSxfC%_I}3ocsIZWX-~ow0 zG(cfcG!$e903#uUtWZuHghsP#gOxC7ur&ns6LgU22Xq&LUwwcDKKYZw&rcE{Bmxi; zGZ2;nib?>*M1G?GH*jw-jJ-Jw1hqp6qW78t2$(K*!D z(aFoL73*$y3b|!1duFK&ghg@P8j$HPL@!Xt0vJtYiJUjJ_>JzOv?;L_o`Iv=zQ%lQE zh~bV22wY;{*AeB!Qyk;*YR3s%)ZsWkDnGhxzb@(-vRP1j!N2P&%1Q1e z%r)Ec>3JUY+`R?cC#rWg8a~YEeN0>-yM4xhZC*oq-KPa+!`OPI-EqohO3U#MwY%X= z^^95C=dGmkGl4IsZS=hvI7enDieU|eB zVjxX(bVpZE_6gB;m{5~&jKiX$XYo{xxczK}RP&x_aO2e`aA z%SfOdT+e7t)W#xb!KJQ44*tp%AU|vl+vLRc-W!O;`s_I@{d|$j+QsVaSB~Im_wvQr zYOV58%$<+-R~$$AR-Qk<-I8nHuY%gxN=?MRtS;EvMlDusybtZo(Y@UPTbL2hG+eIC z@jS7$n#seuaZ$6#^!uC2)aVzwIa_@>E9><;4I8PnQ6(2Fsy)|NCpM1EF3dz|;v#nh zj3wG~&uu<2F0inByq?fD-wNy8Q4HIf#wDk*;v`eYm%Im(^{p6S4*-@7X4s0vBn$g2 zm42lYYvI%4zE^!9IL}Pa< zF&>;wFw=MTqE&9^&MR>O=Tv4Bom@TH+JtnfIAlSMRfCM9Yk`!(ccd5H^^qN2jrzj$ z*LRk;D^j*Bi|R8&sx_(snS#`op4S{vF7I5cjcn^eFp!orYZaomHu`dAd-~b3gZWfD z^sDTocD^SB%ZRVBUqe=2Ru%#m*C)hnJ50N>(QB)29XI_Od35eX@<$nM zxGtMw>sE^0gIZovZVrES#6lY*;NiIUL-J$W*=}Z7N6(d#77#zIB(OHMdOo!1+iGg< z(#-q*RWxh3Q|eug)wzvsL$3kKX4(iqQ}jxt=zwm@si3 z_JezM8T2Vc_nHiYUuy2 z6jv4E=F#bTn|r}b#4$?EYg zt$$SkrSnH2rS2pc=h09OG_$hu(`QsSDD%Cy?`9ePWPD5PrA@uXj^aZeE~sSh30`#s zMS{r*>nCgGQ_9)X>B0zdF&`6kTFP!@d))M7ax> z(j8nh6jK%-c}53vlPm#rzPmPkLVUOt?*rp{dQLK4m4X+oTY8jfJ|`)Iw68|$alF=7 z{Jt~S>r~jG(;kE0%?33}AICZyG2|-5Al3)G7^WH?akB@#5FSTI=;|c++TcjAW)f$U z5cC(O{DzEGp%u{S`wZek65QRgSx!Ylq;Y}+kIj(Nh9P2oweg4Cw5hG-d!9iapY@q$ ze@~4(vV3!%9mTjnI~>D7M|xw8(++jc)PO0OIFV*pp&8))N<;lNdH1zC(73>ZOLr`W z$c-P|n@#=#wfQjj{r(FcYtf=ZpX#AuFX88buo~w$8DuBJs(Nj&F69Vo*SGIq%2TKi z*Ad`lO8Ej?AFpwQfx!4{-nDjz=2h$%*_-cY1c=I0XC^A{92XYWQjWiB1Q}nn7T|Sh zWC`QQ7ptOd#S6YuN3 zbJ!F&bas+P6U{%QlRs*csb2T&Jx_WVb5zxU2#e%7`RDR$kcBqmy| zq=zNkxl9odjR)$f)uRYW|4J`ylr~82P$b!dY+-EMt7+EddqlaM3LLsY+7P0F(V!;F z<~XHjQbCk|x|B1Y>YG6vuVEv_V#xp0*HxCebcwLTyQPN5r$+5|F zi=)rnBgZEdnHwkP}$bZD;EUH z{VcRt@0P?qQp9nWYr8DM-hl%e*~hOl9OCJ}biw;b@;kHV>%4{m4nXsEa|a{(8Z#N8 z6Zej=={M=JAFoxFS4l!j%KDlfZO28MBkVz!Lvx}4hxsQmjc$iK$CY-k2+4 zo_V4htEN-r%J_V1v)X(+R?}TbtpE%VY5m~*VZcI?rjX-pDv0rul%zZE4JT@wt|Q%~ ziEU?SfswEP;^w{5a>PVL8b=5VKumIs=-)*{{3`rLdnLq!sNYzK2!*wM(#AIr7UzDi z75>ov`xkJp;76zy?vpRRc~H(jYKQ-29ATHfPoMD3gF^Q!hkrU|A4POviVS#H9+dKX zCGp>K2(`q0()gR4KPrp=mh|VmeEuS7ztZ@>N-6t`l>N%%zoigrkNY(D7b*X*68S^e z-c#^M z#E)>mw1?W$_WMTePyd_K|2r2vDCPHPNZ9o4QyxA9{=Nk~i2VIECPd49%E!Nhe~q1b VnxuF~5E0She>3 + + + + + + + vnd.sun.star.script:SuperbChemistry.Main.FormatSelectionOrDocument?language=Basic&location=application + + + SuperbChemistry: Format selection or document + + + com.sun.star.text.TextDocument + + + + + diff --git a/extension/META-INF/manifest.xml b/extension/META-INF/manifest.xml index 184035e..e414e74 100644 --- a/extension/META-INF/manifest.xml +++ b/extension/META-INF/manifest.xml @@ -1,4 +1,5 @@ - \ No newline at end of file + + diff --git a/extension/SuperbChemistry/Main.xba b/extension/SuperbChemistry/Main.xba index 530cf8d..960dc6c 100644 --- a/extension/SuperbChemistry/Main.xba +++ b/extension/SuperbChemistry/Main.xba @@ -1,147 +1,224 @@ -' SuperbChemistry version 2.2 -' http://mattmccutchen.net/schem/ -' Written and maintained by Matt McCutchen <matt@mattmccutchen.net> -' -' Applies superscript and subscript formatting to chemical formulas in -' OpenOffice.org Writer documents. -' -' Rules: -' - Quantities [0-9]+ and charges [0-9]*[-+−] are recognized after an element -' symbol [A-Z][a-z]? or a closing delimiter [\])}] . Hyphens are converted -' into real minus signs. -' - A charge sign [-+−] is ignored if it is followed by a letter, digit, -' opening delimiter, or [<>] . (Charges should appear only at the end of a -' formula, and we want to avoid matching ordinary hyphens in text.) -' - When digits followed by a charge sign are recognized, the last digit -' becomes part of the charge and the remaining digits become the quantity. -' (Charges rarely have absolute value more than 9.) -' - In cases like X2-, we have to guess whether the digit is an atom/group -' quantity or a charge amount. We guess atom/group quantity if X is H (NH4+), -' O (NO3-), a halogen (SbF6-, AlCl4-, etc.), or a closing parenthesis -' (Fe(OH)2+; the group likely would not have been parenthesized unless it had -' a quantity). Otherwise we guess charge amount (Fe3+). This heuristic -' should be right most of the time. -' -' Examples: -' C12345 ==> C_{12345} -' H+ ==> H^+ -' Cl- ==> Cl^- -' Fe3+ ==> Fe^{3+} -' SO42- ==> SO_4^{2-} -' C1232+ ==> C_{123}^{2+} -' N3- ==> N^{3-} -' N|_3^- not recognized (| represents "no-width no break") -' NH4+ ==> NH_4^+ -' NO3- ==> NO_3^- -' AlCl4- => AlCl_4^- -' Fe(OH)2+ ==> Fe(OH)_2^+ -' O12 ==> O_{12} -' y4- not recognized -' x2 not recognized -' Foo2 not recognized -' TI-89 not recognized -' -' To format the current document, run the FormatDocument macro: go to Tools -> -' Macros -> Run Macro... -> My Macros -> SuperbChemistry -> Main -> -' FormatDocument -> Run. I realize that this is ugly. I tried to make the -' package install a menu item to format the document, but the resulting package -' caused OpenOffice.org to crash regularly (I didn't investigate why), so I -' abandoned that idea. Note that you can add a menu item as a user -' customization (Tools -> Customize), and I recommend it if you plan to use -' SuperbChemistry frequently. -' -' FormatDocument uses a sequence of regular expression find-and-replace -' operations since that was easy to implement and makes the rules easy to -' change. The operations appear in the undo history, so you can undo a -' formatting run by undoing the block of "Replace" entries at the top of the -' history. -' -' I would like to support formatting a selection, but the OpenOffice.org API -' does not appear to support replace-all within a selection. I could find -' within the selection and implement the replacing myself, but that is more -' work than I want to do. -' -' If SuperbChemistry makes a mistake (e.g., recognizes a "formula" that isn't -' or formats a formula incorrectly), you can correct the formatting yourself -' and prevent future runs of the macro from recognizing the offending text by -' inserting a "No-width no break" character in the middle of it. This character -' is available in the "Insert -> Formatting Mark" menu when "Tools -> Options -> -' Language Settings -> Languages -> Enhanced language support -> -' Enabled for complex text layout (CTL)" is enabled. - -' ============================================================================== - -' Regular expression replace in the document, -' creating superscripts if superb > 0 or subscripts if superb < 0. -' Used by FormatDocument. -sub SuperbReplace(doc as object, searchStr as string, replaceStr as string, superb as integer) - -dim rd as object -rd = doc.createReplaceDescriptor() - -rd.SearchCaseSensitive = true -rd.SearchRegularExpression = true -rd.setSearchString(searchStr) -rd.setReplaceString(replaceStr) - -if superb <> 0 then - dim replaceAttrs(1) as new com.sun.star.beans.PropertyValue - replaceAttrs(0).Name = "CharEscapement" - if superb > 0 then - replaceAttrs(0).Value = 33 - else - replaceAttrs(0).Value = -9 - end if - replaceAttrs(1).Name = "CharEscapementHeight" - replaceAttrs(1).Value = 58 - rd.setReplaceAttributes(replaceAttrs) -end if - -doc.replaceAll(rd) - -end sub - -' Formats the current document -sub FormatDocument - -' Idiom: Match something and tag it on the left or right with @x@ -' for further processing. If the replacement text could use -' backreferences, this would be easier. (I think backreferences were added -' since I originally wrote this code, but I see no need to rewrite it to take -' advantage of them. - Matt 2008-10-26) - -' Tag candidate charges following symbols or ), but not in compound words, etc. -' Acceptable next character. (Has to be before end of line to avoid matching @g@ tag itself.) -SuperbReplace(ThisComponent, "([A-Z][a-z]?|[\])}])[0-9]*[-+−][^[({A-Za-z0-9<>]", "&@G@", 0) -' Retag in front. -SuperbReplace(ThisComponent, ".@G@", "@g@&", 0) -' End of line. -SuperbReplace(ThisComponent, "([A-Z][a-z]?|[\])}])[0-9]*[-+−]$", "&@g@", 0) - -' Some groups grab a single following digit as a quantity rather than a charge amount. -' See detailed rationale above. -SuperbReplace(ThisComponent, "(H|O|F|Cl|Br|I|\))[0-9]", "&@n@", 0) - -' Real minus signs in charges. -SuperbReplace(ThisComponent, "-@g@", "−@g@", 0) - -' Make charges: at most one digit. -SuperbReplace(ThisComponent, "[0-9]?[−+]@g@", "@q@&", 1) - -' Remove the O and ) markers in case of O57. -SuperbReplace(ThisComponent, "@n@", "", 0) - -' Tag quantities: as many digits as we can still grab. -SuperbReplace(ThisComponent, "([A-Z][a-z]?|[\])}])[0-9]+", "&@n@", 0) - -' Make quantities. -SuperbReplace(ThisComponent, "[0-9]+@n@", "&", -1) - -' Clean up all markers. -SuperbReplace(ThisComponent, "@[gGnq]@", "", 0) - -end sub +Option Explicit + +Function HaveSelection(doc As Object) + Dim sel as Object + sel = doc.CurrentController.Selection + If sel.Count > 1 Then + HaveSelection = True + Exit Function + End If + Dim s0 + s0 = sel.getByIndex(0) + If s0.Text.compareRegionStarts(s0.Start, s0.End) = 0 Then + HaveSelection = False + Else + HaveSelection = True + End If +End Function + +' Regular expression replace in the document, creating superscripts if +' superb > 0 or subscripts if superb < 0. +Sub SuperbReplace(doc As Object, searchRegex As String, replacePattern As String, superb As Integer) + + Dim rd As Object + rd = doc.createReplaceDescriptor() + + rd.SearchCaseSensitive = true + rd.SearchRegularExpression = true + rd.setSearchString(searchRegex) + rd.setReplaceString(replacePattern) + + If superb <> 0 Then + Dim replaceAttrs(1) As New com.sun.star.beans.PropertyValue + replaceAttrs(0).Name = "CharEscapement" + If superb > 0 Then + replaceAttrs(0).Value = 33 + Else + ' The default escapement for subscripts is -33, which looked bad to me in + ' chemical formulas. This looks better. Modify to your taste. + replaceAttrs(0).Value = -9 + End If + replaceAttrs(1).Name = "CharEscapementHeight" + replaceAttrs(1).Value = 58 + rd.setReplaceAttributes(replaceAttrs) + End If + + doc.replaceAll(rd) + +End Sub + +Sub ReplaceInSelection(doc As Object, searchRegex As String, replacePattern As String) + + Dim frame As Object, dispatcher As Object + frame = doc.CurrentController.Frame + dispatcher = createUnoService("com.sun.star.frame.DispatchHelper") + + Dim args(6) As New com.sun.star.beans.PropertyValue + args(0).Name = "SearchItem.AlgorithmType" + args(0).Value = 1 + args(1).Name = "SearchItem.SearchFlags" + args(1).Value = &H1800 ' Search in selection + args(2).Name = "SearchItem.SearchString" + args(2).Value = searchRegex + args(3).Name = "SearchItem.ReplaceString" + args(3).Value = replacePattern + args(4).Name = "SearchItem.Command" + args(4).Value = 3 + args(5).Name = "SearchItem.AlgorithmType2" + args(5).Value = 2 + args(6).Name = "Quiet" + args(6).Value = true + + dispatcher.executeDispatch(frame, ".uno:ExecuteSearch", "", 0, args()) + +End Sub + +Global formatSelectionWarningShown As Boolean +' I haven't found a way to initialize the variable, but it looks like its +' default value is treated as false in an if statement. +'formatSelectionWarningShown = False + +Sub FormatSelectionOrDocumentDebug() + ' Replacing with an empty replacement string triggers a bug in LibreOffice + ' (https://bugs.documentfoundation.org/show_bug.cgi?id=136577), so we must + ' avoid it. Fortunately, avoiding it is pretty straightforward. + + ' Insert @m@ between an item and a number or charge. + If HaveSelection(ThisComponent) Then + ' doc.replaceAll is not capable of searching a selection, while the + ' dispatch-based replace API uses the current format options in the + ' "Find & Replace" dialog and does not let us change them, which means that + ' (1) existing settings can break things and (2) we cannot make superscripts + ' and subscripts. We do the best we can by using one dispatch-based replace + ' to tag all the sequences we want to operate on (which loses the selection in + ' OpenOffice anyway) and then proceed with replaceAll calls on the whole + ' document, which will only operate on the already tagged sequences. + If Not formatSelectionWarningShown Then + MsgBox "Due to limitations in the OpenOffice/LibreOffice API, the " & _ + """Format selection"" command may not process some chemical formulas " & _ + "in the selection or may apply incorrect formatting if any format " & _ + "options are active in the ""Find & Replace"" dialog box. If this " & _ + "happens, just undo the command (if any changes were made), clear the " & _ + "format options in the ""Find & Replace"" dialog box (focus the " & _ + """Find"" field, click ""No Format"", and repeat for the ""Replace"" " & _ + "field), and run ""Format selection"" again." & Chr$(13) & Chr$(13) & _ + "This message is always shown on the first ""Format selection"" " & _ + "command in each OpenOffice/LibreOffice session because " & _ + "SuperbChemistry has no way to detect whether format options are " & _ + "active in ""Find & Replace"".", _ + 0, "SuperbChemistry ""Format selection"" notice" + formatSelectionWarningShown = True + End If + ReplaceInSelection(ThisComponent, "(?<=[A-Z][a-z]?|[\])}])[-+−0-9]+", "@m@&") + Else + SuperbReplace(ThisComponent, "(?<=[A-Z][a-z]?|[\])}])[-+−0-9]+", "@m@&", 0) + End If + + ' Insert @c@ after a charge. + SuperbReplace(ThisComponent, "(?<=@m@)([0-9]*[-+−])(?=[ \t\])}.,:;?!'""]|$)", "&@c@", 0) + + ' Real minus signs in charges. + SuperbReplace(ThisComponent, "-@c@", "−@c@", 0) + + ' Some groups grab a single following digit as a quantity rather than a charge amount. + ' Insert @sq@ marker to prevent the charge from grabbing the digit. + SuperbReplace(ThisComponent, "(?<=(H|O|F|Cl|Br|I|[\])}])@m@)[0-9]", "&@sq@", 0) + + ' Each charge grabs at most one digit and moves the @c@ in front to prevent the + ' quantity from grabbing the digit. + SuperbReplace(ThisComponent, "([0-9]?[−+])@c@", "@c@$1", 1) + + ' Remove any @sq@ markers so items can grab all the digits that follow for the quantity. + SuperbReplace(ThisComponent, "(.)@sq@", "$1", 0) + + ' At this point, we have only @m@ and @c@ markers left. + + ' Format quantities: as many digits as we can still grab. + ' We have to allow @ as a following character for our own @c@ tag. + SuperbReplace(ThisComponent, "(?<=@m@)[0-9]+(?=[@A-Z \t\])}.,:;?!'""]|$)", "&", -1) + + ' Clean up @c@ markers. We know there is a charge sign after each. + SuperbReplace(ThisComponent, "@c@(.)", "$1", 0) + + ' Clean up @m@ markers. We know there is some character before each. + SuperbReplace(ThisComponent, "(.)@m@", "$1", 0) + +End Sub + +Dim madeChanges As Boolean + +Sub UndoListener_undoActionAdded() +End Sub +Sub UndoListener_actionUndone() +End Sub +Sub UndoListener_actionRedone() +End Sub +Sub UndoListener_allActionsCleared() +End Sub +Sub UndoListener_redoActionsCleared() +End Sub +Sub UndoListener_resetAll() +End Sub +Sub UndoListener_enteredContext() +End Sub +Sub UndoListener_enteredHiddenContext() +End Sub +Sub UndoListener_leftContext() + madeChanges = True +End Sub +Sub UndoListener_leftHiddenContext() +End Sub +Sub UndoListener_cancelledContext() +End Sub + +Sub FormatSelectionOrDocument() + + Dim undoActionName As String + If HaveSelection(ThisComponent) Then + undoActionName = "SuperbChemistry: Format selection" + Else + undoActionName = "SuperbChemistry: Format document" + End If + ThisComponent.UndoManager.enterUndoContext(undoActionName) + + On Error Goto ErrorHandler + FormatSelectionOrDocumentDebug + On Error Goto 0 + + ThisComponent.UndoManager.leaveUndoContext() + Exit Sub + +ErrorHandler: + + ' If our undo context is nonempty, we want to undo the generated action. + ' If not, we do not want to undo as that would undo the user's previous + ' action. If we just check whether the title of the last undoable action + ' is "SuperbChemistry: Format (selection|document)", that might be wrong if + ' the user ran FormatDocument twice in a row: probably unlikely, but the + ' completely correct check is not that hard. + Dim listener As Object + listener = CreateUnoListener("UndoListener_", "com.sun.star.document.XUndoManagerListener") + madeChanges = False + ThisComponent.UndoManager.addUndoManagerListener(listener) + ThisComponent.UndoManager.leaveUndoContext() + ThisComponent.UndoManager.removeUndoManagerListener(listener) + If madeChanges Then + ThisComponent.UndoManager.undo() + ThisComponent.UndoManager.clearRedo() + End If + + MsgBox "SuperbChemistry encountered an unexpected error:" & Chr$(13) & Chr$(13) & _ + "Code " & Err & ": " & Error$ & Chr$(13) & Chr$(13) & _ + "Any changes made so far have been undone." & Chr$(13) & Chr$(13) & _ + "SuperbChemistry needed to catch the error in order to leave your undo " & _ + "history in a consistent state. If the problem is reproducible and you " & _ + "want to see the precise error location, run the FormatDocumentOrSelectionDebug " & _ + "macro, but be advised that it may generate multiple entries in the undo " & _ + "history and will not undo them on error.", _ + 0, "SuperbChemistry internal error" + On Error Goto 0 +End Sub \ No newline at end of file diff --git a/extension/SuperbChemistry/dialog.xlb b/extension/SuperbChemistry/dialog.xlb index 8d924f4..7609d23 100644 --- a/extension/SuperbChemistry/dialog.xlb +++ b/extension/SuperbChemistry/dialog.xlb @@ -1,3 +1,3 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/extension/SuperbChemistry/script.xlb b/extension/SuperbChemistry/script.xlb index 815dca2..c3d89f3 100644 --- a/extension/SuperbChemistry/script.xlb +++ b/extension/SuperbChemistry/script.xlb @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/extension/description.xml b/extension/description.xml new file mode 100644 index 0000000..7ca5367 --- /dev/null +++ b/extension/description.xml @@ -0,0 +1,16 @@ + + + + + + Matt McCutchen + + + SuperbChemistry + + + + + diff --git a/extension/extension-description.txt b/extension/extension-description.txt new file mode 100644 index 0000000..c9b4c2e --- /dev/null +++ b/extension/extension-description.txt @@ -0,0 +1 @@ +Applies superscript and subscript formatting to chemical formulas in bulk in Writer documents. \ No newline at end of file -- 2.34.1 From db7363ac0b14d0326243f956db60bae8d0527db6 Mon Sep 17 00:00:00 2001 From: Matt McCutchen Date: Sat, 15 Oct 2022 14:25:06 -0400 Subject: [PATCH 2/4] Draft fix and test case for formulas like Mg3(PO4)2. Also improve some comments to hopefully make it easier for future developers to understand what the code is supposed to be doing. There's no update to the "Formatting rules" in the readme because this brings the code into better alignment with what that section already said. --- SuperbChemistry-test.odt | Bin 9223 -> 11763 bytes extension/SuperbChemistry/Main.xba | 35 ++++++++++++++++++----------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/SuperbChemistry-test.odt b/SuperbChemistry-test.odt index 33f2e1c65ece9047516c3fc361e54ff27de5629d..60323fe3a425111018f47d2dbcc4732f2b8b50fd 100644 GIT binary patch literal 11763 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 NHV^X;i0<+1{{T}fBBTHS delta 7665 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 diff --git a/extension/SuperbChemistry/Main.xba b/extension/SuperbChemistry/Main.xba index 960dc6c..1c21b7c 100644 --- a/extension/SuperbChemistry/Main.xba +++ b/extension/SuperbChemistry/Main.xba @@ -85,7 +85,8 @@ Sub FormatSelectionOrDocumentDebug() ' (https://bugs.documentfoundation.org/show_bug.cgi?id=136577), so we must ' avoid it. Fortunately, avoiding it is pretty straightforward. - ' Insert @m@ between an item and a number or charge. + ' Step 1: Insert @m@ between an item and a number or charge that may be part of + ' a chemical formula (subject to later checks). If HaveSelection(ThisComponent) Then ' doc.replaceAll is not capable of searching a selection, while the ' dispatch-based replace API uses the current format options in the @@ -116,33 +117,41 @@ Sub FormatSelectionOrDocumentDebug() SuperbReplace(ThisComponent, "(?<=[A-Z][a-z]?|[\])}])[-+−0-9]+", "@m@&", 0) End If - ' Insert @c@ after a charge. + ' Step 2: Insert @c@ after a charge symbol, if it's followed by one of the + ' allowed characters for the second kind of "recognized sequence" described in + ' the readme. SuperbReplace(ThisComponent, "(?<=@m@)([0-9]*[-+−])(?=[ \t\])}.,:;?!'""]|$)", "&@c@", 0) - ' Real minus signs in charges. + ' Step 3: Real minus signs in charges. SuperbReplace(ThisComponent, "-@c@", "−@c@", 0) - ' Some groups grab a single following digit as a quantity rather than a charge amount. - ' Insert @sq@ marker to prevent the charge from grabbing the digit. + ' Step 4: Some groups grab a single following digit as a quantity rather than a + ' charge amount. Insert @sq@ marker to prevent the charge from grabbing the + ' digit. SuperbReplace(ThisComponent, "(?<=(H|O|F|Cl|Br|I|[\])}])@m@)[0-9]", "&@sq@", 0) - ' Each charge grabs at most one digit and moves the @c@ in front to prevent the - ' quantity from grabbing the digit. + ' Step 5: Each charge grabs at most one digit and moves the @c@ in front to + ' prevent the quantity from grabbing the digit. SuperbReplace(ThisComponent, "([0-9]?[−+])@c@", "@c@$1", 1) - ' Remove any @sq@ markers so items can grab all the digits that follow for the quantity. + ' Step 6: Remove any @sq@ markers so items can grab all the digits that follow + ' for the quantity. SuperbReplace(ThisComponent, "(.)@sq@", "$1", 0) ' At this point, we have only @m@ and @c@ markers left. - ' Format quantities: as many digits as we can still grab. - ' We have to allow @ as a following character for our own @c@ tag. - SuperbReplace(ThisComponent, "(?<=@m@)[0-9]+(?=[@A-Z \t\])}.,:;?!'""]|$)", "&", -1) + ' Step 7: Format quantities: as many digits as we can still grab. The digits + ' must be followed by one of the allowed characters for the first kind of + ' "recognized sequence" described by a readme or by @, which we assume is part + ' of a @c@ tag we added in step 2. The allowed characters A-Z\[({ represent + ' the beginning of another item; the other allowed characters are the same as + ' in step 2. + SuperbReplace(ThisComponent, "(?<=@m@)[0-9]+(?=[@A-Z\[({ \t\])}.,:;?!'""]|$)", "&", -1) - ' Clean up @c@ markers. We know there is a charge sign after each. + ' Step 8: Clean up @c@ markers. We know there is a charge sign after each. SuperbReplace(ThisComponent, "@c@(.)", "$1", 0) - ' Clean up @m@ markers. We know there is some character before each. + ' Step 9: Clean up @m@ markers. We know there is some character before each. SuperbReplace(ThisComponent, "(.)@m@", "$1", 0) End Sub -- 2.34.1 From 05bf78df7ebedd204de0bf2c2ace03a61c74932e Mon Sep 17 00:00:00 2001 From: Matt McCutchen Date: Sat, 15 Oct 2022 14:25:20 -0400 Subject: [PATCH 3/4] Fix a mistake in an error message. --- extension/SuperbChemistry/Main.xba | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/SuperbChemistry/Main.xba b/extension/SuperbChemistry/Main.xba index 1c21b7c..52080f3 100644 --- a/extension/SuperbChemistry/Main.xba +++ b/extension/SuperbChemistry/Main.xba @@ -223,7 +223,7 @@ ErrorHandler: "Any changes made so far have been undone." & Chr$(13) & Chr$(13) & _ "SuperbChemistry needed to catch the error in order to leave your undo " & _ "history in a consistent state. If the problem is reproducible and you " & _ - "want to see the precise error location, run the FormatDocumentOrSelectionDebug " & _ + "want to see the precise error location, run the FormatSelectionOrDocumentDebug " & _ "macro, but be advised that it may generate multiple entries in the undo " & _ "history and will not undo them on error.", _ 0, "SuperbChemistry internal error" -- 2.34.1 From a9a505b5229c217667f597eff3ee3dcf8297545c Mon Sep 17 00:00:00 2001 From: Matt McCutchen Date: Sat, 15 Oct 2022 14:25:41 -0400 Subject: [PATCH 4/4] SuperbChemistry 3.1 --- extension/description.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/description.xml b/extension/description.xml index 7ca5367..fe016ae 100644 --- a/extension/description.xml +++ b/extension/description.xml @@ -3,7 +3,7 @@ xmlns="http://openoffice.org/extensions/description/2006" xmlns:xlink="http://www.w3.org/1999/xlink"> - + Matt McCutchen -- 2.34.1