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

Move sentences plus with functions

Word • Macros • Editing • Functions
Peter Ronhovde
15
min read

We are building several functions to streamline our previous move sentence macro. This article is a work in progress as the various functions are developed in upcoming articles.

Thanks for your interest

This content is part of a paid plan.

Problems with Original Long Macros

The extended content below shows the original macros that move a sentence backward or forward in a document.

Given their complexity compared to other editing macros we’ve done in Word (and we haven’t even added handling dialog naturally which I consider a minimum for an author), they serve as school of hard-knocks examples on why functions and subroutines were created.

If you’ve been following along with at least some of the articles, I generally explain many or even most details. Talking about functions implies an intermediate level student, so I’ll skip some of the extended explanations as we proceed through the associated articles.

The Problems

In the original article, the base macro to accomplish the raw task of moving a sentence is not too bad, but the accumulated steps to handle the special cases (so they work more intuitively) make the macros awkwardly long. However, upon inspection, there are several natural sets of steps that can be neatly separated out and would probably apply to other macros.

Candidate functions for move sentence macro

Encapsulating repetitive tasks is a major use of functions and subroutines. After checking the long versions of the macros, some natural functions pop out.

How to decide on a function?

We’re looking for chunks of steps that perform a specific task or a related set of tasks—not too big or it partially defeats the point of creating the functions.

Sometimes the candidate steps can be obscured if they are interspersed among other more macro specific steps, but usually it’s relatively obvious which steps to extract.

Those best suited to functions would ideally be general purpose and probably usable in other macros. On the other hand, some long macros could still be broken up into unique functions or subroutines to make the work more manageable.

Natural functions

Several examples of general purpose sets of steps in the above macros include:

  • Check and delete any spaces at the end of a paragraph (used four times)
  • Check and add any spaces before or after the moved sentence as needed (used about four times depending on how it’s counted)
  • Check and delete an empty paragraph (used twice)

The first two take more steps to implement, but the third is still worth creating a function since it is a task we would likely reuse in other macros.

Secondary tasks

Sometimes we can also group basic tasks together even if they don’t contain a lot of steps.

  • Select the sentence (used twice)

Moving the sentence selection to a function will further allow us to more easily modify it later to naturally handle dialog or parentheses.

Modular macros

When we’re done, the move sentence macros should include only the necessary steps to implement the specific tasks. Most, if not all, generic subtasks will be relegated to dedicated functions or subroutines.

All together, these functions will also make the original macros easier to read assuming we pick function names that describe their actions well.

In addition, we’ll have a growing toolbox of useful functions and subroutines already tested and ready to help us create better macros in the future. Thinking at a higher level (more task than step oriented) makes creating macros easier and usually faster. You don’t always have to descend down to the nitty-gritty, itty-bitty steps to get something done.

Imperceptible efficiency hit

Using functions results in a slightly less efficient macros since some additional steps and validation checks are typically performed internally in the subroutines, but you will not notice a practical time difference when running the macro. Unless you’re creating a macro that processes your entire novel every time, or something to that effect, the time savings using functions in your macros is well worth the imperceptible degradation in running speed.

Relevant Functions

To facilitate the improved macro below, several functions and have been implemented:

A couple of supplementary functions that would make things more convenient include:

Revised macros using subroutines

Putting everything together, we finally get a more refined version of the move sentence macros. These look a lot more manageable compared to the original longer versions.

Declare ranges

Since we're using the ranges as function arguments (data passed to the functions), we need to declare them explicitly.

Dim rSentence As Range, rCopy As Range

Otherwise, we'll get an error.

We didn't need to do this previously because VBA will infer the type of the variable (using a generic type), but VBA is pickier when we send data to functions.

Move sentence backward

Let’s merge this new subroutine into our macro. In order to work, this macro requires the included functions to also be in your VBA editor, so this macro is not yet fully functional (get it …).

Sub MoveSentenceBackward()
' Move current sentence one sentence backward
' Does not use clipboard

' Select current sentence
Dim rSentence As Range, rCopy As Range
Set rSentence = GetCurrentSentenceRange
' Set working range to same as original sentence
Set rCopy = rSentence.Duplicate

