We create a function to get the current sentence range while allowing naturally including double quotes or parentheses if they are present on both sides of the initial sentence.
Thanks for your interest
This content is part of a paid plan.
Select sentence with dialog or parentheses
This is another in a series of functions intended to streamline our previous move sentence macro.
Sometimes it’s convenient to isolate specific tasks even when they aren’t complicated on their own, and selecting a sentence range is one such function. It's also a general enough task that it makes a good candidate function for use in other text manipulation macros.
In the process of encapsulating the function as its own separate thing, we can often enhance it without complicating the original macro. Then we can leverage it to make other useful macros even easier to implement.
The Problem
In the initial article, we created two macros to move a sentence forward or backward in the document. Both are awkwardly long, so we are breaking them down into smaller chunks. In practice, we would do this while creating the original macros to make the work easier, but we’re attacking the problem after the fact this time.
What’s the plan?
It’s a good practice to think about some details regarding how the macro works. It doesn’t mean you’ll get everything exactly right, but you’ll go into the process with a plan which is usually better than winging it (another life lesson beckons, but I shall restrain myself ...).
Roughly speaking, the function should work as follows:
- Start in a general paragraph with any sentence which could be just regular text, text within dialog, or text between parentheses.
- Set the working sentence range based on the current selection or insertion point (a blinking I-bar with no text selected).
- Trim any paragraph marks or spaces from the right side of the range.
- Trim double quotes or parentheses if they only occur on one side of the range.
We remove any paragraph marks because they shouldn’t be part of a sentence (even though Word includes them in selections). We omit spaces because we need to check for punctuation at the end of the range, and that’s easier to do when we remove the spaces..
Let’s translate those assumptions into a function.
Create function skeleton
The function skeleton is basically the same as previous ones we’ve created.
What parameters to use?
In this function, we want the current sentence range, so there are no parameters to accept. We instead based our starting range on the initial selection or insertion point in the document.
Return type
We’re returning the sentence range after properly accounting for double quotes or parentheses, so I added “As Range” at the end of the Function declaration line. The return value for the sentence range is temporarily set to Nothing because we don’t know what it is yet.
See a previous article for a description of the Nothing value, but briefly, it is assigned to invalid objects that have no regular assignment yet.
Get initial sentence range
Let’s review the sequence for the original sentence selection steps.
Define initial range
We first define the working range for based on the current Selection’s Range property.
This command sets our working range equal to the current selection or insertion point in the document. In Word, an insertion point is just an empty Selection spanning no content. Specifically for this corresponding working range, it's Start and End positions in the document would be the same value.
We’re using a simple working range name r, so it’s easier to type.
Collapse the range
What happens if there was an initial selection of spanned text when the user runs the macro? Let’s avoid any issues by collapsing the selection right off the bat.
Why?
The range is now known to be empty specifically positioned at the beginning of the document Selection.
Basic Expand command
Now we can expand the range as needed with some confidence since we have a consistent starting point for our macro. The command to expand the range over the current sentence is:
Remember the Expand command expands the selection by the given Unit but no more or less even if you run the command twice in a row.
Omit paragraph mark
Word includes an ending paragraph mark after the sentence by default. In fact, Word will include every trailing paragraph mark (along with the empty paragraphs) until it encounters some regular text. This behavior isn’t intuitive to me, so let’s get rid of them.
Remove one paragraph mark?
The vast majority of the time, there will only be one paragraph mark, so the earliest version of the move sentence macro removed a single paragraph mark character using the MoveEnd method.
MoveEnd literally moves the End position of the range by the Unit size given, and a negative count value indicates the End position should be moved backward in the document.
Remove all paragraph marks
Better though, we can remove any number of paragraph marks with a single command using the MoveEndWhile method.
This command keeps moving the End position of the range backward (with the Count option) as long as it keeps finding paragraph marks. Remember vbCr is a special character for a paragraph mark in Word, so we set that character search option Cset equal to it.
In my opinion, omitting paragraph marks from a sentence selection is more intuitive and consistent with the meaning of a sentence within a paragraph.
Original macro sentence selection steps
Put together, the steps to span the initial sentence range are:
This sequence occurs only once at the beginning of each move sentence macro, but it can easily occur in other macros where we want to accomplish related tasks such as copying or cutting a sentence.
This wouldn’t be a bad function all by itself since it encapsulates several intuitive steps, and we can run them as a single command in whichever macro we need.
If this is all you want, see the simple version of the macro below. However, as an author using macros to edit faster, we can do better.
Allowing dialog
I like my macros to work as intuitively as possible (I suppose within reason based upon the work involved). They shouldn’t work against or frustrate me by making me clean up text after they finish. They should just work.
In novels, sentences are often included inside double quotes, so if I’m moving dialog sentences around, I want the macro to naturally interpret whether the dialog is a single sentence or just one of several inside the double quotes.
A similar effect occurs with parentheses in novel notes or work documents. I generally want to exclude a parenthesis if it occurs only on one side of a sentence. For the remainder of this article, I’ll work with double quotes since the logic extends trivially to parentheses.
Use double quote functions
To make this macro easier to read, I’ll assume you’ve implemented the double quote functions in a separate article. In this function, I will refer to the respective left and right double quote characters as LeftDQ and RightDQ knowing these are functions that will return the correct character.
Why?
Left and right double quote characters are different in Word for Mac or Windows systems, so the mentioned functions will give us the correct characters without having to think about it. Both functions are simple, and I will not need to repeatedly rephrase the commands below depending on an assumed operating system. Plus it just looks much nicer.
If you prefer not to use these functions, see the comments after the final function below for the respective standard character references.
What is the dialog situation?
Dialog can occur in various cases, so how do we handle the double quotes? The following includes examples with corresponding explanations.
No dialog
Example: This is a regular sentence in a novel. Another regular sentence follows that one.
The function should just span the whole individual sentence range like normal, so our new steps shouldn’t mess anything up.
Single sentence of dialog
Example: “This is dialog text inside a pair of double quotes.”
Since the sentence is entirely dialog, the function should span the whole sentence range including the double quotes on both sides.
Multiple sentences of dialog
Example: “This is the first sentence of some dialog text. Another sentence follows it inside the double quotes like this.”
Regardless of which sentence is picked, it would have at most one double quote bordering it. The function should span only the current sentence range and exclude the beginning or ending double quote as appropriate.
Summary
So, the double quotes should be included in the range only when they are on both sides of a single sentence.
Hmmm.
See where this gets a little tricky? Glad you have me along for the ride?
Trim spaces
We first trim any spaces from the end of the range, so we can properly test for double quotes. Word never includes (to my knowledge) any spaces on the left side of an automatic selection, so we’ll just trim any spaces from the right side of the range.
Wait a second …
This uses the same MoveEndWhile command as before, even moving backward, except it removes spaces now.
Just combine with removing paragraph marks step
We could just combine it with the earlier command by including a space along with the paragraph mark in the Cset character search string:
We’re adding the two characters together to create a revised Cset search string. This is called string concatenation which we explained in more detail in a previous article.
Get first and last characters
Now we need to get the characters at each end of the range using the characters collection.
Remember the First character is actually a range, so we need to reference its Text property for the actual character. We store this in a variable using the equals = sign for later use.
Similarly, we get the last character of the range.
Presumably, the range is not empty, but the logic below still handles everything correctly even if so.
Remove left double quote
To start the process of removing the left double quote, if appropriate, we compare the FirstCharacter to a left double quote.
First condition
The initial condition is whether the first character is a left double quote. Specifically, the True-False (Boolean) value is:
Not assigning a value
We’re not assigning FirstCharacter the value of LeftDQ. It’s a True-False value to be used in an If statement below to make a decision.
It is unfortunate that VBA has overlapping notation between assigning a value and determining a True-False condition (some other languages use double equals == for True-False values), but we’re stuck with it.
So, the If statement is:
But this isn’t quite correct because we want to keep the double quotes if they are on both sides of the range.
Arghhh.
Second condition
So, if it’s on the left but not on the right, then remove it from the range. The second condition is:
We include the preceding Not statement because we want the opposite True-False value of LastCharacter = RightDQ.
Compound condition
For the compound condition, we use “And” between the individual conditions meaning both must be True to give an overall True result.
Omit left double quote from range
The command to remove the double quote from the left side of the range is:
The MoveStart command moves the Start position of the range one character past the left double quote without changing the End position (unless Start exceeds End). The default is to move by a Unit of wdCharacter (included to be clear), and the default Count option is by 1 Unit.
Resulting steps for left double quote
So the If statement to remove a left double quote when appropriate is:
Remove right double quote
Similarly, we may need remove the right double quote.
We initially check the LastCharacter, but similar to the previous case, we need to make sure the left double quote doesn’t exist before we remove the right one from the range.
Omit right double quote from range
The command to remove the double quote on the right side of the range is:
The MoveEnd command moves the End position of the range without changing the Start position (unless End precedes Start). The negative Count value moves backward in the document by 1 Unit.
Resulting steps for right double quote
So the If statement to possibly remove a right double quote, if appropriate, is:
Allowing parentheses
We can do the same thing for parentheses simply by swapping out the respective left and right characters in the above conditions.
Both of these are a preference, but they are definitely convenient as you use the macros more.
Include ending spaces
Since Word normally includes space on the end of a selection, our range function should probably mimic that behavior. That’s what users are probably expecting in Word even if they’re not aware of it.
Now if the user selects the returned range, it will look normal.
Gotchas
We should get in the habit of considering potential problems.
What if the range ends up empty?
During the function, we remove several characters from the range: excess spaces, paragraph marks, and possibly a double quote or parenthesis. If the range ends up being empty by the time the macro finishes, is that a problem?
Not really because we will just exit the function as normal and return the empty range. In fact, the returned empty range serves as a clue to the user that something did not follow through not as expected, but this function isn’t responsible for correcting anything like that.
Basically, there doesn’t seem to be a problem here, but that doesn’t mean we shouldn’t consider it when creating the macro because sometimes issues like this can return to bite us.
Simple Function
The simpler version of the function without considering double quotes or parentheses is:
This version just acts like a regular sentence range with the exception that it omits any ending paragraph marks that Word likes to include. I find this behavior more intuitive for sentence ranges/selections.
Final Function
Putting it all together, the function to get the current sentence range while accounting for dialog or parentheses is:
These functions require the LeftDQ and RightDQ functions given in a separate article. These additional functions return the correct double quote characters regardless of whether you’re working in Word for Mac or Windows.
If you prefer not to use the double quote functions, replace every LeftDQ with a Chr(210) on Mac and Chr(147) on Windows. Similarly replace every RightDQ with a Chr(211) on Mac and Chr(148) on Windows.
We could combine the double quotes and parentheses character comparisons (the same steps are inside the If statements) before we shrink the range, but the compound If conditions start to look quite cumbersome. It’s not really necessary either.
What are the uses?
Clearly this function is used with the aforementioned move sentence macros. Where else might it be used?
We’ve previously created a delete sentence macro, and we could simplify and generalize that macro at the same time. We could further create macros to select, cut, copy, and italicize (for internal dialog, but probably with some modifications) sentences. These macros would be nearly trivial to create now that we have this workhorse function.
Improvements?
Given the messy conditions and the similarity in the steps, we could further extract the steps that remove the double quotes or parentheses when appropriate into a separate function. This would condense and even generalize the steps.
It’s not a bad idea, and I would probably do so with my own macros. This would be mostly for aesthetics though, so I’m leaving it out of the public macros …
But I can’t seem to help myself.
Character Removal Function
I tried to leave this function out, but the perfectionist in me wouldn’t cooperate. Here is a function to encapsulate the double quotes omission from the working range. It’s presented without explanation other than the comments in the macro.
For those that are unsure about the working range r appearing inside both functions. It will be treated in each function as a different range. This is called the “scope” of the variable, but more explanation is beyond the scope (no pun intended) of this article.
If you’re curious, the “Xor” suffix in the function name refers to the Boolean operator Xor. It’s like Or, but it’s only True if one of the conditions is True, not both like with the Or operator.
Declare range
In order to use the range with the function, we need to declare it explicitly. Otherwise, we'll get an error when we try to pass it to the above function.
We didn't need to do this previously because VBA will infer the type of the variable (using a generic type), but VBA is pickier when we send data to functions.
Revised Final Function
Let’s see if you like the result and agree it might be appealing to write additional functions to clean up your macros.
Just by extracting the steps to exclude a double quote or parenthesis, this version is much easier to read. This version looks so much cleaner than the main function. The tradeoff is we have functions within functions.
Is it worth the extra work?
It’s not really that much extra work, and I cannot assert the extra function will be generally useful in other macros, but I still prefer the cleaner looking macro.
Only you can decide if you like it.