Click here to Skip to main content
15,879,535 members
Articles / Programming Languages / C++
Article

Reformat source code

Rate me:
Please Sign up or sign in to vote.
4.97/5 (34 votes)
26 Sep 20055 min read 409.3K   3.8K   82   127
A great macro for reformatting C++ source code.

Introduction

MakeCodeNicer began as a Visual C++ 6.0 macro used for reformatting C/C++ source files on the fly, right inside the editor. It was used by developers who needed to quickly reformat source files (usually written by other people) for their own use.

Visual Studio 2002 and 2003 introduced a new macro engine, which unfortunately broke MakeCodeNicer. Anders Dalvander was kind enough to convert the code so that it would compile, but for some reason, it didn't quite work as expected.

Well, I've finally taken some time to finish Anders' port so that this macro can once again be used inside Microsoft's development environment. I'm going to keep the VC6 version available for download, but since I don't have VC6 installed, I can't support that version any more.

Installation

To install the VS.NET version:

  1. For 2003, extract the zip file to your "My Documents\Visual Studio Projects\VSMacros71\". (For 2005 and later, please see the Notes section below.) Be sure to "Use folder names" when extracting.
  2. Inside Visual Studio, go to Tools/Macros/Load Macro Project.
  3. Select the ReformatCode.vsmacros file (inside the ReformatCode folder).
  4. In the Macro Explorer, expand the ReformatCode module and then the MakeCodeNicer macro.
  5. You'll see the SelectionOnly and WholeFile methods.
  6. Open a C, C++, C#, Java, or JavaScript source file to be reformatted. Make sure it compiles successfully!
  7. If you want the whole file to be reformatted, double click on the WholeFile method and sit back to let it do its thing. While it's running, you'll see an animated cassette in the system tray, which can be right-clicked to interrupt the macro.
  8. If you only want to reformat a portion of the file, select the portion, and double click on the SelectionOnly method.

What it does

Once the macro is done, you'll see that it does several things to your source code:

  • Adds extra spaces in a few places and removes them from other places:
    • Appends a space to if, for, foreach, while, switch, catch, return, throw, lock, and using statements.
    • Removes any spaces before or after method parenthesis.
    • Adds a space before and after operators (such as =, ==, etc.).
    • Appends a space to commas and semicolons (inside for loops).
    • Inserts a space between the // and the first character of inline comments.
  • Breaks single-line if, for, foreach, and while statements into multiple lines (for easier debugging).
  • Breaks up case statements into their own lines and indents them if necessary.
  • Isolates braces ({ }) into lines by themselves.
  • Removes trailing white space from all lines in the file.
  • Removes extraneous blank lines.
  • Accounts for code within quoted strings or characters.
  • Ignores code after inline comments (//).

If you've worked with MFC in the past and followed its conventions, you'll no doubt recognize this macro's formatting. It's clean and very easy to read, in my opinion.

Notes

I ported and tested this macro using C# source files, but it should work with any of the C-derived languages. If you find any problems, please let me know.

I'm also happy to report that Visual Studio .NET 2005 finally makes this macro obsolete. If you need to reformat source code, simply press Ctrl+E, D and the file will be reformatted according to your settings (inside Tools/Options - Text Editor/Language/Formatting).

Enjoy it!

Updates

5 May 2000

  • Fixed cases when code gets incorrectly unindented if one or more curly braces appear inside quoted strings. (Thanks to Bob Eastman for reporting it.)
  • Fixed cases when "operator=(...)" turns into "operator =(...)".
  • Added more up-front validations to ensure the macro can be run properly.
  • Made the macro execute on a temporary file which is then copied onto the target source code. This allows undoing of all of the macro's changes in one step. (Thanks to Nicolas Fleury for suggesting it.)

16 Feb 2001

  • Added a check for Visual C++'s "out of memory" bug which occurs when the macro is invoked on multiple very-large source files. (Thanks to Aaron Sulwer for reporting and helping me test this problem.)
  • Added code to remove spaces before and after -> operators. (Thanks to Dominic Holmes for suggesting it.)
  • Fixed erroneous space removals before certain opening parenthesis and after inlined functions. (Thanks to Joe Woodbury for reporting these.)

26 Apr 2001

  • Changed the logic that fixes less than and greater than operators to only do so when they're inside "for", "while", and "if" statements. Since these operators have so many uses in C++, it's very difficult to accurately fix them across the board. Thanks to Robin Summerhill and Thomas Freudenberg for bringing these to my attention.
  • Replaced Int() with CInt() in two locations. This should prevent a type mismatch error reported by Henning Flessner.

9 July 2002

  • Stopped multi-line preprocessor macros from being reformatted. (Thanks to Niels Harremoës for reporting it.)
  • Fixed improper splitting of lines with multiple semicolons. (Thanks to Jon for reporting it.)
  • Adjusted the FILE DESCRIPTION comment to allow the macro's description to be displayed on the "Add-ins and Macro Files" tab of the Customize box. (Thanks to Michael Martin for reporting it.)

15 Jul 2002

  • Fixed possible Type Mismatch error. (Thanks to henning flessner for reporting it and providing the solution.)
  • Stopped code containing "','," from turning into "', ',". (Thanks to Dirk for reporting it.)

16 Aug 2005

  • Ported to Visual Studio .NET 2003. Thanks to Anders Dalvander for giving me a great jump start in the effort.
  • I converted all variables to be type specific to improve performance. I also created a local variable in most methods to cache the ActiveDocument.Selection.
  • I fixed several minor issues, thanks to the new ability to debug (woo hoo).

15 Sep 2005

  • Fixed conversion of < and > operators when used for template notation. (Thanks to Eddie Parker for reporting the problem.)
  • Ported to Visual Studio .NET 2005 (Beta 2). Now all it does is run the "SmartFormat" method (which can easily be invoked with Ctrl+E, D). This macro has finally become obsolete. :-)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
