mailwrap icon indicating copy to clipboard operation
mailwrap copied to clipboard

Text filling and wrapping plugin for Apple Mail

MailWrap

MailWrap is a plugin for Apple Mail on macOS 10.12 and later, making it easier to compose well-formed and correctly wrapped plain text messages. It introduces line wrapping and paragraph filling operations, and extends the built-in indentation functions to work in plain text mode.

Mail was once quite a good 'net citizen. It could be configured to generate plain text email, and would do so with the text neatly wrapped at 76 columns. In addition, it used the RFC2646 format=flowed extension to indicate that these wrapped paragraphs could be reflowed. Thus mailing list archives and traditional mail clients saw readable 80-column text without noisy encoding, but more sophisticated readers could re-fill the paragraphs to suit the display width.

Unfortunately, Mail is poorly maintained and has declined in quality over recent years, with many bugs introduced and incompetent design choices made. Current releases have ditched format=flowed for plain text parts, instead opting to emit each paragraph as a single long line. If this is longer than 77 characters, the text is mangled with a quoted-printable transfer encoding. The resulting email leaves a very visible mess in mailing list archives on the web, as well as drawing understandable ire from recipients with traditional unix mail clients.

Fortunately, even if Mac users find themselves drawn to Apple Mail for its convenient reading interface and good platform integration, all is not lost. If lines are no longer than 77 characters, Mail won't use quoted-printable in your outgoing messages, resulting in perfectly acceptable plain text email that won't embarrass you in public. Manual paragraph filling is tedious and time-consuming, but the MailWrap plugin can help automate the job.

An alternative, more automatic solution to the same problem is implemented in the MailFlow plugin, which reimplements format=flowed in Mail and eliminates quoted-printable plain text email under most circumstances. It is available from

https://github.com/arachsys/mailflow https://bitbucket.org/arachsys/mailflow

alongside this plugin. The two plugins can now happily coexist.

Installation

MailWrap is currently compatible with Apple Mail 10.0 and later. To install, clone the git repository or unpack the source tar.gz file, change to the source directory and run 'python install.py' or 'python3 install.py'.

The installer and plugin work with both the system Python 2.7 and more recent Python 3.x, but py2app and pyobjc are required. The installer will prompt you to install these with pip/pip3 if they can't be found.

Plugin bundles contain a list of UUIDs identifying versions of Mail with which they are compatible. The install.py script extracts the correct UUID from the installed version of Mail, generates a MailWrap.bundle to match, and installs it in ~/Library/Mail/Bundles/. You will need to quit and relaunch Mail for the plugin to be registered.

On macOS 10.14 and later, the plugin must be explicitly enabled in Mail Preferences. Choose 'Manage Plug-ins...' from the General tab, tick MailWrap.mailbundle, then choose 'Apply and Restart Mail'.

On macOS 11.0 and later, the plugin also needs to be ad-hoc signed and authorised before it will work. To do this, run

codesign -f -s - ~/Library/Mail/Bundles/MailWrap.mailbundle spctl --add --label MailWrap ~/Library/Mail/Bundles/MailWrap.mailbundle spctl --enable --label MailWrap

after running the install.py script. Many thanks to A. Wilcox (awilfox) for providing these signing instructions on their Cat Fox Life blog.

If the sandbox doesn't allow Mail.app to read the directory where your Python is installed, you may need to change 'semi_standalone' to False in the setup options in install.py. This issue doesn't affect /usr/bin/python or /usr/bin/python3, but has been reported with Homebrew Python running from /opt/homebrew/bin/python3 and /opt/homebrew/Cellar/[email protected]/.

If you use the system Python 2.7 on macOS 11.0 or later, you will need to set SYSTEM_VERSION_COMPAT=0 in the environment when running install.py:

SYSTEM_VERSION_COMPAT=0 /usr/bin/python install.py

Without this, a horrible Apple hack will cause the installer to detect the OS version incorrectly as 10.16. It aborts with a warning if that happens.

Sometimes when Mail is updated, its compatibility UUID changes. Mail will then disable plugins, moving them from 'Bundles/' to 'Bundles (Disabled)/'. The user is notified when this happens, and it is sufficient to simply run the install.py script again. The old disabled bundle will be cleared away, and a new one built and installed to match the new version of Mail.

Features

Two new items are appended to the Edit menu, 'Fill Text' and 'Wrap Text', with key-bindings Command-\ and Option-Command-.

Fill Text reformats the current paragraph block to the configured width, treating a block of lines separated by spaces or a change in quote level as a single paragraph. If a block of text is selected, all paragraphs which overlap the selection are filled.

