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

Navigate or select by punctuation improved

Word • Macros • Editing
Peter Ronhovde
28
min read

Improving document navigation will make our editing more efficient. In this article, we create several macros to jump or select to common punctuation marks reducing how often we need to click around with the mouse.

Thanks for your interest

This content is part of a paid plan.

Navigate or select by punctuation

As writers, we inevitably move around our documents many thousands of times. Improving the efficiency of the navigation and selection process will obviously help us write and edit faster. Word's GoTo dialog is of limited practical use if speed is an important consideration. Document voice control is improving, but it's still clunky at times.

We can't solve all the problems, but we can create some targeted macros to speed up our document navigation. Toward this end, we improve upon a previous set of macros focused on jumping or selecting to common punctuation marks, reducing how often we need to click around with the mouse (which is slower than it seems compared to tapping a keyboard shortcut).

Example of using a macro to jump to the next comma
Example of using a macro to jump to the next comma

The sibling macro selects all text up to the comma such as with an introductory phrase.

Example of using a macro to select to the next comma
Example of using a macro to select to the next comma

The bulk of the article will present the topic in the context of navigating or selecting to a comma, but the final macros can be trivially modified to work with any punctuation mark.

Simple Versions

If you didn’t see the previous article, the simpler macros are below. Check out that article for additional explanation on any overlapping points. Another article covers creating an empty macro if you are starting from scratch.

Jump between punctuation marks

Move forward to the next or previous comma, respectively, in the document:

Sub GotoNextComma()
' Jump to the next comma in the document
Selection.MoveUntil Cset:=","
End Sub
Sub GotoPreviousComma()
' Jump to the previous comma in the document
Selection.MoveUntil Cset:=",", Count:=wdBackward
End Sub

Select to punctuation marks

It only takes a small modification of the movement macros to instead select all text up to the punctuation mark. Specifically, we need to modify the Start of End of the Selection rather than just repositioning the insertion point location. Selecting forward or backward in the document, respectively, is done by:

Sub SelectToNextComma()
' Select text forward to the next comma in the document
Selection.MoveEndUntil Cset:=","
End Sub
Sub SelectToPreviousComma()
' Select text backward to the previous comma in the document
Selection.MoveStartUntil Cset:=",", Count:=wdBackward
End Sub

Common traits

VBA ignores any text on a line after a single quote ' character which gives us a way to make human-only comments in our macros.

I’ve created variants for several common punctuation marks like periods, semicolons, and parentheses; but additional enhancements are covered in this article. To be more concrete, we’ll refer to commas for the majority of the article below.

We can modify the Cset character strings (characters at which the command stops the search) in each case to search for other punctuation marks or even include multiple search characters.

I am still astonished at times how a simple one-line macro can be so helpful.

Why bother with more?

Suppose I just used one of the above macros to select all text up to the nearest comma. What happens if I tap the shortcut again?

Nothing.

The basic macro above won’t select any additional text since the macro immediately “finds” the comma and stops the character search. We would need to manually move past the comma (with Shift+Right arrow) before selecting over to the next comma. The same basic problem occurs with the movement macros.

Ughhh.

This doesn’t feel intuitive to me. It's like your VBA pet is happy, wagging its tail and drooling on the floor while saying, "See what a good job I did for you! Here's your stick (comma)!" But you're like, "No, go fetch another stick!"

It feels more intuitive to extend the selection over to the next comma since I already know I'm at this comma. That's what we want to fix. We need to teach our dog the improved trick. The macro should realize we're already at a comma and extend the selection to the next comma something like this:

Example of extending the selection by commas
Example of extending the selection by commas

That’s one of the macro superpowers. If we’re willing to do some work up front, we get exactly what we want. The necessary changes are mostly easy to implement. Unfortunately, we need to avoid a notable gotcha along the way, but that’s why I’m here to help.

What is the Selection?

As a quick review, every macro above uses the Selection (or see our review for more information). It is VBAs representation of the selection we actually see in the document. It can refer to actual spanned document content, or it could refer to an empty selection called an insertion point (the blinking I-bar waiting for you to type). It is partly defined by its Start and End positions indicating the spanned content in the document, but it also contains other data (properties) and actions (methods) we can use to manipulate it. Only one Selection exists per document. We're ignoring block and non-contiguous selections since VBA does not play nice with them.