' Move to copy location based on sentence position in current paragraph
PreviousCharacter = rCopy.Previous(Unit:=wdCharacter).Text
If PreviousCharacter = vbCr Then
' Move just before previous paragraph mark
rCopy.Move Unit:=wdCharacter, Count:=-2
Else
' Regular case moving back one sentence
rCopy.Move Unit:=wdSentence, Count:=-2
End If

' Move formatted text to new location
rCopy.FormattedText = rSentence.FormattedText
rCopy.MoveEndWhile Cset:=" " ' Extend range over ending spaces
rSentence.Delete ' Delete original sentence

' Ensure proper spacing between sentences
Set rCopy = PadSentenceSpaceLeft(rCopy)
If GetNextCharacterAfterRange(rCopy) = vbCr Then
' Delete spaces at end of copied text range
Call DeleteParagraphEndSpaces(rCopy.Paragraphs.First)
Else
' Add a space based on copied sentence location in paragraph
Set rCopy = PadSentenceSpaceRight(rCopy)
End If

' Delete any spaces at end of original sentence range
If GetNextCharacterAfterRange(rSentence) = vbCr Then
Call DeleteParagraphEndSpaces(rSentence.Paragraphs.First)
' Check and delete possible empty paragraph
Call DeleteEmptyParagraph(rSentence.Paragraphs.First)
End If

' Finish with moved sentence selected
rCopy.Select
End Sub

Move sentence forward

And for the macro to move the current sentence to the right in the document we have:

Sub MoveSentenceForward()
' Move current sentence one sentence forward
' Does not use clipboard

' Select current sentence
Dim rSentence As Range, rCopy As Range
Set rSentence = GetCurrentSentenceRange
' Set working range to same as original sentence
Set rCopy = rSentence.Duplicate

' Move to copy location based on sentence position in current paragraph
NextCharacter = rCopy.Next(Unit:=wdCharacter).Text
If NextCharacter = vbCr Then
' Move additional character past next paragraph mark
rCopy.Collapse Direction:=wdCollapseEnd
rCopy.Move Unit:=wdCharacter
Else
' Regular case moving forward one sentence
rCopy.Move Unit:=wdSentence

' For second to last sentence, Word moves to next paragraph, so reposition
' to end of original paragraph
PreviousCharacter = rCopy.Previous(Unit:=wdCharacter).Text
If PreviousCharacter = vbCr Then
rCopy.Move Unit:=wdCharacter, Count:=-1
End If
End If

' Move formatted text to new location
rCopy.FormattedText = rSentence.FormattedText
rCopy.MoveEndWhile Cset:=" " ' Extend range over ending spaces
rSentence.Delete ' Delete original sentence

' Ensure proper spacing between sentences
Set rCopy = PadSentenceSpaceLeft(rCopy)
If GetRangeNextCharacter(rCopy) = vbCr Then
' Delete spaces at end of copied text range
Call DeleteParagraphEndSpaces (rCopy.Paragraphs.First)
Else
' Add a space based on copied sentence location in paragraph
Set rCopy = PadSentenceSpaceRight(rCopy)
End If

' Delete any spaces at end of original sentence range
If GetRangeNextCharacter(rSentence) = vbCr Then
Call DeleteParagraphEndSpaces (rSentence.Paragraphs.First)
' Check and delete possible empty paragraph
Call DeleteEmptyParagraph (rSentence.Paragraphs.First)
End If

' Finish with moved sentence selected
rCopy.Select
End Sub

VIP technical support

Sometimes debugging more complex macros can feel like playing a Whack-a-Mole game especially as you add new and improved features. Premium members get access to special VIP links for limited technical support as specified in the member benefits [please note support time does not accumulate since I cannot overcommit future scheduling requirements (but I try to be reasonable about it) nor cash out in any way if membership ends or is downgraded; support does not consist of writing entire macros or functions which would instead qualify as a paid service].

Original (Long) Macros

To illustrate the motivation for creating the extra functions, here are the original, awkwardly long, move sentence macros.

Moving sentence backward

Our starting macro to move a sentence backward in the document is:

Sub MoveSentenceBackwardLongVersion()
' Move the current sentence one sentence backward
' Does not use the clipboard

