'############################################################ '# '# '# |*-----------------------------------*| '# || VISUAL STUDIO MACROS || '# |*-----------------------------------*| '# Last modified: 20/05/07 '# by Andrew Noske '# (http://www.andrewnoske.com/) '# '# '# INSTRUCTIONS (HOW TO USE THESE MACROS): '# '# 1) Open Microsoft Visual Studio, then go: '# Tools >> Macro >> Visual Basic Editor (or press Alt+F11) '# 2) On the right hand side, right click "My Macros", then click: '# Add >> Add Existing Item. '# 3) Find this file - if not already make sure you rename it with '# a .vb extension. '# 4) One of these functions uses "System.Web.HttpUtility"... to '# you must go: '# Project >> Add References ... select "System.Web.dll" and '# then click "Select" and "OK" '# NOTE: Is a small chance you'll need to add a reference for '# Regex strings as well '# 5) Minimize the Visual Basic window, and in Word go: '# Tools >> Macro >> Macros Exporer (or press Alt-F8) '# 6) Expand MyMacros and you will see new macros have been added. '# 7) From this menu you can run them by double clicking, or right '# click to edit them etc.... so have a play around, and add your own. '# '# Next time you want to use one of these Macros, press Alt-F8 '# and double click the one you want. :) '# '# '# WHAT ARE MACROS: '# '# Macros are little scripts, which you can record or write '# yourself to help automate repeative tasks in the common MS '# applications. '# '# Macros are great, because, you'll often find yourself performing '# the same sequences of operations again and again in computing, '# and Macros can save you time, but letting you press a short-cut, '# then executing these steps for you. '# '# Visual Basic is a babish language, and often won't work the way '# you want, but it's worth learning a BIT, just for the sake '# of learning how to use Macros. Macros can potentially save you '# HOURS and hours (expecially Excel Macros I've found), and I '# recommend all computer students learn a little about them. '# Identifying what task you should use macros for is half the challenge. '# '# '# WHAT IS THIS FILE FOR: '# '# This file contains some simple Macros for Microsoft Visual Studio '# I wrote for a few common tasks I've encountered including: '# formatting tabs, making cout statements and many more. '# '############################################################ Option Strict Off Option Explicit Off Imports EnvDTE Imports System.Diagnostics Imports System.String Imports System.Web.HttpUtility Imports System.Text.RegularExpressions Public Module AN_VS_Macros '============================================================ ' EXECUTABLE SUB-ROUTINES: '============================================================ '------------------------------------------- ' Removes empty lines ' Sub ReplaceDoubleNewLineWithSingleNewLine() If (Doc_IsSelectionMade()) Then ReplaceAllSelection("\n\n", "\n", True) Else If (MsgYesNo("No selection was made... apply to whole document ?")) Then ReplaceAll("\n\n", "\n", True) End If End If End Sub '------------------------------------------- ' Removes all tabs and whitespaces at the end of lines ' in the currently open document ' Public Sub RemoveTrailingWhiteSpace() ReplaceAll("[\t ]+\n", "\n", True) End Sub '------------------------------------------- ' Takes a selection of several functions and makes each function a single line. ' NOTE: This is useful for copying functions then turning them into single-line definitions. ' WARNING: Does not take comments into account ' Sub MakeSelectedFunctionsSingleLine() ReplaceAllSelection("\n\n\", "\n\", True) ReplaceAllSelection("\n\{", "\t\{", True) ReplaceAllSelection("\{\n\t", "\{\t", True) ReplaceAllSelection("\n\}", "\t\}", True) End Sub '------------------------------------------- ' Takes the currently selected text and executes a google search with that string. ' NOTE: This function was taken from: http://visualbasic.about.com/od/usingvbnet/a/vsmacros01_4.htm ' Public Sub SearchGoogleForSelectedText() Dim s As String = ActiveWindowSelection().Trim() If s.Length > 0 Then DTE.ItemOperations.Navigate("http://www.google.com/search?q=" & System.Web.HttpUtility.UrlEncode(s)) End If End Sub '------------------------------------------- ' Counts the number of comments and brackets in the open document ' This is useful, because it's common to miss a bracket, and when you ' don't have matching '()'s and '{}'s your code will not compile. ' Sub CountBracketsAndComments() '-- save position Dim curCol, curLine curCol = ActiveDocument.Selection.CurrentColumn curLine = ActiveDocument.Selection.CurrentLine '-- counts specific occurances Dim docText As String Dim numSingleLineComments, numMultiLineComments, numLeftBrackets, numRightBrackets, numLeftBraces, numRightBraces As Integer docText = Doc_GetAllTextInDocAsString() numSingleLineComments = StringFs_CountOccurances(docText, "//") numMultiLineComments = RegStr_CountMatches(docText, "/\*(.*)($*)\*/") ' NOT IDEAL YET numLeftBrackets = StringFs_CountOccurances(docText, "(") numRightBrackets = StringFs_CountOccurances(docText, ")") numLeftBraces = StringFs_CountOccurances(docText, "{") numRightBraces = StringFs_CountOccurances(docText, "}") If Not (numLeftBrackets = numRightBrackets) Then MsgBox("WARNING: number of left and right brackets is not even!") End If MsgBox("Number of // comments: " & numSingleLineComments & vbCr & _ "Number of /* */ comments: " & numMultiLineComments & vbCr & vbCr & _ "Number of ( and ) : " & numLeftBrackets & " and " & numRightBrackets & vbCr & _ "Number of { and } : " & numLeftBraces & " and " & numRightBraces) '-- restore position ActiveDocument.Selection.MoveTo(curLine, curCol) End Sub '------------------------------------------- ' Makes all text between and including "/*" and "*/" comment tags red ' NOTE: this function is REDUNDANT now I'm using regex strings ' Public Sub ConvertSingleLineStarCommentsToLineComments() RemoveTrailingWhiteSpace() ReplaceAll("/\*(.*)\*/$", "\0", True) Dim numFound As Integer = StringFs_CountOccurances(Doc_GetAllTextInDocAsString(), "") ReplaceAll("*/", "", False) ReplaceAll("/*", "//", False) MsgBox(numFound & " single line /* */ comments have been replaced") End Sub '------------------------------------------- ' Takes a line selection of several variable names seperated by spaces and generates a COUT statement to output their values. ' EXAMPLE: ' SELECT TEXT: ' var1 var2 ' ... BECOMES: ' cout << " var1=" << var1 << " var2=" << var2 << endl; ' Sub CoutListOfVariables() If (Not Doc_IsSelectionMade()) Then MsgBox("You must select a line first") Exit Sub End If Dim selText As String Dim replace, varStr As String Dim spaces() As Char Dim var() As String spaces = (" ") selText = DTE.ActiveDocument.Selection.Text ' get selected text var = selText.Split(spaces) ' split it apart between spaces replace = "cout" ' generate the replacement text For i = 0 To UBound(var) replace = replace + " << "" " + var(i) + "="" << " + var(i) Next i replace = replace + " << endl;\t\t\t\t//%%%%%%" DTE.ActiveDocument.Selection.Text = replace ' replace selected text End Sub '------------------------------------------- ' Goes through the open document, and scrolls up and down, adding the correct number ' of tabs to the end of any empty line. ' I run this a lot, because I hate the way MSVS (unlike many other editors) doesn't ' automatically put your curor the correct number of tabs accross ' Sub FixTabs() Dim lineStr As String Dim numLines, numTabsAdded, numCharsOnLine, numTabs, numTabsToAdd, numTabsLastLine As Integer numTabsLastLine = 0 numTabsAdded = 0 numLines = StringFs_CountOccurancesInWholeDoc(vbCr) + StringFs_CountOccurancesInWholeDoc(vbLf) DTE.ActiveDocument.Selection.StartOfDocument() '## GO DOWN LINES, ADDING EXTRA TABS WHERE NECESSARY For i = 1 To numLines DTE.ActiveDocument.Selection.StartOfLine(False) ' select line DTE.ActiveDocument.Selection.EndOfLine(True) ' select line lineStr = DTE.ActiveDocument.Selection.text numCharsOnLine = Len(lineStr) numTabs = StringFs_CountOccurancesCharsAtStart(lineStr, vbTab) ' count number of tabs at the start If (numCharsOnLine > numTabs) Then ' if non-tab characters proceed tabs: update the number of tabs for next empty line numTabsLastLine = numTabs Else ' else (if there are no other characters): add addional tabs if needed DTE.ActiveDocument.Selection.EndOfLine(False) ' select line For j = 1 To (numTabsLastLine - numTabs) ' add extra tabs DTE.ActiveDocument.Selection.Text = vbTab numTabsAdded = numTabsAdded + 1 Next j End If DTE.ActiveDocument.Selection.LineDown(False, 1) Next i '## GO UP LINES, ADDING EXTRA TABS WHERE NECESSARY For i = 1 To numLines DTE.ActiveDocument.Selection.StartOfLine(False) ' select line DTE.ActiveDocument.Selection.EndOfLine(True) ' select line lineStr = DTE.ActiveDocument.Selection.text numCharsOnLine = Len(lineStr) numTabs = StringFs_CountOccurancesCharsAtStart(lineStr, vbTab) ' count number of tabs at the start If (numCharsOnLine > numTabs) Then ' if non-tab characters proceed tabs: update the number of tabs for next empty line numTabsLastLine = numTabs Else ' else (if there are no other characters): add addional tabs if needed DTE.ActiveDocument.Selection.EndOfLine(False) ' select line For j = 1 To (numTabsLastLine - numTabs) ' add extra tabs DTE.ActiveDocument.Selection.Text = vbTab numTabsAdded = numTabsAdded + 1 Next j End If DTE.ActiveDocument.Selection.LineUp(False) Next i '## REPORT SUCCESS: MsgBox("Number of tabs added = " & numTabsAdded) End Sub '------------------------------------------- ' Takes a multiple line selection, finds the comment furthest from the start of the line ' and then lines any other single-line comments up with this one. ' I use this regularly to make my comments neater and easier to read. ' Sub AlignSymbols() If (Not Doc_IsSelectionMade()) Then MsgBox("You must select several line for this function to work") Exit Sub End If Dim stingToAlign As String stingToAlign = "(" stingToAlign = InputBox(prompt, "Char to align:", stingToAlign) If (Len(stingToAlign) > 0) Then AlignFirstOccuranceOf(stingToAlign) End If End Sub '------------------------------------------- ' Takes a multiple line selection, finds the comment furthest from the start of the line ' and then lines any other single-line comments up with this one. ' I use this regularly to make my comments neater and easier to read. ' Sub AlignComments() AlignFirstOccuranceOf("//") End Sub '------------------------------------------- ' TEST FUNCTION ' Sub TEST() 'Doc_GoToStart() MsgBox("num chars = " & Doc_GetAllTextInDocAsString()) 'Dim selectedText As String 'selectedText = DTE.ActiveDocument.Selection.Text 'MsgBox(selectedText) End Sub '============================================================ ' ADVANCED (BUT RESUABLE) FUNCTIONS: '============================================================ '------------------------------------------- ' Takes a multiple line selection, finds the comment furthest from the start of the line ' and then lines any other single-line comments up with this one. ' I use this regularly to make my comments neater and easier to read. ' Function AlignFirstOccuranceOf(ByVal stingToAlign As String) As Boolean If (Not Doc_IsSelectionMade()) Then Return False End If Dim numTabs, numChars, numTabsNeeded, effLen, maxLen As Integer Dim selText, varStr, beforeComment As String Dim returnChar() As Char Dim var() As String returnChar = (vbNewLine) '## SEPERATE TEXT INTO ARRAY OF LINES: selText = DTE.ActiveDocument.Selection.Text ' get selected text var = selText.Split(returnChar) ' split it into lines '## FIND NUMBER OF CHARS TO FURTHEST "stingToAlign" maxChars = 0 For i = 0 To UBound(var) numChars = var(i).IndexOf(stingToAlign) If (numChars > 0) Then beforeComment = StringFs_SubstrSafe(var(i), 0, numChars) numTabs = StringFs_CountOccurances(beforeComment, vbTab) effLen = numChars + (numTabs * 3) 'AlertMsg("effLen=" & effLen & " numChars=" & numChars & " numTabs=" & numTabs) If (effLen > maxLen) Then maxLen = effLen End If End If Next i '## ROUND UP MAXLEN TO NEAREST MULTIPLE OF 4 If (maxLen Mod 4 > 0) Then maxLen = maxLen + 4 - (maxLen Mod 4) End If 'AlertMsg("maxLen after mod=" & maxLen) '## FOR EACH LINE MAKE THE FIRST "stingToAlign" IS MAXLEN FROM START (BY INSERTING TABS JUST BEFORE IT) For i = 0 To UBound(var) numChars = var(i).IndexOf(stingToAlign) If (numChars > 0) Then beforeComment = StringFs_SubstrSafe(var(i), 0, numChars) numTabs = StringFs_CountOccurances(beforeComment, vbTab) effLen = numChars + (numTabs * 3) numTabsNeeded = Int((maxLen - effLen + 3.0) / 4.0) DTE.ActiveDocument.Selection.StartOfLine(False) ' go to start of line ActiveDocument.Selection.CharRight(False, numChars) ' go to just before "//" For j = 1 To numTabsNeeded ' add tabs DTE.ActiveDocument.Selection.Text = vbTab Next j DTE.ActiveDocument.Selection.LineDown(False) ' go down a line ElseIf (Len(var(i)) > 1) Then DTE.ActiveDocument.Selection.LineDown(False) ' go down a line End If Next i 'replace = "" ' generate the replacement text 'For i = 0 To UBound(var) ' numChars = var(i).IndexOf("//") ' If (numChars > 0 And Len(var(i)) > 3) Then ' beforeComment = StringFs_SubstrSafe(var(i), 0, numChars) ' comment = Mid(var(i), numChars + 1) ' numTabs = StringFs_CountOccurances(beforeComment, vbTab) ' effLen = numChars + (numTabs * 3) ' numTabsNeeded = Int((maxLen - effLen + 3.0) / 4.0) ' var(i) = beforeComment ' For j = 1 To numTabsNeeded ' var(i) = var(i) + vbTab ' Next j ' var(i) = var(i) + comment + vbNewLine ' End If ' replace = replace + var(i) 'Next i 'DTE.ActiveDocument.Selection.Text = replace ' replace selected text Return True End Function '============================================================ ' DOCUMENT FIND / REPLACE FUNCTIONS: '============================================================ '------------------------------------------- ' Performs a "replace all operation" on a SELECTION only ' Function ReplaceAllSelection(ByVal findStr As String, ByVal replaceStr As String, ByVal useRegex As Boolean) DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection ' current selection only DTE.Find.FindWhat = findStr DTE.Find.ReplaceWith = replaceStr DTE.Find.MatchCase = True DTE.Find.MatchWholeWord = False DTE.Find.MatchInHiddenText = True If (useRegex = True) Then DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxRegExpr Else DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral End If DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone DTE.Find.Action = vsFindAction.vsFindActionReplaceAll DTE.Find.Execute() End Function '------------------------------------------- ' Performs a "replace all operation" on a SELECTION only ' Function ReplaceAll(ByVal findStr As String, ByVal replaceStr As String, ByVal useRegex As Boolean) DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocument ' entire current document DTE.Find.FindWhat = findStr DTE.Find.ReplaceWith = replaceStr DTE.Find.MatchCase = True DTE.Find.MatchWholeWord = False DTE.Find.MatchInHiddenText = True If (useRegex = True) Then DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxRegExpr Else DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral End If DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone DTE.Find.Action = vsFindAction.vsFindActionReplaceAll DTE.Find.Execute() End Function '============================================================ ' DOCUMENT SELECTION FUNCTIONS: '============================================================ '------------------------------------------- ' Gets all the text in the document and returns it as a string ' Private Function Doc_GetAllTextInDocAsString() As String Dim selString As String DTE.ActiveDocument.Selection.SelectAll() selString = DTE.ActiveDocument.Selection.Text DTE.ActiveDocument.Selection.StartOfDocument() Return selString End Function '------------------------------------------- ' Returns true is a selection is made ' Private Function Doc_IsSelectionMade() As Boolean Return (DTE.ActiveDocument.Selection.Text.Length() > 0) End Function '------------------------------------------- ' Counts the number of characters in a document ' Public Function Doc_NumCharacters() As Long Doc_NumCharacters = Doc_GetAllTextInDocAsString.Length() End Function '------------------------------------------- ' Overwrites (deletes and replaces) the entire document with the given string ' Public Function Doc_ReplaceAllText(ByVal newText As String) ActiveDocument.Selection.WholeStory() ActiveDocument.Selection.Delete() ActiveDocument.Selection.TypeText(newText) End Function '============================================================ ' STRING FUNCTIONS: '============================================================ '------------------------------------------- ' Similar to Mid(), but uses index position (starting at 0) for start ' and also adjusts start & length values such that they can't be invalid Public Function StringFs_SubstrSafe(ByVal str As String, ByVal start As Integer, ByVal length As Integer) As String Dim strLen As Integer strLen = Len(str) start = start + 1 If (start > strLen) Then 'NOTE: mid function uses indexes starting at 1... which sucks, but have to work with it! start = strLen length = 0 End If If (length > (strLen + 1) - start) Then length = (strLen + 1) - start End If start = MathFs_Max(start, 1) 'can't be less than 1 length = MathFs_Max(length, 0) 'can't be less than 0 StringFs_SubstrSafe = Mid(str, start, length) End Function '------------------------------------------- ' Returns substring, of specified length, from end of string Public Function StringFs_SubstrFromEnd(ByVal str As String, ByVal length As Integer) As String Dim strLen, start As Integer strLen = Len(str) If (length > strLen) Then length = strLen End If start = strLen - length 'NOTE: mid function uses indexes starting at 1... which sucks, but have to work with it! start = MathFs_Max(start, 1) 'can't be less than 1 length = MathFs_Max(length, 0) 'can't be less than 0 StringFs_SubstrFromEnd = StringFs_SubstrSafe(str, start, length) End Function '------------------------------------------- Public Function StringFs_CountOccurancesInWholeDoc(ByVal needle As String) As Integer Dim storyStr As String Dim storyLen As Integer storyStr = Doc_GetAllTextInDocAsString() storyLen = StringFs_CountOccurances(storyStr, needle) StringFs_CountOccurancesInWholeDoc = storyLen End Function '------------------------------------------- ' Replaces all occurences of "needle" in "haystack" with "replaceWith" ' Public Function StringFs_MyReplace(ByVal haystack As String, ByVal needle As String, ByVal replaceWith As String) As String Dim pos, start, needleLen, replaceLen As Integer needleLen = Len(needle) replaceLen = Len(replaceWith) pos = InStr(haystack, needle) While pos haystack = Left$(haystack, pos - 1) & replaceWith & Mid$(haystack, pos + needleLen) pos = InStr(pos + replaceLen, haystack, needle) End While StringFs_MyReplace = haystack End Function '------------------------------------------- ' Counts all occurences of "needle" in "haystack" and returns this as an integer ' Public Function StringFs_CountOccurances(ByVal haystack As String, ByVal needle As String) As Integer Dim pos, start, needleLen, numFound As Integer numFound = 0 needleLen = Len(needle) pos = InStr(haystack, needle) While pos numFound = numFound + 1 pos = InStr(pos + needleLen, haystack, needle) End While StringFs_CountOccurances = numFound End Function '------------------------------------------- ' Counts all occurences of char "needle" at the start of "haystack" ' Public Function StringFs_CountOccurancesCharsAtStart(ByVal haystack As String, ByVal needle As String) As Integer Dim numFound As Integer For i = 1 To Len(haystack) If Not (Mid(haystack, i, 1) = needle) Then Exit For End If Next i StringFs_CountOccurancesCharsAtStart = (i - 1) End Function '=============================================================================================================== ' REGEX STRING FUNCTIONS: '=============================================================================================================== '------------------------------------------- ' The Macros use "Regular Expressions". ' TO ENABLE ' ' go: Tools >> References ... ' scroll down to "Microsoft VB" ' ' EXAMPLES OF USE: ' "