I've done extensive work with C++, MFC, COM, and ATL on the Windows side. On the Web side, I've worked with VB, ASP, JavaScript, and COM+. I've also been involved with server-side Java, which includes JSP, Servlets, and EJB, and more recently with ASP.NET/C#.

Comments and Discussions

 
GeneralRe: Alternative Pin
Alvaro Mendez28-Sep-05 14:22
Alvaro Mendez28-Sep-05 14:22 
GeneralLooks like my code is already pretty nice Pin
ComputerGuyCJ27-Sep-05 11:39
ComputerGuyCJ27-Sep-05 11:39 
GeneralError during Add Document Pin
rh27-Sep-05 2:18
rh27-Sep-05 2:18 
GeneralRe: Error during Add Document Pin
Alvaro Mendez28-Sep-05 14:26
Alvaro Mendez28-Sep-05 14:26 
GeneralRe: Error during Add Document Pin
RuslanKulubaev9-Jul-07 21:59
RuslanKulubaev9-Jul-07 21:59 
GeneralProblem on var declaration Pin
Stephan Pilz26-Sep-05 19:35
Stephan Pilz26-Sep-05 19:35 
QuestionTemplates &amp; For Loops? Pin
Member 227599414-Sep-05 11:49
Member 227599414-Sep-05 11:49 
QuestionHow about this? Pin
Matt Godbolt14-Sep-05 7:23
Matt Godbolt14-Sep-05 7:23 
GeneralRemoval of &quot;non&quot; blank blank lines Pin
Jim Fougeron30-Apr-04 9:26
sussJim Fougeron30-Apr-04 9:26 
GeneralHandling c++ and c multiline comments Pin
Member 86675722-Apr-04 9:00
Member 86675722-Apr-04 9:00 
GeneralRe: Handling c++ and c multiline comments - correction Pin
Member 86675722-Apr-04 9:10
Member 86675722-Apr-04 9:10 
GeneralRe: Handling c++ and c multiline comments - correction - correction Pin
Member 86675722-Apr-04 9:19
Member 86675722-Apr-04 9:19 
GeneralPorted to VS.NET 7.1 (long post) Pin
Anders Dalvander17-Jul-03 23:50
Anders Dalvander17-Jul-03 23:50 
It doesn't work 100% yet, but I haven't the time to fix it right now. If you fix anything with it, post it here.

'------------------------------------------------------------------------------
'FILE DESCRIPTION: Routines to reformat C/C++ source code.
'Created by Alvaro Mendez - June 10, 1999
'Ported to VS.NET 7.1 by Anders Dalvander - July 18, 2003
'Last Updated: July 18, 2003
'------------------------------------------------------------------------------

Option Explicit On
Imports EnvDTE
Imports System.Diagnostics
Imports Microsoft.VisualStudio.VCProjectEngine
Imports System

Public Module MakeCodeNicer

Sub MakeCodeNicer()
'DESCRIPTION: Reformats the source code to look nicer, the way I like it.

' Check that we have a valid source file
If Not CheckActiveDocument() Then
Exit Sub
End If

' Select the whole file and reformat it
ActiveDocument.Selection.SelectAll()
ReformatSelection()

End Sub

Sub MakeSelectedCodeNicer()
'DESCRIPTION: Reformats the currently selected source code to look nicer.

' Check that we have a valid source file
If Not CheckActiveDocument() Then
Exit Sub
End If

' Check that there's a valid selection
If ActiveDocument.Selection.Text = "" Then
ShowError("NoSelection")
Exit Sub
End If

' Reformat the current selection
ReformatSelection()

End Sub


' Verify there's an active document and that it's valid
Function CheckActiveDocument()

CheckActiveDocument = False

' Check that there's a file to reformat
If ActiveDocument Is Nothing Then
ShowError("NoFile")
Exit Function
End If

' Check that it's at least a text file
If ActiveDocument.Type <> "Text" Then
ShowError("InvalidFile")
Exit Function
End If

CheckActiveDocument = True

End Function

' Show one of the predefined errors in a message box.
Sub ShowError(ByVal strType)
Dim strMessage

Select Case strType

Case "NoFile"
strMessage = "Please open a C/C++ source file " & _
"and run this macro again."

Case "InvalidFile"
strMessage = "Please activate a window containing " & _
"C/C++ source code and run this macro again."

Case "NoSelection"
strMessage = "Please select the source code you want to " & _
"reformat and run this macro again."

Case "OutOfMemory"
strMessage = "This macro is not working properly because " & _
"Visual C++ has apparently run out of memory." & _
vbLf & vbLf & "Unfortunately this is a bug in " & _
"Visual C++. It typically manifests itself " & _
"whenever the macro is run multiple times on very " & _
"large files, inside the same Visual C++ session. " & _
"The only way around it is to open a new Visual " & _
"C++ session for each large file you want to " & _
"reformat." & vbLf & vbLf & "Press OK to go back " & _
"to the original file. You should then exit Visual C++."
Case Else
strMessage = "Unknown Error of type: " & strType

End Select

MsgBox(strMessage, vbExclamation)

End Sub

' Reformat the currently selected source code (if any)
Function ReformatSelection()

' Check if there's something selected
If ActiveDocument.Selection.Text = "" Then
Exit Function
End If

' Copy the selection to the clipboard
ActiveDocument.Selection.Copy()

' Open a new document and change its language to C/C++
' This is required for SmartIndent to work.
DTE.Documents.Add("Text")
ActiveDocument.Language = "C/C++"

' Paste the selection into the new document
ActiveDocument.Selection.Paste()

' Reformat it!
If Not ReformatActiveDocument() Then
ActiveWindow.Close(vsSaveChanges.vsSaveChangesNo)
Exit Function
End If

' Select the resulting code and copy it to the clipboard
ActiveDocument.Selection.SelectAll()
ActiveDocument.Selection.Copy()

' Close the new document (without saving it)
ActiveWindow.Close(vsSaveChanges.vsSaveChangesNo)

' Paste the reformatted code back over the original selection
ActiveDocument.Selection.Paste()

End Function

' Reformat the source code in the currently active file (document)
Function ReformatActiveDocument()