We often avoid using the Selection in complex macros since any changes to the Selection are (usually) immediately visible on the screen, but these macros are so short, we would likely never notice the rapid changes.

Basic planning

We're working through four related macros in this article. Individually, the details are relatively simple, but they accumulate to the point it can be confusing, so let's plan a bit with the higher-level concepts.

How do we get to the next or previous comma?

While the above macros are simple and a great place to get started, none of them work in this more intuitive manner without modification. What are the necessary steps for the improved macros?

When run, the improved macro needs to do the following:

  1. Detect whether a comma already exists beside the insertion point (i.e., the edit location at the blinking I-bar) or the appropriate side of the selection.
  2. If a comma is already present, move past it or extend the selection by one character.
  3. Jump or extend the selection over to the following comma (works whether or not a comma was detected first).

Seems simple enough … we don’t always need to write everything out like this (I’m writing an article after all), but thinking through specific steps helps ensure we aren’t missing any obvious or less-than-obvious steps. It also gives us our initial opportunity to catch any potential issues. The steps aren’t difficult, but the specifics can get a little tricky, and they're often trickier than you think at first glance.

What is the previous or next character?

What character are we using to determine whether we skip over a comma? It's mostly obvious, but for completeness, we want these characters for each case:

  • No selection → Get the character immediately before or after the insertion point
  • Selection → Get the character immediately prior to or after the selection

This choice is where we hit a sticking point with the forward macros. We need a way to detect the selection status and pick the correct comparison character when the macro is run but more on that later.

How do we handle an initial selection?

What do we do with an initial selection? Some variation will exist across the macros, but we would need to do the following based on the initial selection state when the macro is run:

  • Go to previous comma → Collapse the selection toward the start and then move
  • Go to next comma → Collapse the selection toward the end and then move
  • Select to previous comma → Extend the selection backward from the start
  • Select to next comma → Extend the selection forward from the end

In the original macros, these actions happen automatically with the respective move until commands, so that saved us some work. However, our enhanced version needs to detect and adjust for the previous or next comma whether or not a selection already exists. This seemingly simple adjustment ends up being a little mess based on how VBA views the Selection.

Move and select backward in the document

Working backward and forward in the document are different for this set of macros. Moving or selecting backward is easier, so let's start with that.

Check for a previous comma

We need the previous character to check whether it is a comma. Fortunately, the Selection already has a Previous method.

Selection.Previous ' Not done ...

This gives us the document Range of the previous specified element. We usually need to give a document unit from the WdUnits constants table, but the default is a character which is already what we want, so we can omit it in this macro (it clutters the presentation anyhow).

What is a Range?

See our brief introduction to Ranges in VBA for more information, but concisely stated, a range is a span of document content like an invisible but distinct selection. Much like the Selection, a range is partly defined by Start and End positions in the document, but it also contains a bunch of other properties and methods we can use to manipulate it.

Get the previous character

The trick is we don't want the range of the previous character. We want the plain text in that range, so we refer to its Text property.

Selection.Previous.Text

This command tells VBA to give us (often stated as “return”) the next chunk of text prior to the Selection in whatever unit was specified. The unit defaults to a single character which is what we already want, so we can leave it out for this macro.

This command returns a plain text character (called a "string" even if it is only one character long), so we could just use it as it, but it reads better if we store it in a separate variable.

Dim PreviousCharacter As String
PreviousCharacter = Selection.Previous.Text

The Dim statement tells VBA what variable type to expect, but it is optional in standard VBA. I tend to include it in more complicated macros on this site for completeness. I don't like that the statements make the macros look more like programming, but they will eventually save you from some trouble somewhere down the line.

If we were to refer to the previous character often, this variable would also be a tiny bit more efficient.

Check the character

We need to check whether this stored character is a comma. If you've been following along, this is done in VBA (and programming in general) with a conditional statement something like "If ... Then" (see our brief review for more details). In rough "pseudo-code" this looks something like:

If previous character is a comma Then
' Move backward (or select) past the comma ...
End the conditional statement

Character check condition?

How do we detect a comma? We literally just need to compare the previous character to a comma as a plain text string ",".

PreviousCharacter = ","