' Set the starting Selection Range
Set rSentence = Selection.Range

' Select the current sentence
rSentence.Collapse ' Avoid starting selection
rSentence.Expand Unit:=wdSentence
' Remove any paragraph marks from sentence selection
rSentence.MoveEndWhile Cset:=vbCr, Count:=wdBackward

' Set working range to same as original sentence
Set rCopy = rSentence.Duplicate

' Move the copy location back one sentence
' Check for beginning of current paragraph
PreviousCharacter = rCopy.Previous(Unit:=wdCharacter).Text
If PreviousCharacter = vbCr Then
' Move to end of previous paragraph
rCopy.Collapse
' Move one character past previous paragraph mark
rCopy.Move Unit:=wdCharacter, Count:=-1

' Insert space for sentence at end of paragraph
' Assumes no ending space at end of paragraph
rCopy.InsertBefore Text:=" "
' Collapse again so we don't overwrite the space when text is copied
rCopy.Collapse Direction:=wdCollapseEnd
Else
' Regular case with any other sentence
' Move back one sentence in the document
rCopy.Move Unit:=wdSentence, Count:=-2
End If

' Move formatted text to new location
rCopy.FormattedText = rSentence.FormattedText
rSentence.Delete ' Delete original sentence

' Extend range over ending spaces
rCopy.MoveEndWhile Cset:=" "
' Ensure a space separates sentences inside a paragraph
LastCharacter = rCopy.Characters.Last.Text
' NextCharacter checks for end of paragraph exception
NextCharacter = rCopy.Next(Unit:=wdCharacter).Text
If LastCharacter <> " " And NextCharacter <> vbCr Then
' No ending space found, so add one
rCopy.InsertAfter Text:=" "
End If

' Select and delete all ending paragraph spaces at source sentence
' rSentence is already collapsed from Delete above
PreviousCharacter = rSentence.Previous(Unit:=wdCharacter).Text
' Use first character to detect end of paragraph
FirstCharacter = rSentence.Characters.First.Text
If PreviousCharacter = " " And FirstCharacter = vbCr Then
rSentence.MoveStartWhile Cset:=" ", Count:=wdBackward
' Delete preceding spaces
rSentence.Delete

' Delete straggling space Word reinserts sometimes
FirstCharacter = rSentence.Characters.First.Text
If FirstCharacter = " " Then
rSentence.Delete
End If
End If

' Delete empty paragraph if we moved only sentence
' Checked in addition to deleting spaces above
PreviousCharacter = rSentence.Previous(Unit:=wdCharacter).Text
FirstCharacter = rSentence.Characters.First.Text
If PreviousCharacter = vbCr And FirstCharacter = vbCr Then
' Delete empty paragraph
rSentence.Delete
End If

' Select and delete any end of paragraph spaces at target location
' Create temporary range to delete spaces at end of copied text range since we do not want to change rCopy at the moved position
' Set a duplicate of copied sentence range
Set rEnd = rCopy.Duplicate
' Work with end of copied sentence range
rEnd.Collapse Direction:=wdCollapseEnd
PreviousCharacter = rEnd.Previous(Unit:=wdCharacter).Text
' Use first character to detect end of paragraph
FirstCharacter = rEnd.Characters.First.Text
' Need the space check also or it will merge paragraphs for last sentence
If PreviousCharacter = " " And FirstCharacter = vbCr Then
rEnd.MoveStartWhile Cset:=" ", Count:=wdBackward
' Delete preceding spaces
rEnd.Delete

' Delete straggling space Word reinserts sometimes
FirstCharacter = rEnd.Characters.First.Text
If FirstCharacter = " " Then
rEnd.Delete
End If
End If

' Finish with moved sentence selected
rCopy.Select
End Sub

Moving sentence forward

The move sentence forward version of the macro is also long. We need to adjust the corresponding steps (performed off screen) for moving forward, so the details are a little different.

Sub MoveSentenceForwardLongVersion()
' Move the current sentence one sentence forward
' Does not use the clipboard

' Set the starting Selection Range
Set rSentence = Selection.Range

