Keyboard Maestro 8.0.4 “Super Swap” Macro


#1

Keyboard Maestro 8.0.4 “Super Swap” Macro

Name: Super Swap
Author: Mike Pasini
Version: 1.0a
Last Update: 7 December 2017

INTRO

Keyboard Maestro comes with a Swap Characters macro in the Macro Library which does the job for those of us who regularly transpose characters.

I have elevated that defect to a higher level by transposing words and even phrases. So I wrote some macros to swap those as well.

But I started to get trigger happy with multiple swapping macros. So I consolidated them into one macro with the same trigger whose behavior changes based on the selection.

I no longer have to remember how to invoke the correct routine. The macro itself knows what to do.

USAGE

This macro swaps characters if no selection is made, words if there are just two words selected, outer words if three are selected and whole phrases separated by an “and” or an “or.” You can also use a double caret to delimit two elements to swap.

If the macro can’t handle your selection, it leaves it alone.

Each case is illustrated below with an example in which the | indicates cursor position when the whole original is not selected. The swapped line in each example was actually created by the macro.

  1. To swap characters, put the cursor before the first of the two characters to swap or just select both characters.

     t|eh
     the
    
     teh
     the
    
  2. To swap words, just select the two words and the space between them.

     John said
     said John
    
  3. To swap outer words (like “second before first” or “last came first”), just select the three words and the spaces between them.

     worse is better
     better is worse
    
  4. To swap phrases (like “the usual second award or the grand prize”), just select the whole phrase. The “and” or “or” will stay put and the outer phrases will be swapped.

     the first time I saw Rome and my first trip to Europe
     my first trip to Europe and the first time I saw Rome
    
     a $100 mail-in rebate or a free set of ink cartridges
     a free set of ink cartridges or a $100 mail-in rebate
    
  5. To swap elements of a series, select the two items including their punctuation. Elements can be on their own line, as well.

     two, one, three
     one, two, three
     
     one
     two
     
     two
     one
    
  6. To swap anything at all, use two carets (^^) as a fulcrum in your selection. Existing whitespace at the ends of each piece in the selection will be ignored with a space inserted between the two pieces with the caret deleted. A return will be preserved.

     and so on 'abc'^^ but about the 'def'
     but about the 'def' and so on 'abc'
    

RECOVERY

If the result isn’t what you expect, you can restore the original with a Command-Z to undo the swap. That’s when you might resort to the double caret trick or a simple drag-and-drop.

DETECTING A SELECTION

We use a simple, but fast, method of detecting a selection. It works in the applications we use, which include a variety of text editors and InDesign.

If it doesn’t work for you, there is a more sophisticated way to check for a selection. You can use this method ([CB] How to Detect When Clipboard Has Changed [Example]) based on the clipboardseed function (https://wiki.keyboardmaestro.com/function/CLIPBOARDSEED), for example.

WHY PERL?

You can, and we did (initially), write this macro using Keyboard Maestro actions instead of a shell script.

But Keyboard Maestro’s visual If-Then-Else constructs, helpful in simple cases, quickly became unwieldy. As we added options, a simple Perl script that looked for specific clues in the selection to apply simple regexes to it made it easier to test and expand the macro.

Testing was done with a Perl script in BBEdit that threw a number of test strings at the Perl code, which also identified which branch made the substitution, which was edifying, to say the least.

This is simple enough that other languages could easily have handled the task too. We just admire the economy of working with regexes in Perl.

NOTES

Nothing tricky going on here except the evaluation of the selection.

The embedded Perl script handles all four possible selections. Otherwise (no selection) we revert to what Swap Characters does except this version skips an action by requiring you to put the cursor after the last correct character rather than between the transposed ones. You can just use the original Swap Characters actions if you prefer.

One might quibble about using \s+ for whitespace but it fits the bill for this task. Except for Case 5 above, the whitespace is not preserved but replaced by (one might say “tidied up with”) a single space. In Case 5, it is preserved to handle elements on individual lines.

Case 5 is pretty forgiving about trailing punctuation and space. In the example above, you can either include the space after “one,” or not and it will be correctly formatted. You don’t have to remember to be symmetrical in your selection.

The simpler swaps don’t pay any attention to what whitespace the original used on the theory that, in a phrase, spaces delimit the words.

If Case 6 doesn’t work for you, consider the drag-and-drop approach of selecting one element and dragging it by the other to complete the swap. Because the initial selection is often deselected when the cursor is repositioned and the mouse clicked to begin the drag, I added Case 6 to provide an alternative.

ACKNOWLEDGEMENT

The initial public release of this macro comes during the 2017 holiday season as small thank you to the members of this forum whose work has illuminated my own and made life easier.

Super Swap.kmmacros (13 KB)


Best Macro List
#2

Thanks for sharing. This looks like a very useful macro! :+1:

Why don’t you add it (and any other great macros you have) to the Best Macro List.

Excellent point. I also use scripts instead of KM IF/THEN or SWITCH actions for complicated workflows.

However, I have to say that the KM Switch or Case action (KM Wiki) is very powerful and easy to use, and to add new cases to in the future.

Sounds interesting. Would mind describing your method for us non-Perl users? I might like to use it in AppleScript and JXA scripts.

Thanks again.


#3

Thanks for sharing this macro. However, on my computer (running Sierra) Case 1 works only if I select the two characters. If I just place the cursor before the two characters, the last copied text on the clipboard is pasted between the two characters that are meant to be swapped. What might be causing this anomaly?


#4

That branch of the macro simply types Command-V to Paste the clipboard. But before it does that, it types Command-C to Copy the clipboard – just like the Swap Characters macro in the Keyboard Maestro library. And what it is copying is the first character after the cursor, which it selects by typing Shift-Right Arrow.

It sounds like Command-C is not copying anything. You might want to check that you have Shift-Right Arrow in the first action (and not just Right Arrow).

Or just reinstall the macro.


#5

Done. I wasn’t aware we could list our own macros there :slight_smile: – thought it was something the moderators do.


#6

Yes, I agree that’s a welcomed addition. I didn’t use it in the Keyboard Maestro version of the macro that I wrote but I might have.

As I added new cases to this macro, I restructured the logic quite a bit, handling rogues before more common cases. That’s a lot easier to do when 1) you can see everything at once and 2) you have a prototype to debug in a text editor.


