I regularly want to select text within parentheses. It may not happen as often as other tasks, but it happens enough that I wanted to do it more quickly.
This one pops up more in my novel notes and work documents than in fiction manuscripts. It’s quite handy when I need it, and it easily extends to selecting nearby dialog text as well.
Thanks for your interest
This content is part of a paid plan.
Create the empty macro
A previous post covers creating an empty macro like the one shown below. When you’re done, you’ll have something like:
The single quote tells VBA the rest of the text on that line is a comment meant for human readers. We start our macro steps on the empty line.
Also create a second macro to search for parentheses forward in the document. I'll explain the reason for two of them later.
Since this article focuses on text-based punctuation, we’ll assume we’re dealing with text only in this document, so I won’t refer to shapes, frames, or whatever else that’s not important here.
Stand-alone
We’re revising a previous, simpler macro to automatically select parenthetical or dialog text.
This article stands alone, so there is significant overlap in the content if you’ve read the other article for the simpler version, but this version delves a little deeper into some of the specifics of how Word sees documents and how we can manipulate them in a macro.
Why a revision?
The earlier version of this macro was fully functional, but it experienced a small issue with spurious, strange selections if the desired punctuation didn’t exist in the direction of the search.
If you’re like me, it’s aggravating to have a gotcha stalking me, ready to pounce when I least expect it even if I know it’s innocuous. More like being attacked by a kitten than a mountain lion, but hey, those little claws can still scratch and make an owie!
The variation developed below makes the macro more robust to missing punctuation with cleaner, albeit a little more complicated, steps.
Selection versus Ranges
The original version of the macro used the Selection object for all commands for simplicity.
The Selection is basically an enhanced Range in the document with special methods and properties related to a real selection in the document. There are differences between them, but for this macro, they have nearly the same features. As a result, most of the core elements of the original macro are retained.
As you get used to writing your own macros from scratch, using Ranges is generally the better way to work.
See this article for more information about the Selection object in general or this article for a brief introduction to Ranges.
Define the working range
In this version, we use Ranges since they allow us to commit to the selection only after we know it is correct
We start by simply storing the initial Selection in a Range variable.
Recall we must “Set” a Range variable since it contains more information than regular number or text values.
When working with MyRange, we lose a few features specific to the Selection; but for this macro, the distinctions don’t matter.
Start and End with Ranges
Like the Selection, a Range also has Start and End positions. The difference is a Range is invisible to the user.
The initial Selection when the macro is run may span text or just be an insertion point (i.e., the blinking I-bar waiting for your new text in the document).
In the former case, MyRange’s Start and End values will be at different positions in the document. In the latter case, the Start and End positions will be the same.
Range manipulations are invisible
The nice thing about Ranges is any changes are invisible to the user unless a command inserts, deletes, or otherwise modifies text in the document. Thus, we can manipulate them independent of the Selection, and the user doesn’t see anything happening on the screen until we’re ready to commit to the changes in the document.
Assumptions, assumptions
Before we create the macro, we need to understand more about what we are looking for in the document.
Sounds obvious at first, but do we assume the user runs the macro when the insertion point (i.e., the blinking I-bar waiting for new text to be typed) is already between the parentheses? Or do we want our macro to find any nearby parentheses?
The differences are slight, but the specific steps to solve the problem matter in most cases.
Start between the parentheses?
If we assume the former, it simplifies the macro steps since you don’t have to worry about which direction to search in the document at the start of the macro. You know the open parenthesis is on the left, and the close parenthesis is on the right.
Start anywhere including outside the parentheses?
Personally, I like the latter approach since it can find the parentheses anywhere in the document. A macro is also more practical when the user doesn’t have to remember specifics about how it works to use it.
The trade-off is the steps change depending on whether we are looking forward or backward in the document. That's worth it for me since have a more productive macro to use. Both versions happen to work the same if the insertion point happens to already be between the open and close parentheses.
As we develop the macro below, we’ll work on the search left version and tweak it when we're done to search right when we’re done by changing the order of the search and move commands.
Find open parenthesis
We first want to find a set of parentheses before the current insertion point, so we start by searching backward in the document for a left parenthesis “(”. This step is quite general since it works regardless of whether the insertion point is already between the parentheses or not.
Move to open parenthesis
The basic command to move MyRange’s position to a specific character uses the MoveUntil command, so we could try
The command does just as its name implies. It moves MyRange’s position until it finds any one of the characters given in the Cset option. Here we are only looking for a left parenthesis, but you could in principle include any number of characters.
Most regular Cset characters are specified inside double quotes as shown, but some special characters have to be added separately using a few standard Word character constants or a character function for many other special characters.
The Count:=wdBackward option instructs the command to move backward in the document just as the name of the constant implies. The default is forward.
Does the open parenthesis exist?
What if the open parenthesis doesn’t exist in the document? We need a way to check first and avoid any issues if not.
Using function return values
We’ve essentially ignored any information “returned” by commands we’ve called. Most commands will return a value related to the action, of course, and we can use this information to make decisions in our macros.
Specifically for this macro, we try to move the Range’s location to the previous open parenthesis, and then check what the command says before we do anything else. The MoveUntil command returns the number of characters moved (plus or minus 1 depending on the direction) which we store for later use in a new number variable
Negative values indicate a move backward in the document and positive values a move forward. A zero value indicates no move was made because the command did not find (any of) the character(s) given in the Cset option.
Different function notation
As shown in the command above, when storing the returned value in a variable, you must include any options in parentheses. This makes the command look more like a formal function call in other languages.
Checking how many characters were moved
We only want to finish the rest of the macro if an open parenthesis was found. For MoveUntil, this corresponds to a non-zero return value, so we can check against that (we could also just check less than zero since we are moving backward in the document)
Recall the <> symbol is “not equal” (i.e., asking if it is less than or greater than the number on the right), so the If statement is checking whether the returned value is not zero before running the other steps inside the conditional statements.
All the following steps below are included inside the If statement.
Some details
It’s useful to understand some details about the Selection and the document before moving forward. This will also give us some practice understanding macros and how we can bridge the gap between specific macro steps and achieving the overall effect we wish in our document.
At an open parenthesis
If the open parenthesis is located, the Range position is moved just to the right of the newly located open parenthesis. Specifically, the Start and End properties of MyRange are both set to the same position in the document.
No selection yet
Since the Start and End positions are the same, there is no selection in the document yet.
MoveWhile and MoveUntil commands
Both MoveWhile and MoveUntil commands use a Cset option where you can specify move or stop characters, respectively. The catch is the commands move or stop when they encounter any of the characters. That is, Cset:="abcd" will move until it encounters an a or b or c or d not the word abcd.
These commands do not act like a Find operation in your document. You have to use a Range Find command (technically it's a property) to do that.
Include open parenthesis in the selection
Now we start creating the desired selection of parenthetical text. Most of the time, we’ll include the open parenthesis as part of the range using
MoveStart tells Word to move the Start position of MyRange by the given Unit type. We omitted the Unit since the default is to move by character (i.e., the command assumes a Unit:=wdCharacter option if it isn’t given).
The Count:=-1 option tells VBA to move backward (the negative) by 1 Unit to include the open parenthesis in MyRange.
The End position of MyRange is not changed with this command.
Since Start and End now have different positions in the document, we now have a real, albeit incomplete, MyRange spanning a grand total of one character in the document. Time to celebrate!
Include any spaces
If you want to include any spaces on the left side of the selection (not my preference), you could also add
This MoveStartWhile command acts like MoveStart except it keeps moving the Start position while it keeps finding any characters given in the Cset option which is just a space here.
Find close parenthesis
Now that we are somewhat confident our incomplete range is currently in between a pair of the open and close parentheses, we can proceed by moving the End position of MyRange to the close parenthesis.
Since we have already found and included the open parenthesis, we don’t want to disturb the Start side of MyRange for the rest of the macro.
Select to End position
The MoveEndUntil command moves MyRange’s End position to a specific character or any of a set of characters again given by a Cset option.
The Start position is unchanged by this command. Since we are moving the End of MyRange forward, we are extending the selection in the document.
But what if the close parenthesis doesn’t exist?
Check whether close parenthesis exists
To verify whether the close parenthesis exists, we need to store how many characters were moved when trying to reposition the End of MyRange to the next close parenthesis. We again store the value returns by the command
The MoveEndUntil command returns how many characters were moved (plus or minus 1 depending on the direction) just like the MoveUntil command.
We then add another check for whether the command found a close parenthesis exists before committing to any other changes (remember MyRange is still invisible to the user).
The rest of the steps below will be included inside this close parenthesis check.
Include the close parenthesis
Most of the time, we’ll include the close parenthesis as part of the range
The MoveEnd command moves the End of MyRange by a specific number of Units. We don’t need any options here because the default Unit is to move by characters, and the default Count option is to move by one Unit forward in the document.
Include any spaces
Including any spaces on the right side of the range mimics how Word usually works with new selections, so we add
The MoveEndWhile command keeps adding characters to MyRange as long as it keeps finding any character given in the Cset option. We’re just adding spaces. The default is to extend the End of MyRange forward in the document, which is what we need here, so we don’t need the Count option.
The Start position of MyRange is unchanged by this command.
Finally make the selection
Finally commit to the selection we’ve identified using MyRange
The Range Select command does what is says. Whatever text MyRange spans in the document is selected for the user to see in the actual document.
This command will be inside the last close parenthesis check, so no text will be selected unless both parentheses were found.
Final macros
The final versions of these macros are longer and a little more complicated than some others we’ve done, and we also include one for each direction in the document relative to the starting position.
Neither of these require the user to run the macro with the insertion point between the parentheses which is a nice bonus.
As advertised, these versions automatically detect whether the parentheses exist before selecting any document text.
Select parentheses backward macro
To select all text between the previous set of parentheses backward in the document, use
It looks more complicated than before because of the decisions we have to make, but it’s basically the same macro. My actual version is a little different, but the essential elements are the same.
If you want to include spaces on the left side of the Selection, then remove the single quote in your version of the macro on the line
In general, this is a nice way to remove a line from your macro without getting rid of it since VBA will see it as just a comment line.
I assigned my version to the keyboard shortcut Command+Shift+9 in Word for Mac and Control+Shift+9 in Windows.
Select parentheses forward macro
To select all text between the next set of parentheses forward in the document, we need to switch the directions of the searches as well as the search text. After making these changes (not on screen), the result is
I assigned my version to keyboard shortcuts Command+Shift+0 in Word for Mac and Control+Shift+0 in Windows.
Gotchas
Does an initial selection matter?
Does it matter if the user runs the macro with a starting selection instead of an insertion point?
The starting selection, which is also stored as MyRange as the first step, mostly doesn’t matter with this macro because we start by moving the insertion point to an open parenthesis. The move commands that don't modify Start or End automatically collapse the Range when its position is moved.
Having a Range span an open parenthesis could make a difference since the move commands for Ranges appear to work from the Start side of the Range, but the distinction will rarely matter. The only way it would affect the operation of the macro is if the starting Range spanned text including an open parenthesis, and the user expected it to select the current parenthetical text. It's a really small, special case problem.
Even if we start the macro by collapsing the initially stored Range using,
the macro would work the same way.
Faux solutions
If you want to think about some logical gotchas solving this problem, here’s a candidate:
In general, we should be careful about jumping on the first solution that pops into our mind. It may seem to work but may not actually cover the general case.
For example, another solution to the above backward search macro might be
- Select left in the document up to the next close parenthesis
- Extend the selection over a space after the close parenthesis
- Extend the selection backwards to the open parenthesis
- Extend the selection over the open parenthesis
This process seems to save a step compared to the “official” macro depending on the commands used. Unfortunately, they don’t actually work if the user runs the macro when the insertion point is between the open and close parentheses, which is an important case. We need our macro to work either way.
This isn’t to make you skittish about creating macros for your work. It’s just a brief caution to verify that your steps work as expected for your problem.
Adapting for double quotes
Double quotes appear thousands of times in a typical novel, so it would be nice to tweak this macro to allow us to quickly select dialog text.
The direct conversion is to swap out the open parenthesis for a left double quote and a close parenthesis for a right double quote, but there are a few details to handle along the way.
Chr() function for double quotes
The changes are relatively minor assuming we use left and right double quotes. Unfortunately, how we specify them is system dependent.
We'll use the Chr() function since the Cset option in our move command usually takes its search characters in double quotes. Unfortunately, we’re searching for double quotes, so we need an alternative way to specify them as search characters. The character function essentially maps a number in a table to the special character for us.
For a left double quote, Word for Mac uses Chr(210), and Windows systems use Chr(147). Similarly, a right double quote in Word for Mac is Chr(211), and Windows systems use Chr(148).
Assume smart double quotes are used
For simplicity, we will assume smart double quotes are turned on (in AutoCorrect options) meaning Word automatically changes straight quotes to left or right double quotes depending on whether it is on the left or right side of a word. This assumption reduces complications with interpreting the beginning and end of dialog when straight double quotes are used.
Ignore straight double quotes
The Chr(34) character is a straight double quote which is the same on both systems (since it is based on an older table), but we won’t use it for this macro (see gotchas below for more explanation).
Command changes
Revising the respective revised macro commands on a Mac, the initial move command looks like
and the second command extending the End of the selection to the close parenthesis is
On windows, the initial move command is
and the second command extending the End of the selection is
Final Dialog Search Macros for Members
After making the character changes, the final versions are as follows.
Select dialog backward macro
The more robust version to select dialog backward in the document is
My version is different, but the essential elements are the same.
Customizing your version
Remove the single quote comment in your version of the macro on the
line if you like to include spaces at the beginning also.
Also select the correct line, if necessary, for Mac or Windows depending on your system on the lines
For Windows, swap the comment characters and delete the line you don't need.
Keyboard shortcuts
In Windows, I assign this to Alt+Shift+Single quote which is consistent with many other selection keyboard shortcuts.
Unfortunately, Macs or Word for Mac (not sure exactly where the problem is) have problems in some cases processing keyboard input when modifier keys are combined with punctuation. I want to assign this macro to something that makes sense like Command+Shift+Single quote or maybe as a second choice Command+Control+Shift+Single quote.
Unfortunately, Word for Mac will not recognize these key combinations. My guess is it’s a legacy issue from days gone by because there really is no practical reason to not recognize them.
I had to settle for Option+Control+Single quote in Word for Mac which is different than most of my other selection keyboard shortcuts since it doesn’t use the Shift key.
Select dialog forward macro
The more robust version to select the next dialog forward in the document is
Again, swap out the commented MoveUntil lines If you’re working in Word for Windows.
In Windows, I assign this to Control+Shift+Single quote. I had to settle for Command+Control+Single quote in Word for Mac which frustratingly doesn’t use the Shift key.
Gotchas
Straight double quotes
The above double quote versions of the macro required smart quotes (left and right double quotes) to work and ignored straight quotes entirely.
If you wish to extend your version to allow straight double quotes, then there is no explicit direction information associated with the characters, so it becomes unnecessarily complicated to work through the cases to make it work correctly.
How would the macro “know” that a straight double quote is the beginning or the end of a bit of dialog. It’s possible based on spaces or paragraph marks and perhaps other surrounding text, but there are probably more cases to properly handle general dialog than appears at first glance.
I almost always use smart quotes in Word, so I chose not to attempt this variation since it’s not worth the time investment for the small benefit.
If you desperately want this variation, let me know, and if there is enough interest I might create a separate member article since it is likely to be complicated compared to what we’ve already done.
Mixed smart double quotes
Sometimes if you’re working with odd characters or spacing in combination with double quotes, Word will not properly apply the smart quotes. That is, it may leave a right quote on the left or vice versa, but depending on your font, it’s often not clear in the document unless you’re paying close attention to your double quotes.
Unfortunately, this will make the current macro work incorrectly, but it’s not worth the effort to rework the above macro to allow for misplaced or incorrectly applied smart quotes.