From 2eec932f44a3bdc4d2f331a5e48c15dc8632bb31 Mon Sep 17 00:00:00 2001 From: Matt McCutchen Date: Thu, 10 Sep 2020 13:39:47 -0400 Subject: [PATCH] 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 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 b>@VJ1Nf!PlUk(5~xcS)K_@CF`t^9ug?LdZ@ literal 8247 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 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