Quickly inserting a note into our manuscript sets an editing reminder, so we can move on with our writing. Taking it up a level, we can add context to the note using either selected text or a nearby word.
Thanks for your interest
This content is part of a paid plan.
Quickly insert contextual notes
We're in the zone. The words are flowing through our fingers onto the screen. Pages fill with amazing dialog, tantalizing scene descriptions … then we hit a speed bump. What year was a repeating rifle invented? Can this bandit have one for the train robbery? Seven rounds in the magazine is a lot different than sixteen.
Uhhhh … we want to keep our writing momentum, but we also don't want to get a significant detail wrong about a scene. Our minds can freeze like stubborn neurological mules, but it's easy to lose focus if we stop to research it now.
Enter novel notes.
We can quickly insert a note based on the novel context and move on with writing (or editing).
Novel note variations
A lot of personal variation exists based on the placement, wording, context, or style of the notes. We can't cover every variation, but we have introduced several that can be customized as needed.
- A simple inline note could be created with a formatted AutoCorrect entry, but we also implemented an inline note macro (not my preferred approach).
- The little brother of the current macro inserted a simple novel note as a styled paragraph.
- If our list of novel notes grows, a more sophisticated approach would implement the common steps in a few functions and have the specific notes just use those functions.
This version adds the note with some extra context based on the initial selection or nearby text if no text is selected. It also validates the applied paragraph style, so we don't get a nasty error message.
Using the selection for note context
When I write in an historical genre, I sometimes know a period-specific slang word exists. What'd they call a revolver? I might not remember it at the moment, but I've used "six-shooter" forty-eleven times already. I can type the word I'm thinking and just throw in a note to remind myself.
That's what editing is for. Granted we don't want to leave everything for the editing phase, but one priority in the first draft is to get the story on the page. Some details just have to wait.
How do we include the context?
Often it's related to the word just typed, or we could select the text we need for the note context. The macro can detect the difference and include that information in the note.
Create the empty macro
Open the VBA editor and create the following macro. If you prefer, a previous post covers creating an empty macro using the Word interface. Either way, the result will look something like:
The single quote tells VBA the rest of the text on that line is a comment meant for human readers. The note is longer in this macro because it does more than just insert a fixed note. We start our macro steps on the empty line.
What are the manual steps?
We want to quickly insert a note in a new paragraph along with the contextual information. What steps would we use to add a new novel note paragraph manually?
Several ways exist to accomplish this, but let's consider a direct route.
- Select and copy any relevant text for the note with the keyboard or mouse
- Move to the end of the previous paragraph
- Tap Return to Insert a new paragraph
- Type the note prefix text or use an AutoCorrect entry
- Paste any selected text
- Change the new paragraph style (optional)
- Move back to the original editing location
A standard Word keyboard shortcut exists to move by paragraphs in Word (Control+Up in Windows or Command+Up arrow on a Mac), so that helps a little. Using an AutoCorrect entry will speed things up with the note text, but it’s still a lot of steps if we insert that type of note often. Plus, we need to move back and forth in the document to get it done. It adds up to wasted time.
What is an object in VBA?
Word VBA uses virtual (in the everyday sense) "objects" to represent various document elements. Typical examples include a Paragraph or a Document, but more practical and generalized types exist such as a Range (see below). Objects include various actions (called "methods") and data (called "properties") that allow us to manipulate them or any associated document content. They are reminiscent of what we can do with the real-life object in a document, but some are necessary to work with the internal representation of the object data. Most VBA objects are named as expected just capitalized.
What is the Selection?
VBA represents the selection or insertion point on screen with an object called … the Selection (with a capital S). As such, only one Selection object exists per document. It can also represent an insertion point (i.e., the blinking I-bar waiting for text) as just an empty selection.
The Selection consists of its spanned content marked by Start and End positions in a document, but it also includes many other methods and properties that allow us to control its extent or position in the document, change settings, and handle other details specific to its purpose in a document. A separate article goes into more detail about the Selection.
What is a Range?
A Range is an object corresponding to a general span of text in the document with defined Start and End positions. We can assign document ranges to a variable and treat that range much like an invisible selection or insertion point. Using various methods or properties, we could move it around the document, change its extent, delete or modify the spanned text, etc. See a separate article on our brief introduction to Ranges for more information.
What is a Paragraph?
A Paragraph is a more specific VBA representation of its document element. It spans specific document content usually bounded paragraph marks on both sides (unless it's at the beginning of the document). A Paragraph object stores specific information related to its formatting and other settings. We can create a Paragraph variable to refer to a specific paragraph in the document and use the various methods and properties to manipulate it.
Difference between a Paragraph and a Range
A Paragraph is its own object data type with properties and methods specific to describing paragraph related attributes. A Range works more with its the position or extent of its spanned content in the document. A few Paragraph properties overlap with things we can do using its corresponding Range, but they generally have different purposes in VBA.
Use a Range variable in this macro
One point of the current macro is to insert the contextual note but leave the insertion point in place, so we can just keep writing. Using a Range variable is probably the easiest way to make our changes without disturbing the Selection.
Insert a contextual novel note
We will determine the context of the note based on the selection or a nearby word if no selection exists. Then we'll insert it along with the note prefix.
Declare the working ranges
Telling VBA what variables we will use is not strictly required by default. I appreciate the freedom of letting VBA design the variable data types, and I use it for prototype macros. For more serious macros, I make myself declare them explicitly. The slight downside is it makes our macros look a little more like programming than a series of editing steps.
Dim is the keyword we use to declare most variables, and "As Range" tells VBA what type of data it stores. I tend to include an "r" as a prefix for range variables to remind myself of the data type. Specifically, the note context variable is rNote. I often call these a "working range" because we need to work with it to make it refer to the exact document content we need.
This statement also defines the working paragraph range rParagraph which we will use later. We just need to separate them with a comma and make sure to give the data type for both variables. We could reuse the same range variable since rNote is not needed after we get the note text (probably calling it something more generic like just r), but this article macro uses a separate variable for extra clarity.
At this point, the variable names are set aside to store some as yet undetermined document range, but at this point, they are literally assigned a value of Nothing by VBA.
Get the note text
Since the context information is coming from the initial selection or a nearby word, it's convenient to define our working note range based on the initial Selection. We specifically refer to its Range property.
While the Selection is like a Range since both span some document content, we need to refer to the Range of the Selection since the Selection is the distinct object related to what we manipulate on screen. We must Set any object assignment, which includes Range variables, since they contain more data than just a value.
Even though we assigned it based on the Selection, rNote can be manipulated independent of the Selection.
Expand over the nearest word
We extend the range over the nearest word(s) in preparation for our context note using the appropriately named Expand method.
We specifically extend the range over the current word (or words, see below) using the Unit option.
The wdWord constant is from a table of unit constants.
If the macro starts with just an insertion point in the document, the range will expand over the current word. If more than one word is partially selected, the range will extend both ways until all words are included. One side effect is it will extend the range over any trailing spaces (which is also the default Word behavior for automatic selections), so we need to account for that below.
No Selection Type detection needed
It is tempting (for me too) to add an If statement to check whether the range is empty before expanding over the nearest word. Without much explanation, a knee-jerk solution might be to include the following steps:
This isn't necessary.
Word expansion works for either initial state
The expansion by a word unit does not interfere with either of the above use cases (an initial selection exists or not). If the range spanned any text, it just makes sure all the words are fully included. If the range was empty, it expands over the nearest word. Both uses are convenient for this macro. Trimming any extra spaces around the range needs to be done either way.
Trim extra spaces (optional)
I tend to keep article macros simpler, but this improvement is needs two lines, and it keeps the note tidy without annoying extra spaces lurking around the note.
Trim spaces from end of the Range
It's much more likely to need to trim trailing spaces from the end of the range. The MoveEndWhile method retracts the End position of the range back across any spaces while it keeps finding them at the end (or the reverse if moving forward).
The command requires a set of search characters which is just a space in this macro. We assign all desired characters as plain text to the Cset option. The search characters are usually given in double quotes (called a string), so our character set is just " ".
We're moving the End position of the range backward, so we need to specify the count option as wdBackward from a short Word constants table.
This constant tells the command to move any number of spaces backward in the document. We separate the options with a comma, and both require a colon equals := symbol for the assignments.
Trim spaces from start of the Range
We can also trim the space from the beginning of the range. This will not usually matter since to the above Expand command usually stops at the beginning of a word, but it might allow a preceding space when the word begins a paragraph. We use the corresponding MoveStartWhile method which obviously moves the Start position of the range.
We again use a space " " as the character set, but the default is to move the Start position forward in the document, so we can omit the Count option.
Store the contextual note
We first declare a string variable to store the note. We define the note text as a String variable, and assign it a value which we'll use later.
String variables only store plain text without any formatting, but they are a very common way to represent text. Again, this variable declaration is not required in standard VBA, but in member articles and my own editing macros, I prefer to be specific about all variables used.
We refer to the Text property of the note text range to get its plain text contents.
Then we store it in our novel note string variable. This is not strictly necessary, but it makes the macro easier to read and modify later.
We used an equals = sign to store the note text in the variable since a string is basically (but not quite) a value like a number. We "concatenate" the strings together with a plus + sign to mimic addition with numbers. The preface text included a space with "Check " since the rNote text most will not have a space at the start (we trimmed it earlier).
Set the current paragraph Range
The note will eventually be placed in a styled paragraph above the current one (my preferred location, so the novel text remains clear). To accomplish this, it is convenient to define a working range corresponding to the whole original paragraph.
Get the current (first) paragraph Range
We start by referring to the Paragraphs collection of the Selection.
We want the first paragraph of the collection using the First property.
A Paragraph is its own data type in VBA. We're want the paragraph's range, so we refer to the content it spans using its Range property.
If the Selection spans more than one paragraph, we only use the first one which is a reasonable assumption. In most cases, the macro will be run with an insertion point or with a selection spanning only a few words of a single paragraph.
Store the current paragraph range
Now, we assign this paragraph range to our paragraph range variable.
Again, we Set the Range since it represents a more complicated data type than just a value. This assignment works even if the starting selection doesn’t include the whole paragraph or if the selection spans multiple paragraphs (following paragraphs are ignored). We could reuse the previous range variable rNote since we no longer need it, but we use two separate variables for extra clarity.
This range variable spans the first paragraph, but we can manipulate it independent of the paragraph range. In this macro, we just use it to detect the beginning of the paragraph.
Insert the new paragraph
In the current macro, we're inserting the note into a separate paragraph to improve clarity, so we need to add the new paragraph using the InsertParagraphBefore method.
This command illustrates manipulating rParagraph as an independent range. Our rParagraph variable now spans the new, empty paragraph and the original paragraph. The original paragraph range is still present and spans the same document content.
Insert the note text
Finally, insert the actual note text using the InsertBefore method.
This is where it's nice to use a variable. Any changes made to the variable above will automatically be included here when this command inserts the note text. All such insert commands extend the range to include the newly added characters.
Change the note paragraph style
Using a distinct note paragraph style will set the note apart from the novel text.
Collapse the paragraph Range
Since the rParagraph working range currently spans two paragraphs, we use the Collapse method to collapse the range to the newly added paragraph.
The range is now positioned at the beginning of the new paragraph, so the style will only apply to that paragraph. The default direction is toward the Start (left side) of the range, so we omitted the Direction option.
Technically, this Collapse command does not depend on whether the style is valid, but it's tidier to include it inside the above If statement along with the style assignment below.
Assign the note paragraph style
We assign the paragraph style using the Style property of the Range.
The Style property conveniently allows us to just use a literal plain text style name. VBA does all the work under the hood to find the correct style information and apply it. This property is actually a generic data "Variant" type, so we could alternatively assign a Style variable (perhaps after being modified in another macro) to it, or we could use one of many default built-in styles from a table of constants. The property will understand the difference and apply the desired style to the paragraph, but it's usually easier to just use the plain text name.
See the gotchas below about validating the style because it is extra important in this macro.
Gotchas
So, what could go wrong? Don't neglect this step because it likely bite you or at least annoy you at some point. Of course, there's always the question of whether any issues are worth the time and effort to fix, but we should at least consider them, so we don't get unpleasantly surprised sometime in the future.
Is it worth the trouble?
For the style validation and the end-of-paragraph check, it's definitely worth the time and effort. Plus, aren't you just having fun writing macros when you should be writing your novel? Actually, I find it fun, but it doesn't get make progress on my work in progress either.
The troubleshooting commentaries and solutions are more condensed than the main article content.
Is the style valid in the document?
Invalid styles cause a macro to crash … as in quit running immediately and pop up an annoying error message. What's worse is VBA doesn't give us a native way to validate them before trying to apply a style to a paragraph.
Validate the paragraph style
A rough conditional statement to detect a valid style and take the appropriate action is as follows.
Style validation function
Another article covers a separate function to validate any style. It looks like:
We just provide (called "pass") the function a plain text name of any style to validate. It returns a True or False (Boolean) value answering whether the style is accessible in the current document. As a True or False value, we can use the result directly in a conditional statement. This function is handy for most macros that uses styles.
If the style is invalid (a False result), we just skip the style assignment, so we can just omit that part of the If statement. If so, the new note text paragraph will have the same paragraph style as the original paragraph.
What do we do if the style is valid?
Style validation conditional statement
Insert the function validation check and the previous two commands into the above conditional statement.
If you don't want to incorporate an additional function to validate the style, just omit the conditional statement but keep the two commands. Be cautious though, because you will (almost) inevitably forget to add the style in a future novel document, and the macro will crash. I convinced myself so until it crashed multiple times. I eventually became annoyed enough to (almost) always validate a style.
Does it work at the end of a paragraph?
A common use case would be the writer types a word … [literal fictional writing sequence ahead]
Marcel spun with his six-shooter — [writer freezes like the desperado across the saloon]
Ughhh … I don't like that word. What's another one? … [crickets]
Think! … Yep, you get the idea. This is a good time to just insert a novel note and move on.
The insertion point is at the end of the paragraph. We want to add an editing note to find a better word, but unfortunately, the Expand command will extend over the paragraph mark. It's not a fringe case we can sweep under the rug. In fact, it's probably a common use case.
It's even a little trickier since it works correctly if even one space is between the insertion point and the paragraph mark.
Ughhh, again.
How do we detect the end of the paragraph?
So, how to we catch this situation and fix it?
Unfortunately, this isn't enough. The writer might have a selection that accidentally spans the ending paragraph mark, so we need to also check whether the range is all whitespace characters. A previous function to check this looks like:
We want to check the contents of the rNote range. Combine this with the end-of-paragraph check ina conditional statement.
What do we do?
If this compound condition is True, we extend the beginning of the range backward in the document by one word.
If the compound condition is False, we do nothing else. Putting the two conditions together with the extension command, we get:
We need to add a paragraph mark character to the later trim statement.
After than, the excess whitespace on the end of the range. This check should be included since it covers a significant use case for the macro.
Does it work at the beginning of a paragraph?
If a word begins the paragraph, the macro works as expected. However, if spaces begin a paragraph with the insertion point in those spaces, it will span all the spaces up to the first word. This is a fringe case we can ignore if we wish.
If you insist (like I do), then we could check for the spaces to make a better word choice, but it's an example of how a seemingly simple validation is more complicated than it appears. That doesn't mean it's hard, but it makes the macro look inordinately complicated just to catch an unusual and probably rare case.
The solution is much like the end-of-paragraph check, so we could check the expanded range against the beginning of paragraph position.
We still need to validate whether the note range contains only spaces, so we use the IsRangeWhitespace function mentioned above. Then we extend the End of the range forward by one word.
With the whitespace check, the range is only modified if it does not already contain at least one word.
This check is not that important, but it is shown here for completeness.
Does the selection span more than one paragraph?
If the selection spans a paragraph break, the note will contain that break also. This is unsightly, but it's more of an annoyance than an error, so we will ignore it.
We could include a check for more than one paragraph and trim the working range, but it makes the macro look messier than it needs to be.
If more than one paragraph is found by checking the Count property of the Paragraphs collection, we just set the range End position to the first paragraph's End position. This effectively trims the range to be just contained within the first paragraph.
Is this worth it?
I would probably add it to my own macro, but it's a preference. Perhaps, you even prefer to let the not span a line like this. If so, the style application at the end would need to be adjusted.
This check is excluded from the final macro.
Is the final range empty?
After being trimmed for whitespace, it is possible that the final note range ends up being empty. If so, we should probably check and just exit the function before adding the note paragraph.
This command used a condensed If statement for brevity. It isn't required since a goofy note will just look bad, but it stops us from having to delete a poorly worded or formatted contextual note.
Final contextual novel note macro
Now put the commands together to finish the macro.
This macro helps me quickly include research reminders for later editing while allowing me to continue writing in the moment. I have several variants assigned to different keyboard shortcuts.
It may seem inefficient to validate the style every time the macro is run, but it's easy to forget to create every style in a new manuscript. For example, we might change the style name but not transfer it to the newer document. The style error becomes annoying after only a few times. The handful of milliseconds VBA spends validating the style each time the macro is used will almost never be noticed.
After the note is corrected in the text, usually on a later editing cycle, a simple Control+Shift+Delete (or Command+Shift+Delete on Mac) swiftly deletes the note paragraph using another super handy macro.
Revised (more foolproof) final macro
Including some (not all) of the gotcha corrections makes the macro messier but a little more functional.
Are you willing to view the mess for the improved functionality? I usually do.
Improvements
We can still improve several things the above macro.
Screen updates
While we are using a Range variable to insert the note text, the changes are still visible on the screen as they are made, so this macro is a decent candidate to disable screen updates while running. On the other hand, the simple changes are probably fast enough that we wouldn't notice the difference.
Undo record
The multiple small changes made by the macro would need to be undone one-by-one, so this macro is a good candidate for creating an undo record. They make a macro feel more "professional" since its acts more intuitively like a single action, but it is excluded even in this member article because they can cause significant problems with the undo action chain if not properly implemented.
Function version
If you like the novel notes, you'll probably add other variations with different text and/or note paragraph styles. It's cumbersome to copy the entire macros over. Repeating all the steps in each variation with only tiny changes between them clutters our macro file and makes any later changes tedious (we need to change the same thing in each macro). These note macros are begging for a few functions to encapsulate the common steps, but functions make the macros look significantly more technical.