ReformatActiveDocument = False

' Add spaces in a few places and get rid of it in others
Replace(":b+;", ";")
Replace(":b+\:\::b+", "::")
Replace(":b+\(", "(")
Replace(" =\(", " = (")
Replace("if\(", "if (")
Replace("for\(", "for (")
Replace("while\(", "while (")
Replace("switch\(", "switch (")
Replace("catch\(", "catch (")
Replace("return\(", "return (")
Replace("\(:b+", "(")
Replace(":b+\)", ")")
'Replace(";\)", "; )")
Replace(";;:b+\)", ";;)")
Replace("\[:b+", "[")
Replace(":b+\]", "]")
Replace(":b+\[", "[")
Replace(":b+->", "->")
Replace("->:b+", "->")
Replace("\|\(", "| (")
Replace("&\(", "& (")
Replace("\+\(", "+ (")
Replace("\-\(", "- (")
Replace("\*\(", "* (")
Replace("/\(", "/ (")

' Make sure these statements don't end on the same line they started.
BreakSingleLiners("if (")
BreakSingleLiners("for (")
BreakSingleLiners("switch (")

' Break up any lines containing multiple statements
BreakLinesWithMultipleStatements()

' Make sure braces are on lines by themselves (unless followed by comments)
IsolateOnLeft("\{")
IsolateOnRight("\{")
IsolateOnRight("\}")
IsolateOnLeft("\}")

' Break up simple else statements on the same line (except "else if")
Replace("else:b+if \(", "elseif(")
IsolateOnRight("else:b+")
Replace("elseif\(", "else if (")

' Break up case statements appearing on single lines
IsolateOnRight("case .+\::b+")
IsolateOnRight("default\::b+")
IsolateOnLeft("break;")

' Add a space between these operators
FixOperator("=", 1)
FixOperator("==", 2)
FixOperator("!=", 2)
FixOperator("\+=", 2)
FixOperator("-=", 2)
FixOperator("\*=", 2)
FixOperator("/=", 2)
FixOperator("\+", 1)
FixOperator("-", 1)
FixOperator("<=", 2)
FixOperator(">=", 2)
FixOperator("<<", 2)
FixOperator(">>", 2)
FixOperator("&&", 2)
FixOperator("\|\|", 2)
FixOperator("\|", 1)
FixOperator("\?", 1)
FixLessThanAndGreaterThanOperators()
FixExponents()

' Append a space after these
AppendSpace(",")
AppendSpace(";")

' Make sure the first C++ comment of every line has a space after it.
InsertSpaceAfterFirstInLineComment()

' Replace all the trailing whitespace (thanks to Paul Bludov)
ActiveDocument.Selection.ReplaceText(":b+{$}", "\1", vsFindOptions.vsFindOptionsRegularExpression)

' Run Smart Indent on function blocks only
SmartIndentFunctionBlocks()

' Remove any lines that are considered extraneous (ie. extra blank lines)
If Not RemoveExtraneousLines() Then
Exit Function
End If

' Indent every "case" inside switch statements (thanks to Jim Cooper)
IndentSwitchBody()

' Return OK
ReformatActiveDocument = True

End Function

' Is the cursor currently within a quoted string (or character)
Function IsWithinQuotes()
Dim nCurrentLine, nCurrentColumn, iPos, strBuffer, nCount

nCurrentLine = ActiveDocument.Selection.CurrentLine
nCurrentColumn = ActiveDocument.Selection.CurrentColumn

ActiveDocument.Selection.Cancel()
ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, True)

nCount = 0
iPos = 0
strBuffer = ActiveDocument.Selection.Text

