Natural language commands complement our writing process by allowing the computer to accomplish tasks for us more efficiently. Dragon Professional includes some natural language voice command features out of the box, but they need a boost with more flexible command phrasing as well as improvement in how Dragon carries out task details.
Thanks for your interest
This content is part of a paid plan.
Basic Keyword Voice Command Searches in VBA using Like
So, you have a nice script for your new voice command. You’ve even incorporated a list variable or two to generalize it (see related article), but then your mixes get worded up when you say it and …
Huh?
Nothing happens.
Arghhh.
Sure would be nice if we could bake our own …
We can. At least with a little VBA elbow grease and some attention to detail.
Toward that end, we’ll extend a previous custom Dragon script to allow a wider variety of phrasings when invoking a voice command. In the process, we’ll also gain more detailed control over how the commands are performed because we can use our own Word macros to accomplish any necessary tasks.
Getting started
This article leans on three previous functions:
- We introduced a function to parse important command keywords like document elements and direction
- A second article parsed numbers in a text version of a spoken command.
- A third article created a function to more easily run Word macros from a Dragon script.
Many voice commands are relatively simple grammatically speaking, so it’s easier to parse them using simpler search tools like Like. No sense breaking out a pickaxe when a toothpick will do. We will, however, branch out into using regular expressions (regex) for more detailed command scripts in upcoming articles.
We’re using Dragon Professional advanced scripting for the voice commands since Dragon Professional integrates nicely with Microsoft Word, and both applications use VBA for their scripting/macros. Dragon Professional is a paid application with a significant sticker price, but I think it’s worth the investment since I make a living with my fingers … and hopefully my voice as I move forward. Just don’t ask me to sing.
Why bake our own?
Doesn’t Dragon Professional already do that?
Yes, Dragon Professional scripting has some natural language voice commands out-of-the-box, and it allows more custom commands and scripts to be created within its command browser. However, there are limitations.
Command word limitations
With Dragon, every word order variation must be its own custom command. List variables allow word categories like direction or different document elements (although the categorizations are not enforced) providing some generality and flexibility, but the word placement must be preserved in the spoken command.
Words also cannot be omitted. We can sometimes get around this limitation with expanded list variables, but it only works for certain variations. If there is a list variable present, you must say something at that position in the command.
If we wish to expand on the phrasing variety to make it more natural, we’re stuck with duplicating commands with small phrasing changes. They’ll multiply faster than you think, and keeping track of which ones are similar or different can be confusing if you need to make any changes. As your list of custom voice commands grows, including steps to distinguish between different versions in a single script is better than having multiple slightly different custom scripts.
Examples
By the time we’re done, all these voice commands (and more) in Dragon Professional will run from within a single custom script.
These commands would require at least six custom script variations in the Dragon Professional command editor with only minor changes between them. It’s not the end of the world but keeping up with and updating so many nearly duplicate scripts with any changes is annoying.
Just six or seven command variations ... who cares?
It also makes sense to implement cut, copy, or delete versions. Now you're up to at least two dozen scripts just getting started.
Controlling the details
When baking our own command interpretations, we can also control all the details of how the task is performed in Word rather than relying on Dragon Professional’s stock treatment. I suspect you’ll eventually like that more than you realize.
For example, you might want your own sentence manipulation macros to exclude double quotes when working with dialog in a novel or parentheses in other documents.
I’m also not a fan of how “Select the next six words” and similar natural language commands are implemented literally by Dragon. When editing a document, “most people” (pardon my generalization) would consider the selection to start with the current word not the literal previous or next word (at least to me; I even researched it, and that seemed to be the consensus). I’ve done a lot of math and programming where logical statements are strictly evaluated, but the literal interpretation when editing was jarring for me. I had to move over a word before invoking the command. Ughhh.
Create parsing script skeleton
Since we covered this in more detail in a previous article, we’ll be brief here.
Dragon’s advanced scripting editor is mostly a plain text editor where we type commands just like in Word VBA macros. The Dragon editor has fewer features than the already meager Word VBA editor, but it does the job, albeit with some idiosyncrasies.
Open the script editor
Open the command editor using the Dragon Bar menu: Tools → Command Center → Add new command …
In the middle, open the combo box for the Command Type and select Advanced Scripting. This will add some lines to the script editor below.
What you’ll see
After selecting the new command option, it’ll create a default empty VBA script like the following in the script editor.
Dragon includes Option Explicit by default which means we must declare all our variables (no implicit definition). Our script steps will begin under Sub Main.
Examples with more natural language voice commands
Let’s begin with a simple example. Suppose we wanted to act on a command phrase like:
Dragon will already carry out this command, but we're building something. We might as well make it general purpose and allow any of the following:
And others.
What about list variables?
If these were the only cases of interest, a list variable in Dragon’s custom command editor would handle these commands easily and more clearly. For example, we could create a command named:
We then store the list of potential document elements in the <element> list variable and use the spoken word identified by Dragon when it’s used to run the appropriate macro within the script. See this previous article if you prefer a list variable approach for your problem.
Hang on though because we’re building something.
Static portion of the command
“Select that” will be static part of the command for now, so we only need to focus interpreting the last word(s). Later we’ll make “that” a list variable to increase the command flexibility by allowing “this” or “those” as well as other options.
Dragon voice command name
With this tentative voice command, the name would be:
Dictation list variable
We’re parsing the command ourselves, so we use a <dictation> list variable. The <dictation> list variable does not correspond to a list of words or phrases like other custom list variables. Instead, it encapsulates everything a user says after that point in the command. With that in mind, it must be placed at the end of the command name.
Get the command text
We need a way to store the words said for the <dictation> portion of the command, so we can determine what was said inside our script.
Store the dictation text
Any list variable in Dragon Professional is plain text, so we store the result in a String variable named sPhrase.
I like to precede my String variables with an “s” just to remind myself what type of variable it is, but this convention is not required.
Store the dictation phrase text
Now we store the “dictated” command text. Since <dictation> is only one list variable so far, it occurs in the zeroth position of the context values.
The zeroth (0) element of ContextValue corresponds to the first list variable in the command. Counting indices starting from zero is common in programming, so we’ll need to get used to it.
This notation isn’t pretty or clear (to me at least), but this is how we access the words or text in list variables.
Parse document element
Let’s assume we’re storing the relevant document element in a string variable called sElement.
How do we parse the command?
Brief review of Like
We’re looking for certain document elements when spoken as part of the command phrase. We stored the phrase above in a text variable sPhrase. A quick example of using Like to search is for specific text looks like:
Like compares the search text on the left sPhrase to the search pattern on the right "paragraph". It returns a True or False value based on whether the pattern is found within the search text. The asterisks * are wildcard characters that tell Like to allow any or no characters on either side of our keyword.
Like only tells us whether a match was found, so we must separate various conditions into different document elements using conditional statements. Once we identify a match, we can assign the found element in the command to a variable for later use.
We can add other cases to the above conditional statement using ElseIf statements. See our brief review of conditional statements in VBA.
Document element parsing function example
We spent a whole article breaking down how to do this, so we’ll refer to that one and just use the result here. Specifically, the function to get the stated document element is GetPhraseElement.
The first argument should be the command phrase which is the sPhrase variable for us. The second argument is a default element as plain text. If we wish, we can specify something like a “paragraph”, but this argument can also be omitted. If so, it will default to an empty string “”.
We want to store any function result in the aforementioned sElement variable, so we literally just use an equals = to assign the returned document element from the function to our text variable.
What do I do with it?
We can use the identified element to run a specific Word macro related to that element. With simpler tasks, we could make the change directly from within Dragon Professional using a Word application object (don’t worry if that makes no sense at the moment), but I prefer to just keep all the macro activity inside Word and use the Dragon script as more of a manager than a worker.
That is, we'll define all macros to carry out specific tasks in Word, so the Dragon command scripts just tell Word which macro to run. I think that is the cleanest approach that allows the least room for unexpected problems.
Which macro?
Which Word macro do we run?
We’ve identified the element above, so we need to select an appropriate macro to run. We’re not covering any details of specific Word macros here. We just want to identify which one we need and run it from our Dragon script.
Previous script to run a Word macro
We previously introduced a script function to run a Word macro using a voice command. The function name is RunWordMacro which takes the macro name to run within Word as plain text. For example, if we want to run the macro named SelectSentence, we would use the command:
The first parameter is the macro name in plain text or a String variable that stores the name. The second parameter is optional, but it corresponds to a possible macro parameter. Not all macros will require it, but when used, it allows us to send a bit of information to the macro we’re running. For example, we’ll use this parameter later to specify the number of paragraphs to select.
The function returns a True or False (Boolean) value based on whether it was able to run the Word macro, but True does not directly indicate whether the macro successfully executed its task in Word.
This function will also catch an error when trying to execute the macro, so the overall script doesn’t crash.
Unfortunately, given no library exists in Dragon scripting, we need to copy this between scripts. This function allows us to easily run any Word VBA macro we have from within a custom Dragon script.
Tentative Word macros
For our purposes today, we’ll assume our Word macros are named:
With more variations as desired.
Each macro acts much as its name suggests. One exception is when selecting sentences, our version will account for double quotes or parentheses.
Selecting the document element
We need to select between at least four different possibilities. This can be done with If statements, but that’s a little messy.
Select Case statement basics
That’s why Select Case statements were invented. A simple version of a Select Case statement is:
Let’s break it down.
Select Case just defines the start of the command. Then VBA checks the value of SomeVariable we provide. If it has a Value1, then the steps underneath that Case are run. Otherwise in this example, if SomeVariable has either a Value2 or Value3, then the steps under that Case are run.
If no matches are found, the Case Else steps are run, but the Case Else can be omitted, if desired. If no matches are found and no Else part exists, then nothing happens.
We can get fancier, but this statement allows us to make decisions more concisely when there are many options in a script or macro.
Selecting a macro for our document element
For our current toy problem, we’re selecting the macro based on the identified element. We then store this value in the sElement variable.
Our current Cases are: “paragraph”, “sentence”, “heading”, and “heading content”.
Select Case statement to run a macro
Putting it together for each tentative macro to run, we have:
This Select statement combines at least four different macros under one concise voice command.
Notice the cases "heading" and "heading content" are separated. One might wonder if these would overlap, but string comparisons require exact matches to be equal, so these are different cases as far as the Select Case statement is concerned.
Error message
I included an error message in the Case Else because I don’t want to run any macros if I don’t identify a specific command. You can omit the error message if you wish, but it is convenient to know if something went wrong which is especially true when you’re creating the script.
MsgBox function
A standard function to pop up a dialog box with an error (or any kind of) message is the VBA function MsgBox.
MsgBox works in both Mac and Windows, but Dragon Professional only works on Windows.
Adding text strings
We can add strings together to create a more specific message like:
I used a plus + sign to add the sElement variable as a String to the plain text note in double quotes. It mimics addition operations with numbers, but it’s called concatenation when you’re working with strings. Essentially, we're just joining the two strings into a new larger string. We could also use an ampersand &. There is a slight but subtle difference between the two operators, but we don’t care about them here.
The actual error message in the function below is a little more complicated.
Warning on error messages
Assuming you’re not a perfect person and don’t always do everything right the first time, don’t be skimpy with your error messages since they are a significant firewall against unforeseen errors. A few weeks ago, I lost a day of work because I skimped on a clear and detailed error message for a better version of the RunWordMacro function mentioned earlier. In the end, the error was almost trivial, and it would have easily been detected the first time it occurred … if I had included a more descriptive error message.
Baby Script … do, do, do, oooh
Putting all this together, our baby Dragon Professional custom voice command script is as follows:
Remember, we’re building something here. If this was all we could do with command parsing using keywords, then we would just use a list variable in the custom voice command which would require far less effort.
Improved natural language voice commands using keywords
The point is to create better “natural language” commands, so you don’t interrupt your workflow when writing or editing. You’ll be surprised how much your wording may vary when you give a voice command in real time.
Suppose we want to act on a more general command where we say something like the following:
In today’s script, we’re not worried about the details of the Word macro that carries out this command. We’re trying to interpret a text version of the voice command and select the correct Word macro to run.
New command information
This new example command provides four pieces of information.
- The static part of the command “Select …” which indicates something should be selected.
- We want to select a “paragraph” document element as we did with the baby script above.
- The first bit of new information we need to interpret is an implied direction with “next”.
- The number of paragraphs was specified as “three”.
Fortunately, we’ve previously created several functions to facilitate the last three items (see Getting started section at the top).
The versus That
Our previous example command used “that” as the second word, but it is also quite natural to say “Select the …” as is done here. We need to allow these simple variations.
Which list variable
To solve this problem without creating duplicate commands, we can create a new list variable to allow any of several convenient articles or prepositions. I called my list variable <which> which contains words like:
the
those
these
that
by
to the
by the
of the
and several more variations. Each word or phrase should be placed on a separate line. We also don’t need to restrict ourselves to individual words as shown by “to the” and below. Some variations like “of the” apply more when the list variable is used later in a command (see earlier example commands).
List variables are nice but a little bit of a mixed bag in my opinion. They extend the utility of custom scripts without having to create redundant versions, but they are a little awkward to manage. See this previous article for more about using list variables in Dragon Professional advanced scripting.
Revised command name
The new custom voice command name is:
In the command editor, we literally type this as the voice command name.
The static part of the voice command must be stated exactly to run the script. Dragon will automatically detect any of these <which> variations as the next word(s) in the command. Then anything after that is stored as the <dictation> text.
Revised command phrase assignment
We previously stored the <dictation> command text in the sPhrase variable in order to parse it for the relevant information. One small change to the above baby script is <dictation> is now the second list variable. We need to revise the command phrase assignment to reference element (1) of the context values.
Remember the list variable context values are indexed starting from zero which is common in programming, so (1) is the second list variable. We do not care about which word was used for the <which> list variable. It’s just present to allow the natural variations at that position in the command.
We don’t get everything we want since we must state something at that position. We can’t omit a word in the command unless it falls within the <dictation> variable text.
Get document element with a function
As with the above baby script, we get the document element using the GetPhraseElement function from a previous article. We “pass” the function our command text sPhrase and store the result of the function in the sElement variable.
We decline to specify a default element type as a second argument since the document element is a required part of the command if we are to properly interpret the instruction.
Get the direction with a function
We also previously introduced a GetPhraseDirection function to automatically parse a stated or implied direction from the text version of the voice command. We’ll pass the function our command phrase and store the result in a String variable named sDirection.
The function normally returns “forward”, “backward”, or a default empty string “” if no direction was found in the command. We overrode the default value for clarity using the second function argument “forward”. It’s not strictly required, but we probably need at least an implied direction. Using forward as a default value makes sense in this script.
Get the stated number with a function
We previously introduced another function GetPhraseNumber to parse a stated number from a text command. We’ll store the result in a number variable named nElements.
We define nElements as a Long value (basically a bigger Integer or “counting number” in VBA) corresponding to the number type returned by the function.
We pass the command phrase to the function and store the value in the nElements variable.
If no number is stated or implied, the default value is zero. Any stated number is assumed to be positive in the function, but we’ll account for the direction next.
Account for direction with the number of elements
In Word VBA, backward in the document almost always corresponds to negative values and forward to positive in various VBA commands. This makes more sense when we remember the document grows (more characters) as we add content.
The sign of the above nElements number needs to be adjusted based on the direction specified in the document. If the user indicates forward direction in the document, our nElements variable is already positive, so we do nothing. On the other hand, if the user indicates a backward direction, we make nElements negative.
We’ve already identified the direction and stored the result in the sDirection variable. Since we assigned one of two possible text values to sDirection, “forward” or “backward”, we can check for a match directly without using a search comparison such as Like.
Using -nElements is the same thing as multiplying by -1. If no direction was given, then nElements is zero from the GetPhraseNumber function. “Negative zero” is still zero, so nothing happens if so.
Since the command is so short, we can shorten it into one brief line:
I tend to prefer this more concise notation when it’s clear.
Command keyword summary
Let’s collect the four keyword assignments based on the respective functions.
Based on keywords, we’ve parsed the command for a mentioned document element, any direction, a possible document location, and a stated number. These keywords account for many voice command variations without binding us to any specific command phrasings.
These short four steps do so much while also being easy to understand. They’re even more convenient when we reuse them in other scripts without having to reinvent the wheel every time.
Selecting the correct macro to run
There are several ways to select the correct Word macro to run. Assuming the command category is obvious (move, select, copy, etc.), we have three main pieces of information: the direction, a possible location, and how many elements (paragraphs, sentences, etc.) to select.
Messy macro selection … (not used)
The baby script above only considered the document element, which was stored in the sElement variable, to pick the correct Word macro to run. Our extended version is a little more complicated since the command might restrict the selection to a specified direction or only to the “start” or “end” of the stated document element. The command may further specify a number of elements.
Uhhh … where are you—
Hey! Get back here.
It’s not as bad as it sounds. Plus, I’m just making a point here about why I’m using simpler macro selection process below.
Start with the location for macro selection
A logically simple approach is to nest the decisions. That is, decide one part and then decide the specific macro based on that branch. There are a few ways to structure it, so we’ll start with the location and work toward the other elements.
Some of my Word macros are targeted specifically toward the beginning or end of a document element (paragraph, heading, etc.). This corresponds to our location variable sLocation.
We can use a basic If statement with the location whether it's the "start", "end", or anything else (usually an empty string ""). Our conditions look like:
These are intended to be True-False (Boolean) conditions as they’re used below. That is, does the plain text stored in sLocation exactly match “start” or “end”? It would be preferable if these two conditions didn’t read exactly like a variable assignment (store the text value “start” in the variable sLocation) even in isolation. Some languages instead use a double equals sign == for comparisons, but VBA distinguishes them by context. It's not the clearest approach, but we’re stuck with it.
We construct an If statement based on these two potential locations.
It’s not too bad so far. It’s no more complicated than what we’ve done in previous scripts or macros, but it gets messy quick.
Include the document element in the macro selection
Our previous decision based a macro decision on the document element. It used a Select Case statement something like:
Unfortunately, the correct element macro to run will be different for each location. We need to insert a copy of this skeleton into each part of the above location If statement:
Ughhh … see what I’m saying.
I don’t even like looking at that mess much less trying to figure out what it’s doing. Perhaps in another macro this lengthy blob of VBA might be the best solution, but let’s find a better way to solve our current problem.
Flattened Case Selection
The above macro selection process would’ve worked, but it would have “nested” the conditional statements into a two-level decision tree. If that’s the only solution, okay, but it’s a little confusing and harder to follow than just a sequential list of cases.
We can make it easier to read if we create our own little concise baby command using the various keywords we’ve identified above. Then we use that constructed command to choose the correct Word macro with a simple, albeit longer, "flattened" Select Case statement.
Command variable
We first create a plain text variable called sCommand to store our abbreviated command.
Create the simple abbreviated command
The main information to construct our command is the stated document element followed by the location, if available. The easiest thing to do is to just start the command with the identified element.
Now we check whether the location exists and add it to the command. If sLocation is not an empty string “” (the default value returned from the GetPhraseLocation function), then we add the location to the command.
Remember <> is the not equal symbol in VBA (literally less than or greater than). This symbol along with the equals = sign are used mostly with number comparisons, but they also work for exact text comparisons.
Alternative approach (just use one)
If you don’t like using not equals <> to compare text strings, then you could just test for the value of sLocation. Notice “start” and “end” both have the same command structure. That is, either could be True, and we add the document element and the location variables. That corresponds to an Or compound conditional statement.
Only when sLocation is empty does the command change. Thus, we can construct an alternative abbreviated command assignment. Putting it into a conditional statement, we have:
But only use one of them. I prefer the former because it is more concise, but not everybody likes mint chocolate chip ice cream (yum) either.
Note also we could not just always add them. If no location is given, sLocation would be an empty string "" which would add nothing. That seems okay at first glance, but then our command phrase might be something like "paragraph ". The extra space at the end would not be an exact match for "paragraph" as listed in the Select Case statement below.
Leave out direction and number from abbreviated command
We don’t include number variable nElements or the direction variable sDirection in the above abbreviated command because we’ve already accounted for the effect of the direction by changing the sign of nElements when it’s used. Specifically, negative values of nElements correspond to backward selections, and positive values are forward in the document.
Example abbreviated commands
Suppose our document element is a paragraph. This will create three possible abbreviated commands:
Of course, we have variations for sentences, headings, etc. as desired.
Since we’ve set the sElement and sLocation variable values to specific values, we know what any valid abbreviated commands must look like. This allows us to construct a simpler decision tree below that is flat and easier to read than the nested version above.
This isn’t the only solution to the problem. It’s just clean and relatively easy to understand even if it takes a few extra steps to set up.
Parsed command examples
We also have more variations for sentences, words, and headings. Let’s practice a few examples to be clear.
Example 1
What if we stated the following voice command?
The variable sCommand would store the value “heading end”, and nElements would have a value of zero because no number was mentioned.
I’m not assigning these values here. I’m just illustrating what the values would be based on all the above steps.
A direction is implied with “end” in the voice command, but the selection macro handles that detail, and changing the sign of a zero in nElements would not matter.
Example 2
What about this command?
The command values are:
Here “word” is clearly mentioned, and the stated number 5 is negative because “previous” was given which is backward in the document.
Example 3
Another one:
The command values are:
Here paragraph is clearly stated, but no number was given so nElements defaults to zero.
Selecting the Word macro
We’ve constructed our command, so now we’ll use a Select Case statement to pick the appropriate Word macro to run. A Select Case statement is better than an If statement here because there are about a dozen options depending on which document elements you include. The code will still be long, but a Select Case statement allows us to organize them more clearly.
Select to Start of End of the current paragraph
For the start and end of the paragraph, we’ll assume we have a simple macro call to make.
These use the previously presented function RunWordMacro, or see the earlier section for brief explanation. We’re not covering the details of any Word macros mentioned in this article.
We needed to include “Call” because we’re providing (called “passing”) and argument to the macro. The returned function result (a Boolean value indicating whether the function ran successfully) is ignored since we don’t store it in a variable.
Select current paragraphs
The SelectParagraphs macro has a parameter to indicate the number of paragraphs to select. We previously stored that value in the nElements variable, so we add that as a second argument inside the RunWordMacro parentheses.
If you have more complicated macros with more parameters, we’ll cover that function in upcoming articles. It’s a little more complicated.
A similar set of macros are needed for the other options with sentences, headings, etc.
Skeleton to select a macro based on the abbreviated command
We chose the Case based on our constructed abbreviated sCommand.
Selecting correct Word macro to run using the abbreviated command
Now insert out possible constructed commands as cases:
We can add cases for “sentence”, “sentence start”, and “sentence end” as well as those for heading, words, etc. as desired. It stretches out, but the overall macro keeps our collection of voice commands relatively easy to read. The point was to flatten the Select Case statement into one level rather than it being nested two levels.
Gotchas
What could go wrong?
Command conflicts?
With more command variations, there are more chances for command conflicts where the same voice command inadvertently refers to two different scripts. For example, I had a command that overlapped one of the default Dragon Professional natural language commands. It took me a while to realize Dragon was preferring its own native command over mine.
Arghhh.
I wish Dragon had a way to toggle individual commands to active or inactive, but I eventually disabled Dragon’s natural language commands entirely since I was writing my own (I like controlling the details).
As far as I can tell, Dragon Professional handles command conflicts well enough (except for the blip above), so it’s not a big deal, but it is something to keep in mind as you expand your command library. Having no conflicts is better than relying on Dragon to pick your intended command.
Number pronouns
The number “one” sometimes counts as a pronoun such as:
Three is the intended number of paragraphs, but one is a pronoun referring to the current paragraph. Our current number function would not distinguish between “three” and “one” in this phrase. It's not the biggest problem in the world, but a partial solution will be covered in upcoming articles.
Function results?
Some of the internal logic in the above script assumes specific standard values (such as “forward” or “backward” or “start” or “end”) or specific default values (such as 0 or an empty string “”) as the only possible results. The final script below doesn’t check for or validate every possible result before making decisions about which Word macro to run. This keeps this script smaller, but it does push us to make sure the possible function results and the script expectations are in sync. We guard ourselves against some mistakes by only running a macro when very specific abbreviated commands are detected.
A side lesson here is we should be clear and specific when we write functions. Clear input and output should also be explained in dedicated comments, so we can buttress our other scripts and macros against problems.
Final Keyword Command Script Using Like
The final script looks messier than it is logically. In this version, we use the above functions to parse the command phrase for specific information. Recall the main subroutine in a Dragon custom script is literally called Main.
The Getting started section at the top links to the functions used to parse the keywords. Breaking the script down into smaller pieces makes it more manageable and readable. The Word macros would be created separately, and this blog has several examples of such macros at different levels of difficulty.
Is it safe?
I get it. You don’t want something bad happen to your novel text. That sends shivers of fear through an author.
This final script is basically safe because the Select Case statement combined with the abbreviated commands only run a Word macro if one of the specific constructed commands is detected. Of course, this script does not guarantee the Word macro is correct.
What do we gain?
Exact wording is quite flexible
The nice thing about baking our own natural language command is just how flexible the phrasing can be starting from the <dictation> list variable.
We don’t have to worry about exact placements of the the’s, that’s, to’s, etc. except for the word immediately after “Select …” (that position is occupied by a list variable <which>, so it is required). Omitted words and even some word mangling will all work provided the proper keywords are present.
Other commands?
This script can easily be duplicated and modified to work for cutting, deleting, or copying the same document elements. You just need corresponding macros to exist in Word. Change the static part of the command to “Copy <which> …” for whichever variation you wish. Then make the trivial macro name changes in the Select Case statement of the script.
Controlling the details
I mentioned this at the top, but one of the best features in my opinion is we can control all the details of each respective task by using our own Word macros. We don’t need to rely on Dragon’s implementation of a natural language voice command.
Condensed voice commands
We could just create variations of the same macro for each phrase type, but this approach condenses the script variations into a smaller number of custom voice commands. This seems like “no big deal,” but your custom command list will grow faster than you think, and near-duplicate commands are annoying to keep up to date if you ever make changes.
Comments comment
I stretch the script with lots of comments because I like being extra clear. (Yes, I really do this in my own scripts and macros also but maybe not quite as many.) I try to include relevant information that might be important if I come back to the macro after months or more. Comments are more important for more obscure script steps, but I still recommend them for any significant step or related group of steps. The temptation exists to skip adding comments while you’re working on a script, but you will save yourself time in the long run if you or someone else ever returns to the macro or script.
One script to rule them all (natural language commands)?
Is this keyword approach the solution to all voice command problems?
No.
It’s just one attempt that is relatively easy to understand but also generalizes several voice command possibilities. I understand some may prefer dedicated commands for each type of document element, but as your list of voice commands grows, the consolidation is a nice side effect.
The above solution allows many more naturally stated command variations. We don’t have to worry as much about exact phrasing. Of course, this comes with a few issues, but it’s a win overall for improved natural language processing with less up front effort.