The double quotes are required around the comma to tell VBA we're working with the character. Otherwise, VBA would interpret it as part of a longer command which would cause an error.

This comparison works because a comma "," is just a literal value in VBA much like the number 3 (but there are some differences). PreviousCharacter is storing a single character, and we are using the equals = sign to compare it to a literal comma. That is, we're asking VBA whether they are the same string. For two strings to match, they must be exactly equal, no more, no less, no difference in case, no extra or fewer spaces, etc.

Unfortunately, this notation also looks like we're assigning a comma "," to the PreviousCharacter variable, but VBA interprets it as a True or False (Boolean) condition when used in a conditional statement context.

Move past the character

How do we move over the comma? We literally just move one character left in the document using the Selection's MoveLeft method.

Selection.MoveLeft

The default step size is a character unit, and the default count is one unit, so we can omit both. Methods like MoveLeft are one of the differences between a Range and the Selection since Ranges do not have several of these specialty move methods.

Remember, the dot "." with VBA "objects" tells VBA to reference the MoveLeft method associated with the Selection. In this case, it means perform the action of moving one character left.

Conditional move step

Combining these steps, we have the more VBA like conditional statement:

If PreviousCharacter = "," Then
' Move backward past the comma
Selection.MoveLeft
End If

Since the command is so short, we can compress the If statement into one line and omit the End If.

' Move backward past a comma
If PreviousCharacter = "," Then Selection.MoveLeft

I tend to use this compact version when it is clearer, but it is optional.

Move to the previous comma

Now we can get back to the move action in the original macro. The original GotoPreviousComma macro above already jumps to the previous comma using the command:

' Jump to the previous comma in the document (concise version)
Selection.MoveUntil Cset:=",", Count:=wdBackward

As a quick review, the MoveUntil method literally collapses any initial selection and moves the insertion point to the nearest character indicated in the character set Cset. We assigned a plain text comma "," to Cset := ",". We need to specify the direction as wdBackward with the Count option (defined in another miscellaneous Word WdConstants table). The Count option tells the command to move backward in the document by as many characters as needed.

Notice we separate the two command options with a comma which is why we needed the double quotes around our text comma when assigning it to Cset.

This step works whether or not a comma was detected earlier. If no is found, nothing happens with this command.

Final go to previous comma macro

Putting the steps together, we get:

Sub GotoPreviousComma()
' Jump to the previous in the document stepping over an adjacent comma

' Get the character before the insertion point or selection
Dim PreviousCharacter As String
PreviousCharacter = Selection.Previous.Text
' Move backward over the comma if it exists
If PreviousCharacter = "," Then Selection.MoveLeft

' Finally move back to the previous comma
Selection.MoveUntil Cset:=",", Count:=wdBackward
End Sub

Now this improved macro will intuitively step over any adjacent commas allowing us to jump through the document faster. I assigned this macro to Option+, (comma) on a Mac or Alt+, in Windows.

What changes with the select to previous comma version?

The bulk of the go to previous comma macro stays the same, but there are two main changes to make it select document content instead.

Extend the selection backward over the detected comma

We need to change the MoveLeft method to instead move the Start position of the Selection. The command is similar with the MoveStart method, but it's not as pretty.

Selection.MoveStart ' Not done ...

We need to move backward by one character, so we use the Count option set to negative one as Count := -1. The default move unit is for characters, so we can omit it.

Selection.MoveStart Count:=-1

Be sure to properly interpret the meaning of the Count option in different commands. This Count is an actual number of characters to move in the document.

Extend the selection to the previous comma

Instead of using the MoveUntil method as in the original set of macros, we need to move the Start position backward in the document until we encounter a comma. The command is similar with the MoveStartUntil method.

Selection.MoveStartUntil Cset:=",", Count:=wdBackward

The target character set is again a plain text comma using Cset := "," like we used with the above MoveUntil command. We are still extending the Start position backward in the document, so we use Count := wdBackward.

Since the End position of the Selection does not change, this command effectively extends the selection backward in the document.

Final selection to previous comma macro

Making these two changes in the move macro, we instead get:

Sub SelectToPreviousComma()
' Extend selection to the previous comma in the document including an adjacent comma

' Get the character before the insertion point or selection
Dim PreviousCharacter As String
PreviousCharacter = Selection.Previous.Text
' Select the comma if it exists
If PreviousCharacter = "," Then Selection.MoveStart Count:=-1