' Count all occurrences of a double quote which apply to quoted strings
Do While True
iPos = InStr(iPos + 1, strBuffer, """", vbTextCompare)
If Not (iPos > 0) Then
Exit Do
End If

If iPos = 1 Then ' if it's the first character, then it's valid
nCount = nCount + 1
Else
' Make sure it's not preceded by a \ or a \\
If Mid(strBuffer, iPos - 1, 1) <> "\" Then
nCount = nCount + 1
ElseIf (iPos > 2) And (Mid(strBuffer, iPos - 2, 1) = "\") Then
nCount = nCount + 1
End If
End If
Loop

' If number of quotes is odd, we must be inside a quoted string!
IsWithinQuotes = ((nCount > 0) And ((nCount Mod 2) <> 0))

ActiveDocument.Selection.MoveTo(nCurrentLine, nCurrentColumn)

' If we're not inside a quoted string, check for a quoted character
If Not IsWithinQuotes Then
ActiveDocument.Selection.CharLeft(True)

' If we find a quoted character left of us, check for one on the right
If ActiveDocument.Selection.Text = "'" Then
ActiveDocument.Selection.CharRight()
ActiveDocument.Selection.CharRight(True)

If ActiveDocument.Selection.Text = "\" Then
ActiveDocument.Selection.CharRight()
ActiveDocument.Selection.CharRight(True)
End If
ActiveDocument.Selection.CharRight()
ActiveDocument.Selection.CharRight(True)

If ActiveDocument.Selection.Text = "'" Then
IsWithinQuotes = True
End If
End If
ActiveDocument.Selection.MoveTo(nCurrentLine, nCurrentColumn)
End If

' If we're inside quotes, proceed from the next character
If IsWithinQuotes Then
ActiveDocument.Selection.CharRight()
End If

End Function

' Is current selection preceded by a C++ comment? ("//")
Function IsWithinComment()
Dim nCurrentLine, nCurrentColumn

nCurrentLine = ActiveDocument.Selection.CurrentLine
nCurrentColumn = ActiveDocument.Selection.CurrentColumn

ActiveDocument.Selection.Cancel()
ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, True)

IsWithinComment = False
If (InStr(1, ActiveDocument.Selection.Text, "//", vbTextCompare) > 0) Then
IsWithinComment = True ' since it's commented out
nCurrentLine = nCurrentLine + 1 ' we proceed from the next line
End If

ActiveDocument.Selection.MoveTo(nCurrentLine, 1)

End Function

' Is the cursor on a line with a preprocessor macro? ("#define ")
Function IsPreprocessorMacro()
Dim nCurrentLine, nCurrentColumn

nCurrentLine = ActiveDocument.Selection.CurrentLine
nCurrentColumn = ActiveDocument.Selection.CurrentColumn

IsPreprocessorMacro = False
ActiveDocument.Selection.EndOfLine(True)

If (Mid(LTrimTabs(ActiveDocument.Selection.Text), 1, 8) = "#define ") Or _
(Right(ActiveDocument.Selection.Text, 1) = "\") Then
IsPreprocessorMacro = True ' since it's in a macro
nCurrentLine = nCurrentLine + 1 ' we proceed from the next line
End If

ActiveDocument.Selection.MoveTo(nCurrentLine, nCurrentColumn)

End Function

' Should the current selection be ignored?
' (ie., is it within a comment, between quotes, or inside a macro?)
Function ShouldIgnore()

ShouldIgnore = False

If IsWithinQuotes() Then
ShouldIgnore = True
Exit Function
End If

If IsWithinComment() Then
ShouldIgnore = True
Exit Function
End If

If IsPreprocessorMacro() Then
ShouldIgnore = True
End If

End Function

' Put the cursor at the top of the document and return "" to be passed
' initially to GetCurrenPosition
Function InitializePosition()
ActiveDocument.Selection.StartOfDocument()
InitializePosition = ""
End Function

' Retrieve the current position and return true if it's greater than the
' last one. This is used to ensure that the file is only searched once
' (provided the search is started from the top)
Function GetCurrentPosition(ByRef strPos)
Dim nLastLine, nLastColumn, nCurrentLine, nCurrentColumn, iPos, ch

nLastLine = -1
nLastColumn = -1

nCurrentLine = ActiveDocument.Selection.CurrentLine
nCurrentColumn = ActiveDocument.Selection.CurrentColumn

' Parse the last position and extract the line and column
For iPos = 1 To Len(strPos)
ch = Mid(strPos, iPos, 1)
If ch = "," Then
nLastLine = CInt(Mid(strPos, 1, iPos - 1))
nLastColumn = CInt(Mid(strPos, iPos + 1))
Exit For
End If
Next

' Return true if we're currently past the last position
strPos = nCurrentLine & "," & nCurrentColumn
GetCurrentPosition = (nCurrentLine > nLastLine) Or _
((nLastLine = nCurrentLine) And (nCurrentColumn > nLastColumn))

End Function

' Move by a certain number of columns
Sub MoveByColumns(ByVal nBy)
ActiveDocument.Selection.MoveTo(ActiveDocument.Selection.CurrentLine, _
ActiveDocument.Selection.CurrentColumn + nBy)
End Sub

' Replace the given strFrom with strTo case sensitively
Sub Replace(ByVal strFrom, ByVal strTo)
Dim strLastPos, bContinue

strLastPos = InitializePosition()
Do While ActiveDocument.Selection.FindText(strFrom, _
vsFindOptions.vsFindOptionsMatchCase + vsFindOptions.vsFindOptionsRegularExpression)

bContinue = GetCurrentPosition(strLastPos)

' Check if we're inside a comment or between quotes
If Not ShouldIgnore() Then

' Repeat the search since ShouldIgnore puts the cursor at
' the beginning of the line
ActiveDocument.Selection.FindText(strFrom, _
vsFindOptions.vsFindOptionsMatchCase + vsFindOptions.vsFindOptionsRegularExpression)
ActiveDocument.Selection.Text = strTo

ElseIf Not bContinue Then
Exit Do
End If
Loop

End Sub

' Break the given str ending in (, so that instead of this:
' if (a) return b;
' it looks like this:
' if (a)
' return b;
Sub BreakSingleLiners(ByVal str)
Dim strLastPos, strFound, nCol, bBreak, strAfterFound

' Verify str ends in (, the beginning parenthesis
If Right(str, 1) <> "(" Then
Exit Sub
End If

strLastPos = InitializePosition()

While ActiveDocument.Selection.FindText(str, vsFindOptions.vsFindOptionsMatchCase) And _
GetCurrentPosition(strLastPos)

' Check if we're inside a comment or between quotes
If Not ShouldIgnore() Then

' Repeat the search since ShouldIgnore puts the cursor at the
' beginning of the line
ActiveDocument.Selection.FindText(str, vsFindOptions.vsFindOptionsMatchCase)

' Find the matching brace and go to the end of the line
ActiveDocument.Selection.CharRight()
ActiveDocument.Selection.CharLeft()
ExecuteCommand("Edit.GotoBrace")
ActiveDocument.Selection.CharRight()
nCol = ActiveDocument.Selection.CurrentColumn
ActiveDocument.Selection.EndOfLine(True)
strFound = LTrimTabs(ActiveDocument.Selection.Text)

' If there's anything after the brace that isn't a comment, move
' it to the next line
If (Len(strFound) > 0) And (Left(strFound, 1) <> "/") Then
bBreak = False

' Check if there's a "{" after the statement which should
' also be broken into multiple lines
If (Mid(strFound, 1, 1) = "{") And (Len(strFound) > 1) Then
strAfterFound = LTrimTabs(Mid(strFound, 2))
If strAfterFound <> "" Then
ActiveDocument.Selection.Text = "{" + strAfterFound
ActiveDocument.Selection.MoveTo( _
ActiveDocument.Selection.CurrentLine, nCol)
ActiveDocument.Selection.NewLine()
ActiveDocument.Selection.CharRight()
ActiveDocument.Selection.NewLine()

bBreak = True ' primitive but it works
End If
End If

If Not bBreak Then
ActiveDocument.Selection.Text = strFound
ActiveDocument.Selection.MoveTo( _
ActiveDocument.Selection.CurrentLine, nCol)
ActiveDocument.Selection.NewLine()
End If
End If
End If
End While

End Sub

' Trim blanks AND tabs from the left side
Function LTrimTabs(ByVal str)
Dim iPos, ch

For iPos = 1 To Len(str)
ch = Mid(str, iPos, 1)
If ch <> " " And ch <> vbTab Then
Exit For
End If
Next

LTrimTabs = Mid(str, iPos)

End Function

' Isolate the given str on a new line with nothing left of it
Sub IsolateOnLeft(ByVal str)
Dim strLastPos, nLen, bContinue, nCurrentLine, nCurrentColumn

strLastPos = InitializePosition()

While ActiveDocument.Selection.FindText("^.*" & str, vsFindOptions.vsFindOptionsRegularExpression + _
vsFindOptions.vsFindOptionsMatchCase) And GetCurrentPosition(strLastPos)

' Check if we're inside a comment or between quotes
If Not ShouldIgnore() Then

' Repeat the search since ShouldIgnore puts the cursor at the
' beginning of the line
ActiveDocument.Selection.FindText("^.*" & str, _
vsFindOptions.vsFindOptionsRegularExpression + vsFindOptions.vsFindOptionsMatchCase)

' Get the length of the found string
' (which may have been a regular expression)
ActiveDocument.Selection.CharRight()
ActiveDocument.Selection.FindText(str, _
vsFindOptions.vsFindOptionsBackwards + vsFindOptions.vsFindOptionsRegularExpression + vsFindOptions.vsFindOptionsMatchCase)
nLen = Len(ActiveDocument.Selection.Text)

ActiveDocument.Selection.CharLeft()
If Not ShouldIgnore() Then

' Now that we have the length, we need to redo
' the search and go on
ActiveDocument.Selection.StartOfLine()
ActiveDocument.Selection.FindText("^.*" & str, _
vsFindOptions.vsFindOptionsRegularExpression + vsFindOptions.vsFindOptionsMatchCase)

bContinue = False

' If we're isolating a brace, make sure its matching brace
' isn't on the same line
If (str = "{") Or (str = "}") Then
ActiveDocument.Selection.CharRight()
nCurrentLine = ActiveDocument.Selection.CurrentLine
nCurrentColumn = ActiveDocument.Selection.CurrentColumn
ActiveDocument.Selection.CharLeft()

ExecuteCommand("Edit.GotoBrace")
If ActiveDocument.Selection.CurrentLine = nCurrentLine Then
ActiveDocument.Selection.MoveTo( _
nCurrentLine, nCurrentColumn)
bContinue = True ' we found it so move to the next match
Else
ActiveDocument.Selection.MoveTo(nCurrentLine, 1)
ActiveDocument.Selection.FindText("^.*" & str, _
vsFindOptions.vsFindOptionsRegularExpression + vsFindOptions.vsFindOptionsMatchCase)
End If
End If


If LTrimTabs(ActiveDocument.Selection.Text) <> str And _
Not bContinue Then
ActiveDocument.Selection.CharRight()
MoveByColumns(-nLen)
ActiveDocument.Selection.NewLine()
MoveByColumns(nLen)
End If

GetCurrentPosition(strLastPos)
End If
End If

End While

End Sub

' Isolate the given str so that everything after it is placed on the next line
Sub IsolateOnRight(ByVal str)
Dim strLastPos, strRight, nCurrentLine, nCurrentColumn, nLen

strLastPos = InitializePosition()

While ActiveDocument.Selection.FindText(str & ".+$", _
vsFindOptions.vsFindOptionsRegularExpression + vsFindOptions.vsFindOptionsMatchCase) And GetCurrentPosition(strLastPos)

' Check if we're inside a comment or between quotes
ActiveDocument.Selection.CharLeft()
If Not ShouldIgnore() Then

' Repeat the search since ShouldIgnore puts the cursor at the
' beginning of the line
ActiveDocument.Selection.FindText(str & ".+$", _
vsFindOptions.vsFindOptionsRegularExpression + vsFindOptions.vsFindOptionsMatchCase)

' Get the length of the found string
' (which may have been a regular expression)
ActiveDocument.Selection.CharLeft()
ActiveDocument.Selection.FindText(str, vsFindOptions.vsFindOptionsRegularExpression + vsFindOptions.vsFindOptionsMatchCase)
nLen = Len(ActiveDocument.Selection.Text)

' Now that we have the length, we need to redo the search and go on
ActiveDocument.Selection.CharLeft()
ActiveDocument.Selection.FindText(str & ".+$", _
vsFindOptions.vsFindOptionsRegularExpression + vsFindOptions.vsFindOptionsMatchCase)

strRight = LTrimTabs(Mid(ActiveDocument.Selection.Text, nLen + 1))

' Handle braces a bit differently
If (str = "{") Or (str = "}") Then

' If it's a closing brace, and the code after it contains
' a semicolon, leave it alone (ie. variable definition).
If (str = "}") Then
ActiveDocument.Selection.EndOfLine(True)

If (InStr(1, ActiveDocument.Selection.Text, ";", vbTextCompare) _
> 0) Then
strRight = "" ' we found it so move on to the next match
End If
ActiveDocument.Selection.CharLeft()
ActiveDocument.Selection.CharRight()
End If

' If we're isolating a brace make sure the matching brace
' isn't on the same line
If (strRight <> "") Then
ActiveDocument.Selection.CharLeft()
nCurrentLine = ActiveDocument.Selection.CurrentLine
nCurrentColumn = ActiveDocument.Selection.CurrentColumn

ExecuteCommand("Edit.GotoBrace")

If ActiveDocument.Selection.CurrentLine = nCurrentLine Then
ActiveDocument.Selection.MoveTo( _
nCurrentLine, nCurrentColumn + 1)
strRight = "" ' we found it so move on to the next match
Else
ActiveDocument.Selection.MoveTo( _
nCurrentLine, nCurrentColumn)
ActiveDocument.Selection.FindText( _
str & ".+$", vsFindOptions.vsFindOptionsRegularExpression + vsFindOptions.vsFindOptionsMatchCase)
End If
End If
End If

If (strRight <> "") And _
(Left(strRight, 1) <> "/") And _
(strRight <> ",") And _
(strRight <> ";") And _
(strRight <> "\") Then
ActiveDocument.Selection.CharLeft()
MoveByColumns(nLen)
ActiveDocument.Selection.NewLine()
End If

End If
End While

End Sub

' Place the given strOperator (of nLen REAL characters)
' between spaces (if needed)
Sub FixOperator(ByVal strOperator, ByVal nLen)
Dim strLastPos, strFind

strLastPos = InitializePosition()

' Add one space between the operator
While ActiveDocument.Selection.FindText("[A-Z,a-z,0-9,\),_,\]]" & _
strOperator & "[A-Z,a-z,0-9,\(,_,\*,"",',&]", vsFindOptions.vsFindOptionsRegularExpression) And _
GetCurrentPosition(strLastPos)

' Check if we're inside a comment or between quotes
ActiveDocument.Selection.CharLeft()
If Not ShouldIgnore() Then

' Repeat the search since ShouldIgnore puts the cursor at the
' beginning of the line
ActiveDocument.Selection.FindText("[A-Z,a-z,0-9,\),_,\]]" & _
strOperator & "[A-Z,a-z,0-9,\(,_,\*,"",',&]", vsFindOptions.vsFindOptionsRegularExpression)

ActiveDocument.Selection.CharLeft()
ActiveDocument.Selection.CharRight()
ActiveDocument.Selection.Text = " "
MoveByColumns(nLen)
ActiveDocument.Selection.Text = " "

End If
End While

strLastPos = InitializePosition()

' Fix any C++ "operator" member functions which were broken above
While ActiveDocument.Selection.FindText("operator " & strOperator & " ", _
vsFindOptions.vsFindOptionsRegularExpression) And GetCurrentPosition(strLastPos)

' Check if we're inside a comment or between quotes
If Not ShouldIgnore() Then

' Repeat the search since ShouldIgnore puts the cursor at the
' beginning of the line
ActiveDocument.Selection.FindText("operator " & strOperator & " ", _
vsFindOptions.vsFindOptionsRegularExpression)
ActiveDocument.Selection.CharRight()
ActiveDocument.Selection.Backspace()
MoveByColumns(-nLen)
ActiveDocument.Selection.Backspace()

End If
End While

End Sub

' Fix "<" and ">" operators found inside for, while, and if statements.
Function FixLessThanAndGreaterThanOperators()

' Fix "for" loops
FixLessThanOrGreaterThanOperator("^:b*for \(.*;.*[A-Z,a-z,0-9,), ]+\<[^<=]")
FixLessThanOrGreaterThanOperator("^:b*for \(.*;.*[A-Z,a-z,0-9,), ]+\>[^>=]")

' Fix "while" loops
FixLessThanOrGreaterThanOperator("^:b*while \(.*[A-Z,a-z,0-9,), ]+\<[^<=]")
FixLessThanOrGreaterThanOperator("^:b*while \(.*[A-Z,a-z,0-9,), ]+\>[^>=]")

' Fix "if" statements
FixLessThanOrGreaterThanOperator("^:b*if \(.*[A-Z,a-z,0-9,), ]+\<[^<=]")
FixLessThanOrGreaterThanOperator("^:b*if \(.*[A-Z,a-z,0-9,), ]+\>[^>=]")

End Function

' Fix "<" or ">" operator based on the given search condition.
' It assumes the condition will yield one or more characters before
' the operator and only one character after it.
Function FixLessThanOrGreaterThanOperator(ByVal strSearch)
Dim strLastPos, strFound

strLastPos = InitializePosition()

While ActiveDocument.Selection.FindText(strSearch, vsFindOptions.vsFindOptionsRegularExpression + _
vsFindOptions.vsFindOptionsMatchCase) And GetCurrentPosition(strLastPos)

ActiveDocument.Selection.CharRight()
ActiveDocument.Selection.CharLeft()

' Check if we're inside a comment or between quotes
If Not ShouldIgnore() Then

' Repeat the search since ShouldIgnore puts the cursor at the
' beginning of the line
ActiveDocument.Selection.FindText(strSearch, vsFindOptions.vsFindOptionsRegularExpression + _
vsFindOptions.vsFindOptionsMatchCase)

strFound = ActiveDocument.Selection.Text

' Add a space before it (if needed)
If Mid(strFound, Len(strFound) - 2, 1) <> " " Then
strFound = Left(strFound, Len(strFound) - 2) & " " & _
Right(strFound, 2)
End If

' Add a space after it (if needed)
If Right(strFound, 1) <> " " Then
strFound = Left(strFound, Len(strFound) - 1) & " " & _
Right(strFound, 1)
End If

ActiveDocument.Selection.Text = strFound

End If
End While

End Function

' Append a space after the given strOperator (if it needs it)
Sub AppendSpace(ByVal strOperator)
Dim strLastPos, nCurrentLine, nCurrentColumn

strLastPos = InitializePosition()

While ActiveDocument.Selection.FindText(strOperator & _
"[A-Z,a-z,0-9,\(,\-,_,\*,"",',&]", vsFindOptions.vsFindOptionsRegularExpression) And _
GetCurrentPosition(strLastPos)

ActiveDocument.Selection.CharLeft()
nCurrentLine = ActiveDocument.Selection.CurrentLine
nCurrentColumn = ActiveDocument.Selection.CurrentColumn

' Check if we're inside a comment or between quotes
If Not ShouldIgnore() Then

ActiveDocument.Selection.MoveTo(nCurrentLine, nCurrentColumn)
ActiveDocument.Selection.FindText(strOperator & _
"[A-Z,a-z,0-9,\(,\-,_,\*,"",',&]", vsFindOptions.vsFindOptionsRegularExpression)

ActiveDocument.Selection.CharLeft()
MoveByColumns(Len(strOperator))
ActiveDocument.Selection.Text = " "

End If
End While

End Sub

' Run the SmartIndent command (Alt+F8) on function blocks only.
Function SmartIndentFunctionBlocks()
Dim strLastPos, nColumn, cBeforeBrace

strLastPos = InitializePosition()

While ActiveDocument.Selection.FindText("{") And _
GetCurrentPosition(strLastPos)

' Check if we're inside a comment or between quotes
If Not ShouldIgnore() Then

' Repeat the action since ShouldIgnore puts the cursor at the
' beginning of the line
ActiveDocument.Selection.FindText("{")

' Go to matching brace and reformat tabs
DTE.ExecuteCommand("Edit.GotoBraceExtend")
nColumn = ActiveDocument.Selection.CurrentColumn

ActiveDocument.Selection.SmartFormat()
ActiveDocument.Selection.CharRight()
If ActiveDocument.Selection.CurrentColumn > nColumn Then

' If SmartFormat indents the block (by mistake), unindent it
ActiveDocument.Selection.CharLeft()
DTE.ExecuteCommand("Edit.GotoBraceExtend")
ActiveDocument.Selection.Unindent()

End If
End If
End While

End Function

' Since Microsoft's "SmartFormat" is not smart enough to indent case
' statements inside the switch body, we'll do it here.
' (Thanks to Jim Cooper)
Function IndentSwitchBody()
Dim nSwitchLine, nFirstLine, nLastLine, strLastPos, iLine

strLastPos = InitializePosition()

While ActiveDocument.Selection.FindText("switch", _
vsFindOptions.vsFindOptionsMatchWholeWord + vsFindOptions.vsFindOptionsMatchCase) And GetCurrentPosition(strLastPos)

' Check if we're inside a comment or between quotes
If Not ShouldIgnore() Then

nSwitchLine = ActiveDocument.Selection.CurrentLine

' Now find the opening brace and make sure it's on the next line
If ActiveDocument.Selection.FindText("{") And _
Not ShouldIgnore() And _
(ActiveDocument.Selection.CurrentLine = nSwitchLine + 1) Then

' Repeat the action since ShouldIgnore puts the cursor at the
' beginning of the line
ActiveDocument.Selection.FindText("{")

' Find next line in file, since earlier code put '{' on
' a line by itself
nFirstLine = ActiveDocument.Selection.CurrentLine + 1

' Go to matching brace and reformat tabs
ExecuteCommand("Edit.GotoBrace")

' Find previous line in file, since earlier code put '}' on
' line by itself
nLastLine = ActiveDocument.Selection.CurrentLine

' Move to the line after the opening brace
ActiveDocument.Selection.GoToLine(nFirstLine, 1)

' Select everything between the braces and indent it
For iLine = nFirstLine To nLastLine - 1
ActiveDocument.Selection.LineDown(True)
Next
ActiveDocument.Selection.Indent()
End If
End If
End While
End Function

' Remove any lines that are considered extraneous (usually blank ones).
Function RemoveExtraneousLines()
Dim strLastPos, nCurrentLine, nCurrentColumn, nLastLine, bContinue

strLastPos = InitializePosition()
RemoveExtraneousLines = True

' Remove any blank lines that fall below any open braces ("{")
While ActiveDocument.Selection.FindText("{") And _
GetCurrentPosition(strLastPos)

' Check if we're inside a comment or between quotes
If Not ShouldIgnore() Then
' Repeat the action since ShouldIgnore puts the cursor at the
' beginning of the line
ActiveDocument.Selection.FindText("{")

nCurrentLine = ActiveDocument.Selection.CurrentLine
nCurrentColumn = ActiveDocument.Selection.CurrentColumn

' Go to matching brace
ExecuteCommand("Edit.GotoBrace")

' Check if it's on the same line
bContinue = (nCurrentLine <> ActiveDocument.Selection.CurrentLine)
ExecuteCommand("Edit.GotoBrace")

' If the matching brace is on the same line, proceed to the next brace
If bContinue Then

ActiveDocument.Selection.LineDown()

' Cut any blank lines below the {
Do While True
ActiveDocument.Selection.StartOfLine()
ActiveDocument.Selection.EndOfLine(True)

If LTrimTabs(ActiveDocument.Selection.Text) <> "" Then
Exit Do
End If

' Get the last line of the document
ActiveDocument.Selection.EndOfDocument()
nLastLine = ActiveDocument.Selection.CurrentLine

' Remove the extra line
ActiveDocument.Selection.MoveTo(nCurrentLine + 1, 1)
ExecuteCommand("LineCut")

' Make sure we haven't hit the bottom of the file and that LineCut worked
ActiveDocument.Selection.EndOfDocument()
If ActiveDocument.Selection.CurrentLine = nLastLine Then
Exit Do
End If
ActiveDocument.Selection.MoveTo(nCurrentLine + 1, 1)
Loop

End If

ActiveDocument.Selection.MoveTo(nCurrentLine, nCurrentColumn)
End If
End While

strLastPos = InitializePosition()

' Remove any blank lines right above any closing braces ("}")
While ActiveDocument.Selection.FindText("}") And _
GetCurrentPosition(strLastPos)

' Check if we're inside a comment or between quotes
If Not ShouldIgnore() Then
' Repeat the action since ShouldIgnore puts the cursor at the
' beginning of the line
ActiveDocument.Selection.FindText("}")
ActiveDocument.Selection.CharLeft()

' Cut blank lines above the }
Do While True
ActiveDocument.Selection.LineUp()
ActiveDocument.Selection.StartOfLine()
ActiveDocument.Selection.EndOfLine(True)

nCurrentLine = ActiveDocument.Selection.CurrentLine

If LTrimTabs(ActiveDocument.Selection.Text) <> "" Then

If ActiveDocument.Selection.CurrentLine > 1 Then
ActiveDocument.Selection.LineDown()
End If

ActiveDocument.Selection.StartOfLine()
ActiveDocument.Selection.FindText("}")
strLastPos = ActiveDocument.Selection.CurrentLine & "," & _
ActiveDocument.Selection.CurrentColumn

ActiveDocument.Selection.LineDown()
ActiveDocument.Selection.StartOfLine()
Exit Do
End If

' Get the last line of the document
ActiveDocument.Selection.EndOfDocument()
nLastLine = ActiveDocument.Selection.CurrentLine

' Remove the extra line
ActiveDocument.Selection.MoveTo(nCurrentLine, 1)
ExecuteCommand("LineCut")

' Make sure LineCut worked -- if not something's really messed up
ActiveDocument.Selection.EndOfDocument()
If ActiveDocument.Selection.CurrentLine = nLastLine Then
ShowError("OutOfMemory")
RemoveExtraneousLines = False
Exit Function
End If

ActiveDocument.Selection.MoveTo(nCurrentLine, 1)
Loop
End If
End While

End Function

' Remove all spaces and tabs found in the current selection
Function RemoveSpacesInSelection()
Dim iPos, ch, strNoSpaces

For iPos = 1 To Len(ActiveDocument.Selection.Text)
ch = Mid(ActiveDocument.Selection.Text, iPos, 1)
If ch <> " " And ch <> vbTab Then
strNoSpaces = strNoSpaces + ch
End If
Next

ActiveDocument.Selection.Text = strNoSpaces
End Function

' Fix any code with exponential notation (ie. 3.4e-2) which was
' broken when the + and - operators were fixed above (by FixOperator).
Function FixExponents()

While ActiveDocument.Selection.FindText("[0-9,\.]e [\+\!\-] [0-9]", _
vsFindOptions.vsFindOptionsRegularExpression)
RemoveSpacesInSelection()
End While

End Function

' Break any lines containing multiple statements (separated by semicolons)
Function BreakLinesWithMultipleStatements()
Dim strLastPos, nCurrentLine, nCurrentColumn

strLastPos = InitializePosition()

' Search for multiple semicolons on the same line
Do While ActiveDocument.Selection.FindText(";.+;", vsFindOptions.vsFindOptionsRegularExpression)

ActiveDocument.Selection.CharLeft()
If Not GetCurrentPosition(strLastPos) Then
Exit Do
End If

nCurrentLine = ActiveDocument.Selection.CurrentLine
nCurrentColumn = ActiveDocument.Selection.CurrentColumn

' Check if we're inside a comment or between quotes
If Not ShouldIgnore() Then
' Repeat the action since ShouldIgnore puts the cursor at the
' beginning of the line

ActiveDocument.Selection.MoveTo(nCurrentLine, nCurrentColumn)
ActiveDocument.Selection.FindText(";.+;", vsFindOptions.vsFindOptionsRegularExpression)

nCurrentLine = ActiveDocument.Selection.CurrentLine
ActiveDocument.Selection.CharLeft()
ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstText, True)

' If found, check that the semicolons don't belong to a for loop
If (InStr(1, ActiveDocument.Selection.Text, "for (", _
vbTextCompare) > 0) Then
ActiveDocument.Selection.MoveTo(nCurrentLine + 1, 1)
Else
If ActiveDocument.Selection.Text <> "" Then
ActiveDocument.Selection.CharRight()
End If
ActiveDocument.Selection.CharRight()
ActiveDocument.Selection.NewLine()
End If
End If

Loop

End Function

' Make sure the first C++ comment of every line has a space after it.
Function InsertSpaceAfterFirstInLineComment()
Dim strLastPos, nCurrentLine

strLastPos = InitializePosition()

While ActiveDocument.Selection.FindText("//[A-Z,a-z,0-9]", vsFindOptions.vsFindOptionsRegularExpression) _
And GetCurrentPosition(strLastPos)

ActiveDocument.Selection.CharLeft()

' Check if we're inside a comment or between quotes
If Not ShouldIgnore() Then
' Repeat the action since ShouldIgnore puts the cursor at the
' beginning of the line
ActiveDocument.Selection.FindText("//[A-Z,a-z,0-9]", vsFindOptions.vsFindOptionsRegularExpression)

ActiveDocument.Selection.CharRight()
ActiveDocument.Selection.CharLeft()
ActiveDocument.Selection.Text = " "
End If
End While

End Function

' Display a message box if bDebug is true
Sub DebugBox(ByVal strMessage)
Dim bDebug
bDebug = False

If bDebug Then
MsgBox(strMessage)
End If

End Sub
End Module
GeneralRe: Ported to VS.NET 7.1 (long post) Pin
Joe Woodbury22-Aug-03 8:43
professionalJoe Woodbury22-Aug-03 8:43 
GeneralRe: Ported to VS.NET 7.1 (long post) Pin
Anonymous17-Mar-04 3:08
Anonymous17-Mar-04 3:08 
GeneralRe: Ported to VS.NET 7.1 (long post) Pin
Yuh-Rong Leu16-Mar-05 4:36
sussYuh-Rong Leu16-Mar-05 4:36 
GeneralOdd behavior Pin
dparker29-Jun-03 11:41
dparker29-Jun-03 11:41 
Generalproblem with stl-functions like find_if Pin
tux*elan2-Apr-03 22:55
tux*elan2-Apr-03 22:55 
GeneralMakecodenicer Pin
Harm Salomons20-Mar-03 21:36
Harm Salomons20-Mar-03 21:36 
QuestionSpaces to tabs? Pin
slegon25-Feb-03 22:06
slegon25-Feb-03 22:06 
GeneralVisual Studio .NET Pin
Anonymous10-Sep-02 21:07
Anonymous10-Sep-02 21:07 
GeneralRe: Visual Studio .NET Pin
Alvaro Mendez11-Sep-02 14:44
Alvaro Mendez11-Sep-02 14:44 
GeneralRe: Visual Studio .NET Pin
CoreyCooper26-Feb-04 9:30
CoreyCooper26-Feb-04 9:30 
GeneralMoving && and || to start a new line Pin
Ron Sanderson30-Jul-02 5:38
Ron Sanderson30-Jul-02 5:38 
GeneralI love your macro, but... Pin
NGS 5496729-Jul-02 8:25
NGS 5496729-Jul-02 8:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.