Techniques for Swapping Words Using the StringSplit AutoHotkey Command; ErrorLevel for Producing Alternative Results; a Glance at a Regular Expression (RegEx) for Swapping Words
In the last blog, we looked at a quick Hotkey for swapping mistyped letters in any Windows document or text editing field. Just place the text cursor between the two letters and hit ALT+R. The characters reverse positions. This time the technique is expanded and refined for swapping two words.
This beginning Hotkey blog builds upon the discussions in the previous parts. If you find any of the information too confusing, then reviewing earlier blogs may be worthwhile.
New to AutoHotkey? See “Introduction to AutoHotkey: A Review and Guide for Beginners.”
* * *
First, we look at a quick-and-dirty method for switching inadvertently transposed words. Place the text cursor between any two words (surrounded by spaces), then swap them by pressing the ALT+W Hotkey combination. Next, we add a more exact technique for those times when punctuation follows the second word. Finally, a new routine using the ErrorLevel variable detects which of the two techniques to use.
Hotkeys for Swapping Words
In Chapter Seven, “A Simple Beginner’s Trick for Swapping Letters and Words” from the book, A Beginner’s Guide to Using Regular Expressions in AutoHotkey (not a beginning level AutoHotkey book), we extend the SubStr() function for swapping words before introducing a more functional Regular Expression (RegEx) for the same purpose. While this SubStr() technique works, in the book it becomes increasingly complicated before we switch to the more powerful—although a little mind bending—RegEx. However, though less comprehensive than the RegEx, there is a simpler AutoHotkey approach to word swapping which doesn’t require the use of a RegEx.
The StringSplit command is a quick method for parsing (breaking apart) a group of words and placing them in an array of discrete variables (Array1, Array2, Array3, …). Using the StringSplit command eliminates much of the complication involved in word swapping with the SubStr() function.
The following AutoHotkey script selects the two words on either side of the text cursor and swaps them in place:
$!w:: OldClipboard := ClipboardAll ;save old Clipboard Clipboard := ;empty Clipboard SendInput ^{Left}^+{Right 2} SendInput, ^c ClipWait 0 StringSplit, word_array, Clipboard, %A_Space% SendInput, %word_array2%%A_Space%%word_array1%%A_Space% Clipboard := OldClipboard ;restore old Clipboard Return
Note: This snippet of code uses the Clipboard techniques discussed in the last blog for saving and restoring the old Clipboard contents.
The SendInput command word highlighting technique—used in a previous blog for deleting selected words—now automatically selects the target words for swapping by first jumping one word to the left (CTRL+Left Arrow or ^{Left}), then jumping two words to the right while holding down the SHIFT key (CTRL+SHIFT+Right Arrow twice or ^+{Right 2}). (These SendInput commands simulate the keyboard combinations which execute the same word selection actions.) Then, AutoHotkey copies the selected words to the Windows Clipboard (SendInput, ^c).
Parsing (Breaking Apart) Words with the StringSplit Command
The beauty of the StringSplit command is the way it quickly breaks apart any line of text and places each word (or text segment) into a separate variable. While replicating this action is possible with variations of the SubStr() function, the additional complications involved with using it make StringSplit a tremendous time saver over SubStr().
In the above example, the contents of the Clipboard is broken into two pieces, then placed in the pseudo-array word_array (word_array1 and word_array2). The SPACE character acts as a delimiter (%A_Space%) marking the separation between the words. The StringSplit command uses the same variable name for each text segment by concatenating (adding) a number onto the end. For example, the variable name word_array assigns word-array1 and word_array2 to the first two parsed segments respectively. The variable word_array0 contains the number of variables created by StringSplit. This word_array0 value comes in handy when manipulating an unknown number of text segments.
Tip: If you plan to break text into sentences for placement in array variables, then use the period or dot (.) as the delimiting character (StringSplit, word_array, Clipboard, .) in place of the SPACE character. That way the same word swapping code can switch two sentences inside a paragraph—although first, you must highlight the target sentences since there are no standard shortcuts for selecting complete sentences in Windows. (Use the pre-selected text technique illustrated in the next example.)
Finally, the SendInput command inserts the two words into the document in the reverse order:
SendInput, %word_array2%%A_Space%%word_array1%%A_Space%
Notice that the spaces (%A_Space%) must be added back into the string, since, as the delimiter, they were removed by the StringSplit command.
Selecting Text for Better Accuracy, Plus an ErrorLevel Trick
The strength of the above word swapping Hotkey is that it’s simple and quick. Merely locate the text cursor between the two words and hit ALT+W—no pre-selection of text needed. This is fine when there are spaces surrounding both the words, but what if you have punctuation at the end of the second word—such as at the end of a sentence. Swapping “guy tough.” would become “tough. guy” moving the period with the second word.
We could use the following script for another Hotkey for pre-selecting the words before using the Hotkey combination:
$!w:: OldClipboard := ClipboardAll Clipboard := SendInput, ^c ;text pre-selected ClipWait 0 If ErrorLevel { MsgBox, No text selected! } StringSplit, word_array, Clipboard, %A_Space% SendInput, %word_array2%%A_Space%%word_array1% Clipboard := OldClipboard Return
Tip: This is the same routine that could be used to swap contiguous sentences in the same paragraph. Simply change the delimiter to a period (StringSplit, word_array, Clipboard, .) and replace the %A_Space% with the same period (SendInput, %word_array2%.%word_array1%.). When selecting the two sentences, be sure to include both periods and the leading spaces.
Before the word swap in this routine, the text must be pre-selected in its entirety—excluding the last period.
You might find creating extra Hotkey combinations inconvenient. After all, they do almost exactly the same type of word swapping. Wouldn’t it be better to use the same Hotkey combination for the quick-and-dirty swap when nothing is selected, as well as, the more accurate punctuation-excluding swap when the words are pre-selected? This is where the ErrorLevel variable comes in handy.
Optional Action For ErrorLevel Variable Condition
The two previous snippets are combined into one Hotkey script which takes one action with pre-selected text and another when no text is selected. The ErrorLevel variable detects whether or not data moves during the ClipWait into the previously emptied (Clipboard :=) Clipboard. If so, the text was pre-selected and ErrorLevel is set to 0. If not, there was no pre-selection and ErrorLevel is set to 1.
$!w:: OldClipboard := ClipboardAll Clipboard := SendInput, ^c ClipWait 0 If ErrorLevel ;no pre-selected text { SendInput ^{Left}^+{Right 2} SendInput, ^c ClipWait 0 StringSplit, word_array, Clipboard, %A_Space% SendInput, %word_array2%%A_Space%%word_array1%%A_Space% } Else ;text pre-selected { StringSplit, word_array, Clipboard, %A_Space% SendInput, %word_array2%%A_Space%%word_array1% } SendInput ^{Left} Clipboard := OldClipboard Return
I would guess that ninety percent of the time the quick-and-dirty routine where spaces surround both words would do the job. But on those occasions where a period or comma immediately follows the second word, pre-selecting the text while excluding the following punctuation mark works. (The space character should not be added to the end of the second word because the pre-selection excluded the possibility.) However, these are not the only issues which arise when swapping words in a document.
Suppose there is intervening punctuation? What if there are other words between the two you plan to switch? While I’m sure that there are ways to solve these problems with the StringSplit command, they are likely to become convoluted. It’s at times like these that I start looking at Regular Expressions (RegEx).
Using a Regular Expression (RegEx) to Swap Words Anywhere (Advanced Topic for the Curious Only)
As mentioned in the beginning of this blog, in Chapter Seven, “A Simple Beginner’s Trick for Swapping Letters and Words” of the book A Beginner’s Guide to Using Regular Expressions in AutoHotkey, we walk through process of the building a RegEx which, with only one line of code, addresses the issues cited. While Regular Expressions (RegEx) are beyond the scope of this blog, it’s worth noting their power by giving this final example for word swapping from the RegEx book. (If you would like a mini-RegEx tutorial, check out this “Introduction to Regular Expressions (RegEx) in AutoHotkey.”)
$+F2:: OldClipboard:= ClipboardAll Clipboard = SendInput, ^c ClipWait 0 If ErrorLevel { MsgBox, No text selected! } SwappedWords := RegExReplace(Clipboard," ;This one ([\w']+\w)(\S?\S?\S?) ;line broken (\s.*\s|\s+)(""?'?) ;for display ([\w']+\w)","$5$2$3$4$1") ;purposes only SendInput, %SwappedWords% Clipboard:= OldClipboard Return
Note: The line of code for the SwappedWords := RegExReplace() function is one continuous line—only broken into four segments for display purposes. To test the snippet remove all space between each segment. The expression alone should read as follows:
([\w']+\w)(\S?\S?\S?)(\s.*\s|\s+)([""|']{0,2})([\w']+\w)
As you can see this routine also uses standard tricks for Clipboard text manipulation. The one Regular Expression line of code replaces what would consume many more lines of code when using the SubStr() function—or even the StringSplit command.
In the book, the development of this RegEx starts with the simple task of swapping two contiguous words, as shown above. Next, it deals with a conjunction appearing between the two words as in swapping “bows and buttons” to “buttons and bows.” Then, the problem of punctuation appearing between the target words gets included in the RegEx. Finally, the capability to swap any two words more than one word apart, dealing with contractions, and other RegEx solutions are added, finishing up the above RegEx.
Okay, this example goes way beyond what any beginner level. I only include it as an illustration of what’s possible. At some point in your AutoHotkey scripting endeavors, you may reach the point where Regular Expressions look like a good solution. Then, while it takes a little more mental agility to grasp them, it may be well worth the time to learn Regular Expressions.
Next time, let’s make Hotkeys for swapping letters and words even simpler by using a simple click of the mouse with little-used keys.