' Finally move back to the previous comma
Selection.MoveStartUntil Cset:=",", Count:=wdBackward
End Sub

The similarity is why we're including all these variations in the same article. I assigned this macro to Alt+Shift+, (comma) in Word for Windows, but the natural corresponding shortcut Option+Shift+, does not work on a Mac since the operating system does not like to recognize that key combination. I've had to try other variations including Option+Control+Shift+, but the asymmetry in the shortcuts is frustrating since it is a little harder to remember.

Moving or selecting forward in the document

The forward version is similar, but we have an extra problem to solve.

Get the Next character

How do we get the next character in the document?

Mirroring the previous macros above, the most obvious method for getting the next character is the Selection's Next method. We just reference the method with the Selection:

Selection.Next ' Not done ...

As with the Previous method earlier, the default unit for Next is a single character, so we can omit it. As used, the Next method also returns a character range in the document not the text, so we again need the plain text. We store it in a variable for later reference:

Dim NextCharacter As String
NextCharacter = Selection.Next.Text

Yep, looks basically the same ... so we're done, right?

Nope.

Next character problem

After some testing, we might notice an occasional difference in the result (the insertion point position or the final selection) based on whether we begin the macro with an initial selection or not. The selection or position will sometimes jump too far in the document.

What's the issue?

Word VBA isn't quite consistent with how it views the next character of empty versus non-empty selections. We get different “next” characters in each case at least in the intuitive sense.

Digging a little deeper

In the Word document snippet on the right, suppose we have some text with the insertion point at the beginning of the word "document":

Example document text
Word document text snippet

In this text sample, the character given by the text of the Selection.Next method is "o" not "d".

Huh? What?

Intuitively, a human would consider the "next" character to be the one immediately to the right of the insertion point, but apparently an empty selection is considered to have one character in it. Unfortunately, a Selection that spans just the character at "d" returns the same result.

Arghhh.

The confusion deepens when we check the Selection properties and find out that the Start and End positions are the same with an empty selection (as they should be), but the Selection still considers the "d" character to be "included" in the Selection. I'm sure Microsoft had a reason for this design decision (they have smart people over there), but this particular VBA "feature" is counterintuitive and even a little contradictory.

What do we do?

Move forward in the document

Since just moving the insertion point is the easier macro when moving, let's go ahead and teach our dog to fetch the forward stick (comma).

Can we just collapse the selection?

If we just collapse the selection every time, we wouldn't need to worry about the difference in the next characters depending on whether we have a selection or not. This technique side steps the next character ambiguity, and I use it on occasion. The Selection literally has a Collapse method.

Selection.Collapse ' Not done ...

This command collapses the Selection to its start by default, but we should be consistent with how the MoveUntil command moves forward in the document starting from the End position. We use the Direction option with the direction constant wdCollapseEnd (from another short Word constants table) to collapse the Selection toward the end.

Selection.Collapse Direction:=wdCollapseEnd

After the collapse, we know the intuitive next character is always the character immediately to the right of the insertion point. Fortunately, the collapse method has no effect on an insertion point (the blinking I-bar) since it is already collapsed.

Why didn't we do this on the previous macros?

One might wonder why we didn't collapse the selection for the previous macros. Basically, we encountered no problems either way for them since the previous character was unambiguous, and the movement or selection extension both occurred from the start of the selection.

Get the intuitive next character of an empty selection

If the selection is collapsed, the character immediately to the right of the insertion point is found using the Selection's Text property:

Selection.Text

This is technically just giving us the full plain text of the "empty" Selection, but it is the character we want to check whether it is a comma or not. I do not find it intuitive that an empty selection has any text associated with it. An empty string "" would make more sense, but we have no choice but to play with the tools VBA gives us.

Move past a possible comma

Since we need to move forward in the document over any detected comma, we use another of the Selection's specialty move methods, MoveRight.

' Move forward past a comma
Selection.MoveRight

The default step size is by one character, so we omit those option details.

Check the next character for the move forward macro

We now have the correct intuitive next character after the collapsed Selection, so we want the macro to skip a comma if it is at the edit location. Putting the steps together, the basic logic is the same as with the previous macro. We're just using a different variable name and move method.