#7

The simply method of detecting a selection I mentioned is, as you can see in the macro, to look at the state of the Copy command in the Edit menu. But I referenced your more elaborate technique of checking for a change in the clipboard itself for those who find that simple check doesn’t work in their application.

But you mentioned Perl, so I suspect you’re referring to something else.

The Perl code just looks at a Keyboard Maestro variable populated by what’s in the clipboard. It looks at it a lot of different ways, starting with the rare double caret then trailing punctuation, bracketed elements, multiple words, truncated words and then and/or, outer words, paired words before finally two characters. In a nutshell.


#8

Command-C was missing, and so I added it to the macro. But the macro still wasn’t working, and so I added pauses after the Command-C and Command-X actions. The macro now works, but is slower than when one selects the two characters to be swapped. The need to add pauses may be peculiar to my computer, since I have to do that with any macro that involves copying. But thanks again for a very useful macro.


#9

What application are you running that requires a pause for the Copy and Cut commands? (I’m guessing this has more to do with the application than your hardware.)


#10

It seems to happen in all applications—Scrivener, Mellel, Mail, Safari, Typinator, etc. I got a new iMac about six months ago, but I had the same problem with the previous iMac too.


#11

You might try this to see if it makes any difference. (Just type the lines 2 and 3 into Terminal’s prompt, one at a time.)

That really shouldn’t be happen system-wide.


#12

I tried your suggestion, but unfortunately it doesn’t seem to have had any effect. But thanks all the same.


#13

Super Swap 1.0b Update

This version adds two more ‘pivots’ around which you can swap: a single comma or a single semicolon (semicolon takes precedence over comma). From the Read Me in the accompanying archive:

7) If there's a single comma or semicolon in the selection, the phrases on either side will be swapped. Semicolons take precedence over commas, as in real life:

		on second thought, on first thought
		on first thought, on second thought

		the second, third and fourth thought; a premonition
		a premonition; the second, third and fourth thought

Super Swap 1.0b.zip (6.8 KB)


#14

Thanks for this, @mrpasini! I’ve been using your original macro for a while now, and this update should make it even better, since swapping clauses around a comma is one of my most common uses for it.


#15

Thanks, @gglick. I really had to scratch my head wondering why I didn’t think of it for the original release. Quite an omission for such a common situation!


#16

Thanks for his great macro. Can it be that your latest enhancement conflicts with item #5, To swap elements of a series, select the two items including their punctuation. Elements can be on their own line, as well.

I get:

three one, two,
two, one, three

two one

one
two


#17

The new code does have a bone to pick with some of the old code (I’ll explain below). But in BBEdit I can’t duplicate your issue.

So I’m guessing your results have to do with your initial selection. In a series, you would select the item plus its punctuation, so both commas would be selected in “three one, two,” and they yield (in BBEdit) the expected “two, three one,” (here at least).

Here are your other examples rendered with the current macro in BBEdit:

one two
two one

one
two

two
one

The multiple line selection would include both returns, BTW.

The new code does takes precedence over pairs (Case 2), outer words (Case 3), “and/or” (Case 4). I thought a semicolon or single comma would be more significant than those cues. But the precedence can be changed in the Perl code by moving the new lines to the end:

} elsif (/\b(and|or)\b/) {
	s/(.+)\s+?(and|or)\s+?(.+)/$3 $2 $1/;
} elsif (/(.+)\s+?(.+?)\s+?(.+)/) {
	s/(.+)\s+(.+?)\s+(.+)/$3 $2 $1/;
} elsif (/(.+)\s+(.+)/) {
	s/(.+)\s+(.+)/$2 $1/;
} elsif (length($_)==2) {
	s/(.)(.)/$2$1/;
} elsif ((my $count = $_ =~ tr/;//) == 1) {
	s/(.+)(;\s+)(.+)/$3$2$1/;
} elsif ((my $count = $_ =~ tr/,//) == 1) {
	s/(.+)(,\s+)(.+)/$3$2$1/;
}

That would change something like:

one and two; or three

from

or three; one and two [split on “;”]

to

three or one and two; [split on “or”]

If none of this helps, let me know what editor you’re using so I can explore the issue further.


#18

My example wasn’t clear indeed. I’m using BBEdit and the test string is:

two, one, three

Selecting the three words and running the macro gives:

three one, two,

Where I should expect:

three, one, two

Regarding the swapping of two lines: this is solved (I had indeed to include the second line feed).


#19

Ah, I see. In this case it’s the lack of symmetry that causes Super Swap to assume you want to swap the outer elements. So “three” gets swapped with “two,”.

Add a comma to “three” and you’ll get the series swap you expect.