\s*([\s\S]*?)\s*

" - find

tags ' ' MORE INFO: ' > http://en.wikipedia.org/wiki/Regular_expression ' > http://en.wikibooks.org/wiki/Programming:Visual_Basic_Classic/Regular_Expressions ' > http://msdn.microsoft.com/library/default.asp?url=/library/en-us/script56/html/3f3e319c-18c2-46df-90b2-023d6a53dac9.asp ' '------------------------------------------- '------------------------------------------- ' Replaces any text in "haystack" matching the regex pattern "regExFindPattern" ' with "replaceStr" ' Public Function RegStr_Replace(ByVal haystack As String, ByVal regExFindPattern As String, ByVal replaceStr As String) As String Dim newString As String Dim myRegExp As Regex 'myRegExp = New Regex 'myRegExp.Multiline = True 'myRegExp.Global = True 'myRegExp.Pattern = regExFindPattern newString = myRegExp.Replace(haystack, regExFindPattern, replaceStr) RegStr_Replace = newString End Function '------------------------------------------- ' Returns a MatchCollection with all matches to the regex pattern "regExFindPattern" ' Public Function RegStr_GetMatches(ByVal haystack As String, ByVal regExFindPattern As String) As MatchCollection Dim myRegExp As Regex 'myRegExp.Options.IgnoreCase = True 'myRegExp.Global = True 'myRegExp.Options.Pattern = regExFindPattern Dim myMatches As MatchCollection myMatches = myRegExp.Matches(haystack, regExFindPattern, RegexOptions.IgnoreCase) RegStr_GetMatches = myMatches End Function '------------------------------------------- ' Returns a MatchCollection with all matches to the regex pattern "regExFindPattern" ' Public Function RegStr_CountMatches(ByVal haystack As String, ByVal regExFindPattern As String) As Integer 'Dim myMatches As MatchCollection 'myMatches = RegStr_CountMatches = Regex.Matches(haystack, regExFindPattern).Count() End Function '============================================================ ' MATHS FUNCTIONS: '============================================================ '------------------------------------------- ' Takes two integers and returns the lager value ' Public Function MathFs_Max(ByVal num1 As Integer, ByVal num2 As Integer) As Integer Dim retVal As Integer If (num1 > num2) Then retVal = num1 Else retVal = num2 End If MathFs_Max = retVal End Function '------------------------------------------- ' Converts an integer to a double ' Public Function Convert_Int2Double(ByVal num As Integer) As Double Dim retVal As Double retVal = num Convert_Int2Double = retVal End Function '============================================================ ' OTHER FUNCTIONS: '============================================================ '------------------------------------------- ' Displays an alert message box with the provided message string ' Public Function AlertMsg(ByVal message As String) MsgBox(message, vbOKOnly, "Alert") End Function '------------------------------------------- ' Displays an yes no message and returns true if the user clicks "yes" ' Public Function MsgYesNo(ByVal message As String) As Boolean Dim result As Integer = MsgBox(message, MsgBoxStyle.YesNo, "Alert") Return (result = MsgBoxResult.Yes) End Function '============================================================ ' OTHER FUNCTIONS: (NOT MINE) '============================================================ Private Function ActiveWindowSelection() As String If DTE.ActiveWindow.ObjectKind = EnvDTE.Constants.vsWindowKindOutput Then Return OutputWindowSelection() End If Return SelectionText(DTE.ActiveWindow.Selection) ' FUNCTION CALL End Function Private Function OutputWindowSelection() As String Dim w As Window = DTE.Windows.Item(EnvDTE.Constants.vsWindowKindOutput) Dim ow As OutputWindow = w.Object Dim owp As OutputWindowPane = ow.OutputWindowPanes.Item(ow.ActivePane.Name) Return SelectionText(owp.TextDocument.Selection) ' FUNCTION CALL End Function Private Function SelectionText(ByVal sel As EnvDTE.TextSelection) As String If sel Is Nothing Then Return "" End If If sel.Text.Length = 0 Then SelectWord(sel) ' FUNCTION CALL End If If sel.Text.Length <= 2 Then Return "" End If Return sel.Text End Function Private Sub SelectWord(ByVal sel As EnvDTE.TextSelection) Dim leftPos As Integer Dim line As Integer Dim pt As EnvDTE.EditPoint = sel.ActivePoint.CreateEditPoint() sel.WordLeft(True, 1) line = sel.TextRanges.Item(1).StartPoint.Line leftPos = sel.TextRanges.Item(1).StartPoint.LineCharOffset pt.MoveToLineAndOffset(line, leftPos) sel.MoveToPoint(pt) sel.WordRight(True, 1) End Sub End Module