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:

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.

In the Macro Explorer, expand the ReformatCode module and then the MakeCodeNicer macro.

You'll see the SelectionOnly and WholeFile methods.

Open a C, C++, C#, Java, or JavaScript source file to be reformatted. Make sure it compiles successfully!

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.

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:

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.)

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.

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.

Share

About the Author

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#.

The tool is very nice and usefull , especially when different users with different styles are dealing with the same code.
I tried the tool, and therefore I would like to add a comment.
Some people are writing code by using " { " on a different line , and others put " { " on the same line: Sample :
for
{
}

or

for {
}

Me am intereseted in having on the same line , and here rise my question: It is possible to make it configurable to support both variants ?

That happens as a result of the call to "SmartIndent" on line 231. Comment that out and the problem won't occur.

Regards,
Alvaro

I cannot take anything the Bush administration does seriously. The corruption, the cynical disregard for humanity, the cronyism and incompetence, all wrapped in a slimey flag of ultra-marketed nationalism repulses me. -- consdubya from fark.com.

http://sourceforge.net/projects/astyle/[^]
I have used it for a very long time (C++)and works perfectly.If you need a shortcut, you can declare it as a tool in MSVC IDE with proper parameters, and create a macro to activate it.
Sorry if I offended anyone.

I cannot take anything the Bush administration does seriously. The corruption, the cynical disregard for humanity, the cronyism and incompetence, all wrapped in a slimey flag of ultra-marketed nationalism repulses me. -- consdubya from fark.com.

Thank you very much for this nice set of macros. I always wonder how ugly my code may look to someone else. When I ran the macros, it pretty much left things intact. I do like the extra spaces after the 'if' statements, but I find it so much easier to but the parenthesis up right next to it while working. That was the only difference I noticed in my code. Thanks again!

That's strange. Your current project does not allow creating Text files? Have you tried creating a Text file from the "File/New/File..." menu?

Regards,
Alvaro

I cannot take anything the Bush administration does seriously. The corruption, the cynical disregard for humanity, the cronyism and incompetence, all wrapped in a slimey flag of ultra-marketed nationalism repulses me. -- consdubya from fark.com.

Studio auto formatting code has a bad habit of causing blank lines to be filled
with spaces (or tabs) up to the indent level. I created a macro a long time ago
which removes this problem. It may not be the best way to do this (I don't know
the language well, but can create a quick macro and make it work). I have added
a call to this function right after the call to IndentSwitchBody, and now the
MakeCodeNicer will truely blank out the blank lines:

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

' 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

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

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

' 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

' 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

' 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

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

' 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

' 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

' 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)

' 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

' 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)

' 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 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("{")

' 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()

' 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

' 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

Now that I upgraded to .net I think its wonderful - but... this is one macro that I really miss - I have to load up a VC6 just to run the macro. Anybody have a port or a converter to convert vc6 macros to vc.net? big booboo on m$ part.