Heading

This is some text inside of a div block.
This is some text inside of a div block.
This is some text inside of a div block.
min read

Select Parentheses or Dialog More

Word • Macros • Editing
Peter Ronhovde
24
min read

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:

Sub SelectParenthesesBackward()
' Select all text between the previous pair of open and close parentheses
' including the parentheses

End Sub

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.

Example macro selecting parenthetical text

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.

Set MyRange = Selection.Range

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

MyRange.MoveUntil Cset:="(", Count:=wdBackward

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

nCharactersAwayLeft = MyRange.MoveUntil(Cset:="(", Count:=wdBackward)

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)

If nCharactersAwayLeft <> 0 Then
' Parenthesis found, so do the other steps ...
End If

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

MyRange.MoveStart Count:=-1

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

MyRange.MoveStartWhile Cset:=" ", Count:=wdBackward

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.

MyRange.MoveEndUntil Cset:=")"

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

' Move forward to the next close parenthesis
nCharactersAwayRight = MyRange.MoveEndUntil(Cset:=")")

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).

If nCharactersAwayRight <> 0 Then
' More steps used only when a close parenthesis exists ...
End If

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

MyRange.MoveEnd

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

MyRange.MoveEndWhile Cset:=" "

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

MyRange.Select

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

Sub SelectParenthesesBackward()
' Select all text between the previous pair of open and close
' parentheses including the parentheses.
' Insertion point does not have to start between the parentheses.
' Only selects text if both double quotes are found.

' Store the current selection
Set MyRange = Selection.Range

' Try to move to a previous open parenthesis
nCharactersAwayLeft = MyRange.MoveUntil(Cset:="(", Count:=wdBackward)

' Check whether the open parenthesis was found
If nCharactersAwayLeft <> 0 Then
' Found an open parenthesis so continue
' Include the parenthesis and any spaces (optional) before it
MyRange.MoveStart Count:=-1
'MyRange.MoveStartWhile Cset:=" ", Count:=wdBackward

' Extend the selection forward to the next close parenthesis
nCharactersAwayRight = MyRange.MoveEndUntil(Cset:=")")

' Check whether the close parenthesis was found
If nCharactersAwayRight <> 0 Then
' Include the parenthesis and any spaces on the end
MyRange.MoveEnd
MyRange.MoveEndWhile Cset:=" "

' Found close parenthesis, so select the working range
MyRange.Select
End If
End If
End Sub

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

'MyRange.MoveStartWhile Cset:=" ", Count:=wdBackward

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

Sub SelectParenthesesForward()
' Select all text between the next pair of open and close
' parentheses including the parentheses.
' Insertion point does not have to start between the parentheses.
' Only selects text if both double quotes are found.

' Store the current selection
Set MyRange = Selection.Range

' Try to move to the next close parenthesis
nCharactersAwayRight = MyRange.MoveUntil(Cset:=")")

' Check whether the close parenthesis was found
If nCharactersAwayRight <> 0 Then
' Found a close parenthesis so continue
' Include the parenthesis and any spaces after it
MyRange.MoveEnd
MyRange.MoveEndWhile Cset:=" "

' Extend Start backward to the previous open parenthesis
nCharactersAwayLeft = MyRange.MoveStartUntil(Cset:="(", Count:=wdBackward)

' Check whether the open parenthesis was found
If nCharactersAwayLeft <> 0 Then
' Include the parenthesis and any spaces (optional) at the start
MyRange.MoveStart Count:=-1
'MyRange.MoveStartWhile Cset:=" ", Count:=wdBackward

' Found open parenthesis, so select the working range
MyRange.Select
End If
End If
End Sub

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,

MyRange.Collapse

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

MyRange.MoveUntil Cset:=Chr(210), Count:=wdBackward

and the second command extending the End of the selection to the close parenthesis is

MyRange.MoveEndUntil Cset:=Chr(211)

On windows, the initial move command is

MyRange.MoveUntil Cset:=Chr(147), Count:=wdBackward

and the second command extending the End of the selection is

MyRange.MoveEndUntil Cset:=Chr(148)

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

Sub SelectDialogBackward()
' Select all text between the previous pair of double quotes.
' including the double quotes.
' Insertion point does not have to start between the double quotes.
' Assumes curly/smart double quotes and does not check for
' straight double quotes.
' Only selects text if both double quotes are found.

' Store the current selection
Set MyRange = Selection.Range

' Try to move to a previous left double quote
nCharactersAwayLeft = MyRange.MoveUntil(Cset:=Chr(210), Count:=wdBackward) ' On Mac
'nCharactersAwayLeft = MyRange.MoveUntil(Cset:=Chr(147), Count:=wdBackward) ' On Windows

' Check whether the left double quote was found
If nCharactersAwayLeft <> 0 Then
' Found a left double quote so continue
' Include the double quote and any spaces (optional) before it
MyRange.MoveStart Count:=-1
'MyRange.MoveStartWhile Cset:=" ", Count:=wdBackward

' Extend End forward to the next right double quote
nCharactersAwayRight = MyRange.MoveEndUntil(Cset:=Chr(211)) ' On Mac
'nCharactersAwayRight = MyRange.MoveEndUntil(Cset:=Chr(148)) ' On Windows

' Check whether the right double quote was found
If nCharactersAwayRight <> 0 Then
' Include the double quote and any spaces on the end
MyRange.MoveEnd
MyRange.MoveEndWhile Cset:=" "

' Found both double quotes, so select the quoted text
MyRange.Select
End If
End If
End Sub

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

'Selection.MoveStartWhile Cset:=" ", Count:=wdBackward

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

Selection.MoveUntil Cset:=Chr(210), Count:=wdBackward ' On Mac
'Selection.MoveUntil Cset:=Chr(147), Count:=wdBackward ' On Windows

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

Sub SelectDialogForward()
' Select all text between the next pair of double quotes
' including the double quotes.
' Insertion point does not have to start between the double quotes.
' Assumes curly/smart double quotes and does not check for
' straight double quotes.
' Only selects text if both double quotes are found.

' Store the current selection
Set MyRange = Selection.Range

' Try to move to the next right double quote
nCharactersAwayRight = MyRange.MoveUntil(Cset:=Chr(211)) ' On Mac
'nCharactersAwayRight = MyRange.MoveUntil(Cset:=Chr(148)) ' On Windows

' Check whether the right double quote was found
If nCharactersAwayRight <> 0 Then
' Found a right double quote so continue
' Include the double quote and any spaces after it
MyRange.MoveEnd
MyRange.MoveEndWhile Cset:=" "

' Extend Start backward to the previous left double quote
nCharactersAwayLeft = MyRange.MoveStartUntil(Cset:=Chr(210), Count:=wdBackward) ' On Mac
'nCharactersAwayLeft = MyRange.MoveStartUntil(Cset:=Chr(147), Count:=wdBackward) ' On Windows

' Check whether the left double quote was found
If nCharactersAwayLeft <> 0 Then
' Include the double quote and any spaces (optional) at the start
MyRange.MoveStart Count:=-1
'MyRange.MoveStartWhile Cset:=" ", Count:=wdBackward

' Found both double quotes, so select the quoted text
MyRange.Select
End If
End If
End Sub

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.

Affiliate Links

If you're interested in using Word or another tool related to the article, check out these affiliate links. I may make a small commission if you purchase when using them, but there is no increase in cost for you, and it helps to support this site and associated content.

I've been using Microsoft for Business for commercial use (that's us writers) on one of the lower pricing tiers for years. I get to use my macros, have online storage, and don't have to worry about software updates.