Typing
Abbreviations
A short run of characters that turns into a longer one as you type it, wherever you are typing. The third way of setting something off, beside a copy and a chord: the keyboard itself.
An abbreviation is a short run of characters that turns into a longer one as it is typed. Type !email and it becomes test@example.com, wherever the typing is happening — a mail window, a terminal, somebody else's app.
Each one is two things: what is typed and what it expands to. The expansion is either
- text, typed as it is written; or
- a pipeline, run when the abbreviation fires, with what it produces typed in its place.
Which of the two it is, is chosen rather than guessed at: an expansion meant literally may perfectly well hold a $ or a |, and running one nobody asked to have run is a worse mistake than typing one out.
Pipeline expansions
A pipeline expansion is the same language the search bar speaks and reaches the same things:
$signature a variable, on its own
$1 | trim the newest copy, tidied
$1 | jira-link the newest copy, turned into a link
"Dear $name," ++ $1 text written out, with pieces filled in
| now | strftime %F today's date
Unlike a pipeline action, one here needs no filters to be worth running: what it reads has somewhere to go, since it gets typed, so $signature alone is a whole abbreviation.
A > $name inside one is honoured — and since a redirect sends the result somewhere instead of to the clipboard, such an abbreviation stores its result and types nothing. A forget means what it always means: the entry the pipeline read from is dropped.
How one fires
The app keeps the run of characters typed since the last thing that broke the run, and when the end of that run is an abbreviation, it fires.
Nothing about this is a word boundary. !email fires at the end of write to me at !email, which is what makes a leading ! worth having — an abbreviation of letters alone fires inside ordinary words too, and the pane says so where one is written.
The run ends, and starts over, on anything that moves the caret somewhere the app has not been watching: ↩, ⇥, ⎋, an arrow key, ⌦, a jump around the document, a click, a chord with ⌘ or ⌃ on it, or another app coming to the front.
⌫ takes a character back off the run rather than ending it, so a typo can be corrected and the abbreviation still fires; ⌫ past the start of the run leaves it empty, since what is to the left of where the run began was never seen. ⌥ does not break a run — on many layouts it is how a character is typed. At most 64 characters are remembered, or as many as the longest abbreviation defined.
Where two abbreviations both finish on the same keystroke, the longest wins: with !em and !email both defined, typing the second means the second, since the first would have fired three keystrokes ago if it were what was wanted. Between two of the same length the topmost wins, and the pane flags the duplicate — the lower of the two can never fire.
How the expansion arrives
The characters you typed are taken back out with as many ⌫s, and the expansion is typed in their place — as the characters themselves rather than as the keys that would produce them, so an em dash arrives on a keyboard that has no way to type one.
It types rather than pastes on purpose: nothing goes on the clipboard, so what you had copied is still there afterwards, no history entry is made, and no trigger fires on an expansion.
The order matters too. The expansion is worked out before anything is deleted, so a pipeline that fails leaves the keyword sitting on screen rather than a hole where it used to be; what it had to say goes to the HUD.
Typing is slower than pasting for long text, at about a millisecond and a half per key press, and an expansion carries no formatting, since it is characters.
What it costs
Watching what is typed means a listen-only event tap, which reads the key stream without touching it: nothing typed is delayed, swallowed or rewritten on the way to the app it was meant for. It needs the same Accessibility permission automatic pasting needs, and the pane says so, with a link, while there is something to watch for and no permission to watch with.
The tap only exists while there is something to watch for. With no abbreviations defined — which is every install until somebody writes one — there is no tap and the app reads no keystrokes at all. Disabling the last one takes it back down.
Two things are never read:
- anything typed while a password field has secure input on, which macOS suppresses event taps for entirely;
- anything typed while an excluded app is in front — an app PasteDaemon has been told to keep out of is one it keeps out of.
The app's own synthesised keys are marked as its own and ignored, so an expansion holding !email cannot set that abbreviation off again.
When abbreviations stop for no reason
Secure input is the one thing that stops abbreviations without leaving anything to see. Any app can switch it on — it is how a password field keeps what you type out of every other app on the Mac — and some apps forget to switch it back off, at which point nothing expands anywhere and PasteDaemon looks perfectly well.
So the app watches for it. When secure input has been on for a while, held by an app that is not the one you are typing into, the Abbreviations pane names it. There is nothing to press: only the app holding it can let go, and quitting it is the surest way. The warning clears itself as soon as it does.
Nothing is said while the app holding it is the one in front, because then nothing is wrong — that is a password field doing its job, and you did not want an abbreviation expanding into it.
The Abbreviations pane
A table of what is typed and what it turns into, with a panel below for the selected one — an expansion can run to a paragraph or to a pipeline, neither of which fits in a row one line high, so the row shows the expansion and the panel edits it.
Each row has a switch, the characters typed, a Kind of Text or Pipeline, and what it expands to. Adding one starts it at !abbr1, which nothing else answers to and which carries the leading ! the pane recommends. The count at the bottom says how many are actually watched for when that is fewer than there are rows — a row with no expansion yet is not one.
The panel says three things beyond the field:
- For a pipeline, what it will do in words — where it reads from, whether it ends in a redirect and so types nothing, and any step naming a filter that does not exist. Run It Now runs it against the clipboard as it stands and shows what would be typed, without typing it anywhere and without storing anything a
> $namewould store. - For text with line breaks in it, that they are typed as
↩, which some fields read as "send". - For an abbreviation of letters and digits alone, that it will fire inside ordinary words as well as on its own.
When something is standing between your abbreviations and the keyboard, a warning at the top of the pane says what. A missing Accessibility permission comes with a link to the setting and a Recheck button that claims the keyboard as soon as it has been granted; a keyboard macOS refused comes with Try Again; and an app holding secure input is named.