' Select the current sentence
rSentence.Collapse ' Avoid starting selection
rSentence.Expand Unit:=wdSentence
' Remove any paragraph marks from sentence selection
rSentence.MoveEndWhile Cset:=vbCr, Count:=wdBackward

' Set working range to same as original sentence
Set rCopy = rSentence.Duplicate

' Move the copy location forward one sentence
' Check for end of current paragraph
NextCharacter = rCopy.Next(Unit:=wdCharacter).Text
If NextCharacter = vbCr Then
' Move to beginning of next paragraph
rCopy.Collapse Direction:=wdCollapseEnd
rCopy.Move Unit:=wdCharacter
Else
' Standard case to move forward one sentence
rCopy.Move Unit:=wdSentence, Count:=2

' For second to last sentence of a paragraph, Word automatically moves past the paragraph mark to next paragraph, so catch beginning of paragraph (wrong) target position and reposition to end of original paragraph
PreviousCharacter = rCopy.Previous(Unit:=wdCharacter).Text
If PreviousCharacter = vbCr Then
rCopy.Move Unit:=wdCharacter, Count:=-1
End If
End If

' Move formatted text to new location
rCopy.FormattedText = rSentence.FormattedText
rSentence.Delete ' Delete original sentence

' Extend selection over ending spaces
rCopy.MoveEndWhile Cset:=" "
' Ensure a space separates it from next sentence inside a paragraph
LastCharacter = rCopy.Characters.Last.Text
' NextCharacter checks for end of paragraph exception
NextCharacter = rCopy.Next(Unit:=wdCharacter).Text
If LastCharacter <> " " And NextCharacter <> vbCr And Then
' No ending space found, so add one
rCopy.InsertAfter Text:=" "
End If

' Ensure a space separates it from prior sentence inside a paragraph
' First character of rCopy cannot be a space since it is copied from original sentence selection
' Previous character checks for beginning of paragraph or space exceptions
PreviousCharacter = rCopy.Previous(Unit:=wdCharacter).Text
If PreviousCharacter <> " " And PreviousCharacter <> vbCr Then
' No prior space found, so add one
rCopy.InsertBefore Text:=" "
' Remove inserted space from range to mimic sentence selections
rCopy.MoveStart Unit:=wdCharacter
End If

' Select and delete ending paragraph spaces at source sentence
' rSentence is already collapsed from Delete above
' First character of rSentence detects end of paragraph
FirstCharacter = rSentence.Characters.First.Text
PreviousCharacter = rSentence.Previous(Unit:=wdCharacter).Text
If PreviousCharacter = " " And FirstCharacter = vbCr Then
rSentence.MoveStartWhile Cset:=" ", Count:=wdBackward
' Delete preceding spaces
rSentence.Delete

' Delete straggling space Word reinserts sometimes
FirstCharacter = rSentence.Characters.First.Text
If FirstCharacter = " " Then
rSentence.Delete
End If
End If

' Delete empty paragraph if we moved only sentence
' Checked in addition to deleting spaces above
PreviousCharacter = rSentence.Previous(Unit:=wdCharacter).Text
FirstCharacter = rSentence.Characters.First.Text
If PreviousCharacter = vbCr And FirstCharacter = vbCr Then
' Delete empty paragraph
rSentence.Delete
End If

' Select and delete ending paragraph spaces at target sentence
' Create a temporary working range to delete spaces at the end of copied text range since we do not want to change rCopy at the moved position
' Set a duplicate of copied sentence range
Set rEnd = rCopy.Duplicate
' Work with end of copied sentence range
rEnd.Collapse Direction:=wdCollapseEnd
PreviousCharacter = rEnd.Previous(Unit:=wdCharacter).Text
' Use first character to detect end of paragraph
FirstCharacter = rEnd.Characters.First.Text
If PreviousCharacter = " " And FirstCharacter = vbCr Then
rEnd.MoveStartWhile Cset:=" ", Count:=wdBackward
' Delete preceding spaces if any
rEnd.Delete

' Delete straggling space Word reinserts sometimes
FirstCharacter = rEnd.Characters.First.Text
If FirstCharacter = " " Then
rEnd.Delete
End If
End If

' Finish with moved sentence selected
rCopy.Select
End Sub

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.