Many spelling errors are quick flubs while you’re typing where you don’t necessarily want to take the time to go back and make the change. Even creating a quick shortcut to jump back to it will still be kind of slow.
Thanks for your interest
This content is part of a paid plan.
Correct Previous Spelling Error
If you’re like me, you can’t stand to see those little red squiggles as you type. It breaks my concentration like it’s mocking me. I know there is an edit feature where I can iterate through the errors, but I don’t want to wait. Writing sprint or not, the squiggles have to go! At least in my book.
AutoCorrect approximation
There is an AutoCorrect setting that will correct some spelling errors from the dictionary while you type, and you can add your own common misspellings (e.g., I added Ture for True because I kept messing it up). I use both of those features. However, they obviously don’t catch all errors, so let’s take the next step on the journey.
What’s the plan?
My current context is typing a novel and making a mistake. I’m in the flow, but those red squiggles pop under, and … My. Attention. Must. Fix. Error. I don’t want to take the time to move back and do so manually, so … let’s fix it right now.
Fortunately, most of the time, the first suggested spelling correction for is the right one, so why not let Word go ahead and make the change ... without touching the mouse. What do we need to do?
- Find the previous spelling error within a reasonable document range
- Get the collection of spelling suggestions
- Replace the spelling error text with the first (presumably best) suggestion
A bonus case is similar:
- If a spelling error doesn’t exist, correct any doubled spaces in the same range
Search range restriction?
“Huh?”
Before I leave you confused, let’s clarify a few points that may not be immediately obvious.
A range has collection of proof-reading errors for all errors inside the range.
“Okay—”
Just hold on. We’re not to the confusing part yet.
A Find operation is performed using a range variable (or the Selection), but it doesn’t stop a search within the used range. That behavior might seem intuitive given the errors collection is based on the spanned document range; however, the Find operation will continue searching through the document and update the used range variable to span any found text.
Not all words are in the standard or custom dictionaries, so in this macro, we want to restrict the search range to reduce any chances of an unintended correction. Toward this end, we create a second search range variable and ensure the found text range is inside it before we do anything else.
Previous spelling error
Limiting the search to somewhere within the previous portion of the page allows me to see the immediate change (I often write with a whole page visible on screen) and whether the change matched my expectations. Later, we’ll also see that it makes it easier to target the intended spelling error.
Using the first suggestion
We pick the first corrected spelling suggestion since this macro is intended to be a quick keyboard shortcut tap, and it’s done. I’m not interested in opening dialog boxes or jumping back to the error to manually fix it. My experience is the first suggestion is correct roughly 90% of the time, and that’s good enough for me.
Double spaces extra case
I added the doubled spaces correction because I consider that “like” a spelling error, so it fits the use case for this macro. They can annoy me about as much as a spelling error, so just fix the problem and move on. See the extended content below for more on this addition.
Create macro skeleton
Open the VBA editor (Option+ or Alt+F11) and create an empty macro.
Macros we assign to a keyboard shortcut can’t have any parameters or return values.
Define working range
To define the search range, which we’ll call rSearch, we start with the current page. A previous article talks more about using the special Word bookmarks, so we’ll be quick here. A bookmark is like a range in Word but with some extra stuff.
The fastest way to get the current page is to use one of Word’s hidden bookmarks named \Page. All the hidden bookmarks start with a backslash \. We use the Bookmarks collection of the Selection using double quotes for the bookmark name “\Page”.
We get the corresponding page range using the Bookmark's Range property. We then Set this range equal to our search range variable.
Limit working range to previous content
The above range is currently the entire page. The intended use case corrects the previous spelling error, so we need to limit the extent of the range based on the user’s current location in the document. To accomplish this, we assign the end position of the Selection to the End position of the working range.
No Set is required here because the End positions are just character position values in the document. I personally further tweak the search range based on the current paragraph, but this is good enough here.
Get spelling errors
Define a ProofReadingErrors variable:
Yes, there’s an actual collection of proof-reading errors in Word. There are grammar errors and spelling errors, but we’re focused on spelling errors here.
This variable declaration isn’t strictly necessary (unless you’re using Option Explicit), but for such an unusual collection, I thought it was clearer to include it.
Since we’re dealing with an object that’s not a regular value, we need to Set it equal to the SpellingErrors collection. Our working range rSearch has a collection of included spelling errors which may be empty.
We’re storing the errors collection in the MyErrors variable.
Fix spelling error
Correcting the error is a little more complicated than using the context menu, or whatever other approach, because we want to correct it automatically. It takes a little work to sort all this out before correcting the spelling error.
Check error count
What if there are no spelling errors detected or any suggested corrections?
Obviously, there is nothing to do if there are no spelling errors found in our restricted search range. We can check that using the Count property of the errors collection.
This stores the total number of errors in the rSearch range for convenient use below. As a mnemonic, I will often add a prefix for any number-related variable with a lower-case or capital N. It also shortens the variable names.
The If statement ensures we only proceed with any changes if we find at least one prior spelling error. Otherwise, we just exit the macro.
Get last spelling error
The last spelling error range in the search range is the one just prior to our current typing location. Based on the restricted rSearch range, this is the last error in the collection.
The parentheses notation used with a collection picks the spelling error at that particular number of the collection. NErrors is the total number of errors, so it is the last error in the collection. Working with only the prior spelling errors on the current page made it easy to identify the spelling error immediately before our typing location.
For an error collection, this reference returns a document range corresponding to that particular error which we're storing in the rError range variable.
To be extra clear, MyErrors is the collection of ranges including the spelling errors in the rSearch range. The rError variable above is the document range corresponding to the last spelling error.
Alternative with GoTo command
In general, a more direct method to get the previous spelling error would be using a GoTo method with a command something like the following:
But in this macro, we already have the restricted search range, so we just immediately accessed the last spelling error in the range. This also allowed us to avoid additional verifications that the found grammar error is in the search range.
Get spelling suggestions collection
What is the correction text?
We first need the collection of possible suggestions. Any suggestions are returned by the GetSpellingSuggestions method of the error range defined above.
We tell the method to IgnoreUppercase by assigning True to the option since we don’t want case to matter.
The Suggestions variable is being Set to a collection of suggested spellings of whatever misspelled word is in the rError range.
Get spelling suggestions
We need at least one spelling suggestion before we continue. Otherwise, we have nothing to use for the automated correction. Get the total number of suggestions using the collection’s Count property.
Make sure we have at least one suggestion before proceeding.
Get correction text
Setting up to make the text change, we first identify the suggestion text.
This picks the first suggestion in the Suggestions collection using the number one in parentheses (1). A suggestion’s plain text value is stored in its Name property.
Fix spelling error
Finally assign the correction text to the spelling error text.
This is a plain text change not respecting any formatting other than perhaps adopting formatting of the first character in the document at the text location.
Gotchas
Where could all this go wrong?
What if there are no spelling errors or suggestions?
The first If statement checks whether any spelling errors exist, so if they don’t, nothing happens. Same goes for no suggestions. With the checks performed in the macro, nothing happens in the document if no errors or suggestions exist.
However, when using the macro, this can be a little disconcerting at first because you might wonder whether you actually tapped the keyboard shortcut correctly. In my opinion, this behavior is far more preferable than having to dismiss a dialog box that pops up. I could always just tap the keyboard shortcut again to be sure.
What if the spelling correction isn’t what I wanted?
That definitely happens occasionally. The top suggestion in the list is correct much of the time, but if my spelling is particularly atrocious for that word (missing the first letter seems to cause the most trouble), it can misread our intended word. In that case, I usually just undo the correction, and I have a supplementary macro to jump to the previous spelling error where I can manually make the change. Alternatively, I just undo the correction and ignore the error until the next edit. It just depends on my mood at the time.
Final Macro
Putting everything together, the macro is:
I assigned this macro to Command+Shift+F7 in Word for Mac or Control+Shift+F7 on Windows. I override a standard function key shortcut because I rarely use most Word's default function key shortcuts.
What about grammar errors?
Grammar errors don’t correspond to word-related suggestions in the same way as spelling errors, and VBA does not give us access to the specific grammar errors in the same way as spelling errors (the whole sentence range is returned). It is likely not currently possible to replicate or extend the above macro for grammar errors.
However, if you use the default Word keyboard shortcut Option+F7 or Alt+F7, you can jump to the next grammar error. Word will immediately offer suggestions in a context dialog ready to apply, usually just by pressing Enter. It’s not as nice as just making the change and moving on, but on the other hand, grammar errors often require more of a human touch anyhow.
Fix Previous Double Spaces
This is a smaller addition (but more steps) to the automated spelling correction above since I sometimes accidentally add doubled spaces also.
Other Functions
To complete the macro below, we'll need a few assistant functions. They aren't strictly necessary, in general, but they make writing the macro easier and cleaner.
- Detect sentence punctuation to distinguish between different sentence punctuation marks
- The above function also uses some character functions since some special characters depend on whether you're working in Word for Mac or Windows.
- Delete spaces at the end of a paragraph to automatically handle a small Word gotcha.
We also use If-ElseIf statements below, so if you'd like an introduction or a basic refresher on conditional statements, check out this introductory article.
Additional working range
We established the working search range rSearch earlier. A second working range will start with the current Selection Range.
This is basically the current typing position in the document. We’ll use it to search for the doubled spaces with the Find method. Since we’re searching backward, we collapse toward the end of the Selection, so we don’t miss anything.
Setup Find parameters
I won’t go through every Find parameter option below, but I like to list anything relevant to clearly define the search and avoid any unexpected issues.
This uses a With statement since we’re referring to many Find parameters at once. With statements allow us to omit the r.Find reference for all settings inside it.
Most of these make sense for a spaces search. For example, we can’t ignore spaces. Most of the other settings are just to be careful, but two are more relevant.
We’re searching backward, using .Forward = False since the use case mentioned earlier was correcting recent document errors while writing a novel. The .Wrap = wdFindStop settings doesn’t allow the search to wrap around from the end of the document. Although, technically this one is superfluous since I restrict the found text to the search range.
Do the search
The Find operation is performed using the Execute method of the Find property.
The FindText is literally two spaces in double quotes “ ”. We are not replacing the text, thus the wdReplaceNone value for the Replace option.
After the command executes, the Find operation will select the relevant text as the new working range r, so we can do something with it. As a reminder, the search is not restricted to the starting range. It will continue through the document trying to find a match according to the rules given to the Find command.
Check working range extent
We need to know if the find operation was successful which is determined using the Found property.
This condition is True if the search was successful and False if not.
Constraining the search
The Find operation will continue through any defined range into the rest of the document. In my use case, I don’t want to be jumping back to seemingly random locations in the document, so I restrict my search to the previously defined search range rSearch.
If the found text is inside the previous page range rSearch, then delete the doubled (or more) spaces. Fortunately, there is already an InRange method to detect such a condition.
This method returns True if the range r is inside the argument range rSearch (the one in parentheses).
Our condition requires both a successful Find action to be True and for it to occur in the constrained page range. Since both must be True to proceed, this requires a Boolean And operator.
Extend range over excess spaces
There’s no sense leaving other spaces if there are more than two, so expand the range to include any additional spaces. Our initial search was backward, so the working range r would necessarily find the two right-most spaces in any gap.
We’ve used MoveStartWhile multiple times through this blog, but briefly it moves the beginning of the range while it keeps finding any characters given in the Cset option which is given as a space " " here (double quotes are required to define regular text for the character set). In this command, the Count option tells the command to move the Start position backward in the document.
Now we know we’ve included all local spaces in whatever gap exists.
Delete spaces conditions
We could just delete the doubled spaces, but I like my macros to handle the different cases naturally within reason. What are the various cases?
- If the next character is a paragraph mark, delete all spaces.
- If the next character is a sentence punctuation mark (most of them, see below), delete all spaces.
- If the next character is any other character, delete all but one of the spaces.
Unfortunately, this is a little trickier than it seems at first glance.
Negatory on reducing to two conditions
You might want to combine the first two conditions since they have the same result (delete all spaces in the range), and normally this would be logical since they correspond to the same action, but there is a confounding issue for the end of a paragraph that makes it a separate case (see below). There is a way around it, but I felt like this was the clearest solution.
ElseIf statements
We have three possible conditions above, so we need an If-ElseIf statement (see article introducing conditional statements). A whirlwind introduction to the If-ElseIf statement follows.
An If-ElseIf is essentially a chain of If statements with another keyword ElseIf joining them together.
We can chain more ElseIfs in the middle section, but if it gets too long, you should probably consider a Select statement instead since they’re designed for longer decision branches.
How it works
If the first condition is False, then check the next one, and repeat until no more ElseIfs are present. Once a branch is selected based on the first one with a True condition, run those steps and ignore all other branches regardless of their condition values. If all conditions are False, do the Else steps if they’re included just like a regular If statement.
Basic idea for our problem …
For our particular problem, the branches would be something like:
Of course, we need to replace the words with actual logical conditions and decide on the specific steps to take in each branch.
Get next character
We first need to get the next character after the spaces range for our testing. This is easy since we know we have a range with something in it.
We technically don’t have to declare the NextCharacter variable yet, but we’ll need it later to pass it to a function, and it needs to be declared before we use it.
In general, a String can contain more than one character, but we’re picking it to correspond to a single character.
We’ve used the Next method several times. It returns the next Unit given, and we specify wdCharacter here. The default Count is 1 which is what we want. We store the value in the NextCharacter variable which is one reason why we needed to include the Unit option in parentheses.
The Next method returns a range, but we want the actual character, so we reference its Text property at the end.
Paragraph ending
Recall a paragraph ends with the special paragraph mark character vbCr.
Checking for the paragraph ending is easy. Just compare the known NextCharacter variable to vbCr.
Unfortunately, correctly deleting the space can be a little complicated for certain cases because Word likes to automatically adjust the spacing for us even while our macro is running. We’ve previously written a function to overcome this tiny but annoying issue, so we’ll take advantage of it here.
The DeleteParagraphEndSpaces function deletes any extraneous spaces at the end of a given Paragraph (not a Range), so we give it the First paragraph of the working range r using its Paragraphs collection.
This paragraph is not actually within the range r, but the function doesn’t know or care. It just works with the given paragraph. Check out the previous function if you’re curious. As simple as the task sounds, it ends up taking several steps to correctly complete in all cases.
Sentence end
Word doesn’t seem to cause any trouble (by adding spaces back without our knowledge) when we’re deleting spaces at the end of a sentence, so just delete the whole range of spaces.
With that in mind, we need to know whether NextCharacter is a sentence ending punctuation mark.
Other punctuation
However, we can easily generalize this condition to allow for other punctuation marks that are also not preceded by a space like dashes, an apostrophe, a comma, etc. For lack of a better brief description, we’ll refer to these as “right” punctuation marks in analogy with right double quotes. See the previous function for this simplifying function.
We pass the known NextCharacter to the function for the test.
This function returns true if it finds a punctuation mark indicated by NextCharacter isn’t usually preceded by a space or paragraph mark.
This function call is also why we needed to explicitly declare the NextCharacter variable earlier. If not declared explicitly, VBA would complain and not run the function (see earlier article for a more explanation).
Delete spaces at punctuation marks
Now use the punctuation mark function to decide whether to delete the spaces.
This looks simple because the IsRightPunctuation function did the checking for us, so it is just a quick function call.
Other next characters
For any other characters, we delete all but one space. Since the range currently contains only spaces, we can just move the Start position and remove the first one.
We could also just replace the Text property with a single space. It just a matter of preference.
Delete doubled spaces snippet
Putting the double space code together, we have a long snippet of code which we include inside an Else part of the original If statement in the macro above.
Combined Macro
This is the extended version of the macro combining the spelling and double space corrections.
I assigned this macro to Command+Shift+F7 in Word for Mac or Control+Shift+F7 on Windows. I override a standard function key shortcut because I rarely use most of the default Word function key shortcuts.
Copy and paste warning
If you copy the above macro (which is welcomed for individual use), HTML formatting often collapses repeated spaces such as the doubled spaces in the Find.Execute FindText option. I used two non-breaking spaces as a workaround for this issue (a non-breaking space is a different character than a regular space). In testing, this seemed to circumvent the space collapse by HTML since the editor appeared to translate the non-breaking space into a regular space during the text paste. To be safe though, after you copy it into the VBA editor, you should manually verify the FindText option is a double regular space in double quotes before using the macro.
VIP technical support
Sometimes debugging more complex macros can feel like playing a Whack-a-Mole game especially as you add new and improved features. Premium members get access to special VIP links for limited technical support as specified in the member benefits [please note support time does not accumulate since I cannot overcommit future scheduling requirements (but I try to be reasonable about it) nor cash out in any way if membership ends or is downgraded; support does not consist of writing entire macros or functions which would instead qualify as a paid service].