This operation preserves quote level, allowing for the quote characters in the fill width, and if the first line is indented by one or more spaces, subsequent lines will be indented with spaces to the same column. Exactly one blank line is left between the formatted paragraph and the next.

Similarly, Wrap Text wraps the current line to the configured width, retaining any leading indent and the quote level on any continuation lines. If a block of text is selected, all lines which overlap the selection are wrapped.

MailWrap also fixes the built-in Increase/Decrease Indentation operations to work on plain text messages. These will insert or remove a configurable number of spaces at the start of the current line or all lines overlapping the current selection.

Finally, by default MailWrap will trim the excessively verbose attribution line Mail inserts when composing a reply, i.e.

On 8 Apr 2014, at 10:08:34, Chris Webb [email protected] wrote:

will become just

Chris Webb [email protected] wrote:

Since version 8.0, Mail has a bug which causes the attribution line to be quoted as if it were part of the original message. MailFlow will also fix this whilst trimming the attribution line.

This behaviour can be disabled in the preferences.

Configuration

MailWrap reads a handful of preferences from the com.apple.mail domain. These can be set at the command line with the macOS defaults command:

defaults write com.apple.mail MailWrap -dict-add BulletLists -bool false defaults write com.apple.mail MailWrap -dict-add BulletLists -bool true - configure MailWrap to interpret '- ', '+ ' or '* ' at the start of a paragraph as introducing a bullet item and so indent subsequent lines appropriately. The default is on.

defaults write com.apple.mail MailWrap -dict-add FixAttribution -bool false defaults write com.apple.mail MailWrap -dict-add FixAttribution -bool true - configure MailWrap to strip the verbose date and time information from the attribution line when composing a reply. The default is on.

defaults write com.apple.mail MailWrap -dict-add IndentWidth -int NN - configure MailWrap to increase/decrease plain text indentation by NN spaces when Command-] and Command-[ are used. The default is 2.

defaults write com.apple.mail MailWrap -dict-add WrapWidth -int NN - wrap lines and fill paragraphs at a width of NN characters. The default is 76, which is short enough that Mail won't inflict a quoted-printable transfer encoding on the message text.

pbmbox

For command-line users, the MailWrap distribution also includes a small utility, pbmbox. When messages are selected and copied in Mail, they are added to the clipboard as RFC822MessageDatasPboardType objects. pbmbox decodes these objects and emits the messages in unix mbox format on stdout.

To install, copy it to a directory in your PATH and make it executable:

sudo install -m 0755 pbmbox.py /usr/local/bin/pbmbox

A typical use is importing a patch series from email into a git repository. Select the messages in Mail, copy them with Command-C, and then run

pbmbox | git am

within the repository.

By default, the mboxrd format is used: lines beginning with />*From / are quoted with one additional leading '>'. This encoding avoids corruption of messages and is always reversible. If the -n or --no-quote-from option is given, pbmbox will not attempt to quote 'From ' lines. This is sometimes useful for simple command-line handling of a single message, where no ambiguity can result from an unquoted 'From '.

Implementation notes

Apple haven't done everything wrong in Mail. Writing this plugin would be near-impossible had their mail client not been furnished with a plugin interface, albeit an undocumented one. The reading interface is nice, with good platform integration, fast full-text search and cross-mailbox threading.

Curious readers of the code may be surprised by the long-winded approach taken in manipulating the message text. The editor is a WebKit editable view, for both rich text (HTML) and plain text messages, and it is certainly possible (and more efficient) to directly manipulate and modify the underlying DOM tree. However, MailWrap deliberately takes another approach, sticking to the simple NSResponder interface which offers simple operations such as moveToBeginningOfParagraph:, moveToEndOfParagraphAndModifySelection: and insertText:.

This is not due to a perverse desire to use the most awkward interface or write the least efficient wrapping code possible! When manipulating the message in simple ways that would not possible through the UI, it turns out to be very easy to get Mail into a confused state, where for instance the message looks fine in the editor but has unintended blank lines when rendered to plain text upon sending. The BLOCKQUOTE elements used to implement quoting are particularly fussy in this regard.

Even using the NSResponder interfaces, it is necessary to insert a temporary padding space at the beginning of a paragraph before selecting and replacing the rest of the text to avoid problems with lost quote level on the second and subsequent lines of the reformatted paragraph. (Of course, this means that this is a bug that can be triggered directly from normal user input at the keyboard; I have reproduced and reported it to Apple in this form.)

An additional benefit of sticking to basic interfaces is that they are less likely to change, disappear or develop significant bugs in future updates of Mail.

Copying

This software was written by Chris Webb [email protected] and is distributed as Free Software under the terms of the MIT license in COPYING.