If NextCharacter = "," Then
' Move forward past the comma
Selection.MoveRight
End If

We again use the compressed the short command conditional statement.

' Move forward past a comma
If NextCharacter = "," Then Selection.MoveRight

The compact version is optional, but this is clear here.

Move to the next comma

The final move command to jump to the next comma is still MoveUntil like the previous macro earlier, but we can drop the Count option since the default is to move forward in the document.

Selection.MoveUntil Cset:=","

This command will move past any number of characters trying to find a comma. If no comma is found, the insertion point will not move.

Final go to next comma macro

The go to next comma macro is just a slight tweak on the go to previous version, so let's put the new pieces together.

Sub GotoNextComma()
' Jump to the next comma in the document stepping over an adjacent comma

' Collapse toward the end to ensure the correct next character
Selection.Collapse Direction:=wdCollapseEnd
' Get the character just after the insertion point using the Text
' of the empty selection
Dim NextCharacter As String
NextCharacter = Selection.Text
' Move forward over the comma if it exists
If NextCharacter = "," Then Selection.MoveRight

' Finally move forward to the next comma
Selection.MoveUntil Cset:=","
End Sub

I assigned this macro to Command+, (comma) on a Mac or Control+, in Windows.

Select forward in the document

Now let's turn our attention to solving the more aggravating macro of the quad group.

Collapsing the selection was a valid workaround for the next character problem when just moving the edit location to the next comma, but ... we also want to solve the problem for the selection version of the macro. We need to keep any initial user selection which means we need the macro to handle the problem either way depending on the current document.

Fixing the problem for any selection

So, the next character problem with selections is:

  • If a Selection exists, we want the character after the selection using Selection.Next.Text.
  • If no selection exists, we want the character immediately after the insertion point which is given by Selection.Text.

What's the plan?

For the selection macro, we need to know whether a selection exists or not before we can pick the correct comparison character. Once we know, we use the appropriate text element to include the comma in the selection. How do we detect whether the selection is empty selection or not?

Selection conditional statement

Roughly speaking, translating the above into a conditional statement we get something like:

If a selection exists Then
' Pick the character after the selection
Otherwise
' No selection, so pick the character after the insertion point
End the selection check

How do we detect an empty selection?

How do we detect an initial selection of spanned text in the document?

Character count?

Unfortunately, we cannot use the character count to check for an empty selection since it still returns one character even for an empty selection.

Hmmm.

Selection Start and End positions?

We've mentioned the Selection is partly defined by Start and End positions. If the selection is empty, then these positions should be the same location in the document, so a tentative condition might be:

' Tentative condition to detect an empty Selection (not the best)
Selection.Start = Selection.End

This condition indeed distinguishes between an empty selection and one spanning one or more characters.

It appears to work in principle because the Word object anchor seems to count as a spanned invisible character at a specific location in the document text. However, it doesn't distinguish between the different types of Word selections. Some of the possible selected objects like shapes may not have actual Text associated with it, and a shape or other Word object does not make sense in the contenxt of the current macro.

Ughhh.

We are asking for trouble to not be more specific when identifying whether we have a selection or not because the current macro assumes a text selection if any. We need a safer way to detect a normal text selection.

What type of selection?

Another way to check whether any text is selected is to use the Selection Type property.

Selection.Type ' Not done ...

Several categories of selections exist including text; but they can also include shapes, tables, images, etc. (see our introduction to the Selection Type). The various Selection Types are defined in a WdSelectionType constants table. We're not concerned with all the Selection variations, so we basically want to know whether or not a "normal" text selection exists. The Word constant that indicates a normal selection is wdSelectionNormal, so we just check the Selection Type against it:

' Better way to check for a normal text selection
Selection.Type = wdSelectionNormal

Checking the Selection Type avoids most problems allowing us to (mostly) focus on text selections. Putting this condition into our conditional statement, we have:

' Check for a selection and pick the correct next character
If Selection.Type = wdSelectionNormal Then
' Selection is (probably) a normal text selection
Else
' Selection is empty
End If

Occasionally, it is more convenient to check if a selection does not exist. The constant for an empty selection is wdSelectionIP not wdNoSelection (see our summary article for more information).

A few gotchas could still occur such as with control elements or inline images both of which still show up as a normal text selection because they exist inline with the text. Fortunately, the unusual default text values will probably weed out these outlier cases since we can't cover or account for everything.

Get the next character

Inserting the correct next character choice along with the above Selection Type detection, we get:

' Check for a selection and pick the correct next character
If Selection.Type = wdSelectionNormal Then
' Pick the character after the selection
NextCharacter = Selection.Next.Text
Else
' No selection, so pick the character after the insertion point
NextCharacter = Selection.Text
End If

Notice the NextCharacter variable is assigned something for either case, so we exit the conditional statement knowing we at least have a value.

This Selection Type choice pops up enough through various macros that is it useful to extract the steps into a separate function, but we're just including it manually for now.

Select the comma

Now that we've identified the correct next character, we need to check it and decide whether to extend the selection over a comma. The comparison is the essentially same as we did for the previous selection macro. We compare NextCharacter to a text comma. If they match, we extend the selection.

If NextCharacter = "," Then
' Next character is a comma, so extend the selection over it
End If

We can accomplish the extension in several ways, but one of the easier ones just uses the MoveEnd method.

' Select the next comma character if it exists
If NextCharacter = "," Then Selection.MoveEnd

This literally just moves the End of the Selection forward in the document by one character (the default). Since the Start position is not affected, the command extends the selection.

Select over to the next comma

We finish the macro by extending the selection over to the next comma in the document. The corresponding command is the MoveEndUntil method.

' Extend the selection over to the next comma in the document
Selection.MoveEndUntil Cset:=","

We specified a character set as a comma "," using Cset := ",". The default is to move by as many characters forward in the document as necessary. If no further commas exist in the document, then the command does nothing.

Final select forward to a comma macro

Putting the selection variation steps together, we get:

Sub SelectToNextComma()
' Extend the selection over to the next comma in the document
' automatically including an adjacent comma

' Check for a normal selection type to pick the correct next character
Dim NextCharacter As String
If Selection.Type = wdSelectionNormal Then
' Pick the character after the selection
NextCharacter = Selection.Next.Text
Else
' Empty selection, so pick the character after the insertion point
NextCharacter = Selection.Text
End If

' Extend the selection forward over the comma if it exists
If NextCharacter = "," Then Selection.MoveEnd

' Finally extend the selection over to the next comma in the document
Selection.MoveEndUntil Cset:=","
End Sub

I assigned my version of this macro to Control+Shift+, in Word for Windows. The Word for Mac sibling shortcut Command+Shift+, seems to work, but some shortcuts involving punctuation keys don't work on a Mac (not that Word for Windows is perfect).

Improvements

Even this set of improved macros can be made better. Obviously, we eventually reach a point of diminishing returns, but we haven't yet reached the ceiling for this set of macros. "Better" means different things with different improvements, but here are a couple good candidates.

Generalizing the macros using the common steps

We want to create variations of these macros for different punctuation marks, so we really should extract the common steps into a few functions.

Why?

It makes the other macros much easier to create. Many times, they will be "one-liners" or just include a few lines for the differences. If any changes need to be made to the base macro (you might realize a wonderful tweak later), it only needs to be changed in one place.

Unfortunately, generalizing these macros ends up being quite a bit more work with a lot more explanation, so it falls outside the scope of this article. However, it might pop up in some extra member content if there is enough interest.

Finishing position tweak?

Details matter ... especially when a tweak can be added quickly.

For the move macros, I like the insertion point to finish on the left side of a comma or a close parenthesis but on the right side of an open parenthesis. Of course, this is a preference, but it just feels more natural and allows me to start typing faster most of the time without having to tap another arrow key. To accomplish this, we simply move one extra character ...

The catch is we only want to move another character if the search character was found on the final long move. If we don't add this condition, then we'll get an extra character moved every time no more commas exist in the document in the search direction.

Ughhh.

I rarely hesitate to make such preferential tweaks to my own macros, but the presentation will get muddled, so we'll save this tweak for another day. If you're champing at the bit, we need to store how many characters were moved by the MoveUntil command and move an extra character in the same direction if the result is more than one.

Why no Range variables yet?

The changes to the Selection were minimal in this set of macros, so I felt like using a range variable was superfluous. If we were to implement one or both of the above improvements, then using ranges might make the process easier.

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.