Reformat C/C++ code

So you finally got your hands on this awesome piece of C/C++ code that promises to solve your burning problem! You download it and open it inside DevStudio to see how it works and guess what? It looks like crap! I mean, the person who wrote the code used the Martian convention to write it. Granted, it may work, but the way it looks makes it hard to see how.

So you press a button on your toolbar which runs a macro and presto! Everything becomes instantly formatted just the way you like it!

That's what my MakeCodeNicer macro does (below). I got tired of grunting and fixing other people's code with the Find/Replace box so I wrote a nifty little macro that rearranges things such as parenthesis, braces, and brackets to make everything look more civilized.

My coding style happens to look a lot like Microsoft's. However, I'm aware that not everyone likes it (as much as I wish they did) so please feel free to change things to suit your own tastes. Each Find/Change iteration has a comment preceding it which explains what it does.

Enjoy!

Option Explicit

'----------------------------------------------------------
'FILE DESCRIPTION: Routines to reformat C/C++ code.
'----------------------------------------------------------

Sub MakeCodeNicer()
'DESCRIPTION: Reformats the source code to look nicer, the 
' way I like it. 
'Written by Alvaro Mendez on 06/10/1999
' Taken from 
' http://codeguru.developer.com/devstudio_macros/MakeCodeNicer.shtml

 ' Check that the current document can be changed
 if ActiveDocument.ReadOnly then

  ' If we're connected to SourceSafe, let it prompt for 
    ' check out
  ActiveDocument.Selection = "a"
  ActiveDocument.Undo

  if ActiveDocument.ReadOnly then  ' check again
   MsgBox "This macro cannot be executed on a read-only file",_
    vbExclamation
   exit sub
  end if
 end if

 ' Save current line so we can return to it at the end
 dim nLine
 nLine = ActiveDocument.Selection.CurrentLine

 ' Add spaces in a few places and get rid of it in others
 Replace "\:b+;", ";"
 Replace "\:b+::\:b+", "::"
 Replace "\:b+(", "("
 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+\[", "["

 ' 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 case statements appearing on single lines
 IsolateOnRight "case .+: "
 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
 FixLessThanOperator
 FixExponents

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

 ' Make sure C++ comments (followed by words) have a space 
  ' after them
 while ActiveDocument.Selection.FindText("//[A-Z,a-z,0-9]", _
   dsMatchRegExp)

  ActiveDocument.Selection.CharRight
  ActiveDocument.Selection.CharLeft
  ActiveDocument.Selection = " "
 wend

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

 ' Fix tabs within code surrounded by braces
 TabifyMatchingBraces

 ' Remove any lines that are considered extraneous (ie. 
  ' extra blank lines)
 RemoveExtraneousLines

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

 ' Go back to where we were at the beginning
 ActiveDocument.Selection.GoToLine nLine

End Sub

' 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 dsFirstText, dsExtend

 nCount = 0
 iPos = 0
 strBuffer = ActiveDocument.Selection

 ' 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 it's the first character, then it's valid
  if iPos = 1 then 
   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 dsExtend

  ' If we find a quoted character left of us, check 
    ' for one on the right
  if ActiveDocument.Selection = "'" then
   ActiveDocument.Selection.CharRight
   ActiveDocument.Selection.CharRight dsExtend
   if ActiveDocument.Selection = "\" then
    ActiveDocument.Selection.CharRight
    ActiveDocument.Selection.CharRight dsExtend
   end if
   ActiveDocument.Selection.CharRight
   ActiveDocument.Selection.CharRight dsExtend

   if ActiveDocument.Selection = "'" 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 dsFirstText, dsExtend

 IsWithinComment = false
 if (InStr(1, ActiveDocument.Selection, "//", 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

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

 ShouldIgnore = false

 if IsWithinQuotes then
  ShouldIgnore = true
  exit function
 end if

 if IsWithinComment 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(strPos)
 dim nLastLine, nLastColumn, nCurrentLine
  dim 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 = Int(Mid(strPos, 1, iPos))
   nLastColumn = Int(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(nBy)
 ActiveDocument.Selection.MoveTo _ 
   ActiveDocument.Selection.CurrentLine, _
   ActiveDocument.Selection.CurrentColumn + nBy
end sub

' Replace the given strFrom with strTo case sensitively
sub Replace(strFrom, strTo)
 dim strLastPos, bContinue

 strLastPos = InitializePosition
 do while ActiveDocument.Selection.FindText(strFrom, _
  dsMatchCase + dsMatchRegExp)

  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, _
    dsMatchCase + dsMatchRegExp
   ActiveDocument.Selection = 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(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, dsMatchCase) _
 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, dsMatchCase

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

   ' 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 = "{" + 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 = strFound
     ActiveDocument.Selection.MoveTo _
      ActiveDocument.Selection.CurrentLine, nCol
     ActiveDocument.Selection.NewLine
    end if
   end if
  end if
 wend

end sub

' Trim blanks AND tabs from the left side
function LTrimTabs(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(str)
 dim strLastPos, nLen, bContinue, nCurrentLine, nCurrentColumn

 strLastPos = InitializePosition

 while ActiveDocument.Selection.FindText("^.*" & str, dsMatchRegExp) _
 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, dsMatchRegExp

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

   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, dsMatchRegExp

    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 "GoToMatchBrace"
     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, _
         dsMatchRegExp
     end if
    end if


    if LTrimTabs(ActiveDocument.Selection) <> str and _
     not bContinue then
     ActiveDocument.Selection.CharRight
     MoveByColumns -nLen
     ActiveDocument.Selection.NewLine
     MoveByColumns nLen
    end if

    GetCurrentPosition strLastPos
   end if
  end if

 wend

end sub

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

 strLastPos = InitializePosition

 while ActiveDocument.Selection.FindText(str & ".+$", dsMatchRegExp) 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 & ".+$", dsMatchRegExp

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

   ' Now that we have the length, we need to redo the 
      ' search and go on
   ActiveDocument.Selection.CharLeft
   ActiveDocument.Selection.FindText str & ".+$", dsMatchRegExp

   strRight = LTrimTabs(Mid(ActiveDocument.Selection, 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 dsExtend
     if (InStr(1, ActiveDocument.Selection, ";", vbTextCompare) _
      > 0) then
      strRight = "" ' we found it so move on to the next match
     end if
     ActiveDocument.Selection.CharLeft
    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 "GoToMatchBrace"
     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 & ".+$", dsMatchRegExp
     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
 wend

end sub

' Place the given strOperator (of nLen REAL characters)
' between spaces (if needed)
sub FixOperator(strOperator, 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,\(,_,\*,"",',&]", dsMatchRegExp) 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,\(,_,\*,"",',&]", dsMatchRegExp

   ActiveDocument.Selection.CharLeft
   ActiveDocument.Selection.CharRight
   ActiveDocument.Selection = " "
   MoveByColumns nLen
   ActiveDocument.Selection = " "

  end if
 wend

 strLastPos = InitializePosition

 ' Fix any C++ "operator" member functions which were broken above
 while ActiveDocument.Selection.FindText("operator " & strOperator & " ", _
  dsMatchRegExp) 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 & " ", _
            dsMatchRegExp
   ActiveDocument.Selection.CharRight
   ActiveDocument.Selection.Backspace
   MoveByColumns -nLen
   ActiveDocument.Selection.Backspace

  end if
 wend

end sub

' Fix > operator without altering template<T> code and operator <<
function FixLessThanOperator()
 dim strLastPos, strFound, strTemplate

 strLastPos = InitializePosition

 while ActiveDocument.Selection.FindText("^.*[^ <]<.", dsMatchRegExp) 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 "^.*[^ <]<.", dsMatchRegExp

   strFound = ActiveDocument.Selection

   ' Fix the left side
   strFound = Left(strFound, Len(strFound) - 2) & " " & _
      Right(strFound, 2)
   ActiveDocument.Selection = strFound

   ' Fix the right side
   strTemplate = Right(strFound, 11)
   if  (Left(strTemplate, 8) <> "template") and _
    (Right(strFound, 1) <> " ") and _
    (Right(strFound, 1) <> "=") and _
    (Right(strFound, 1) <> "<") and _
    (Right(strFound, 1) <> ">")then
    ActiveDocument.Selection.CharLeft
    ActiveDocument.Selection = " "
   end if

  end if
 wend

end function

' Append a space after the given strOperator (if it needs it)
sub AppendSpace(strOperator)
 dim strLastPos

 strLastPos = InitializePosition

 while ActiveDocument.Selection.FindText(strOperator & _
  "[A-Z,a-z,0-9,\(,\-,_,\*,"",',&]", dsMatchRegExp) and _
  GetCurrentPosition(strLastPos)

  ' Check if we're inside a comment or between quotes
  ActiveDocument.Selection.CharLeft
  if not ShouldIgnore then

   ActiveDocument.Selection.FindText strOperator & _
     "[A-Z,a-z,0-9,\(,\-,_,\*,"",',&]", dsMatchRegExp

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

  end if
 wend

end sub

' Fix tabbing within function blocks (surrounded by braces)
function TabifyMatchingBraces()
 dim strLastPos, 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
   ExecuteCommand "GoToMatchBraceExtend"
   ActiveDocument.Selection.SmartFormat

   cBeforeBrace = Mid(ActiveDocument.Selection, _
    Len(ActiveDocument.Selection) - 1, 1)

   ' If SmartFormat indents the block (by mistake), unindent it
   if (cBeforeBrace = vbTab or cBeforeBrace = " ") then
    ActiveDocument.Selection.Unindent
   end if
  end if
 wend

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", _
  dsMatchWord + dsMatchCase) 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 "GoToMatchBrace"

    ' 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 dsExtend
    next
    ActiveDocument.Selection.Indent
   end if
  end if
 wend
end function

' Remove any lines that are considered extraneous (usually blank ones).
function RemoveExtraneousLines()
 dim strLastPos, nCurrentLine, nCurrentColumn

 strLastPos = InitializePosition

 ' 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

   ActiveDocument.Selection.LineDown

   ' Cut any blank lines below the {
   do while true
    ActiveDocument.Selection.StartOfLine
    ActiveDocument.Selection.EndOfLine dsExtend

    if LTrimTabs(ActiveDocument.Selection) <> "" then
     exit do
    end if
    ExecuteCommand "LineCut"

    ' Make sure we haven't hit the bottom of the file
    ActiveDocument.Selection.EndOfDocument
    if ActiveDocument.Selection.CurrentLine = nCurrentLine + 1 then
        exit do
    end if
    ActiveDocument.Selection.MoveTo nCurrentLine + 1, 1
   loop

   ActiveDocument.Selection.MoveTo nCurrentLine, nCurrentColumn
  end if
 wend

 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 dsExtend

    if LTrimTabs(ActiveDocument.Selection) <> "" 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
    ExecuteCommand "LineCut"
   loop
  end if
 wend

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)
  ch = Mid(ActiveDocument.Selection, iPos, 1)
  if ch <> " " and ch <> vbTab then
   strNoSpaces = strNoSpaces + ch
  end if
 next

 ActiveDocument.Selection = 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]", _
   dsMatchRegExp)
  RemoveSpacesInSelection
 wend

end function

' Break any lines containing multiple statements (separated by semicolons)
function BreakLinesWithMultipleStatements()
 dim strLastPos, nCurrentLine

 strLastPos = InitializePosition

 ' Search for multiple semicolons on the same line
 while ActiveDocument.Selection.FindText(";.+;", dsMatchRegExp) 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 ";.+;", dsMatchRegExp

   nCurrentLine = ActiveDocument.Selection.CurrentLine
   ActiveDocument.Selection.CharLeft
   ActiveDocument.Selection.StartOfLine dsFirstText, dsExtend

   ' If found, check that the semicolons don't belong to a for loop
   if (InStr(1, ActiveDocument.Selection, "for (", _
    vbTextCompare) > 0) then
    ActiveDocument.Selection.MoveTo nCurrentLine + 1, 1
   else
    ActiveDocument.Selection.CharRight
    ActiveDocument.Selection.CharRight
    ActiveDocument.Selection.NewLine
   end if
  end if
 wend

end function



Sub MakeSelectedCodeNicer()
'DESCRIPTION: Reformats the currently selected source code to look nicer.
' Written by Alvaro Mendez on 07/15/1999

 ' Check if there's a valid selection
 if ActiveDocument.Selection = "" then
  exit sub
 end if

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

 ' Open a new document and changed its language to C/C++
 ' This is required for SmartIndent to work.
 Documents.Add "Text"
 ExecuteCommand "Properties"
 ActiveDocument.Language = "C/C++"

 ' Paste the selection into the document and run the macro
 ActiveDocument.Selection.Paste
 ExecuteCommand "MakeCodeNicer"

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

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

End Sub

Download

Download source - 6 Kb


Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Learn How A Global Entertainment Company Saw a 448% ROI Every business today uses software to manage systems, deliver products, and empower employees to do their jobs. But software inevitably breaks, and when it does, businesses lose money -- in the form of dissatisfied customers, missed SLAs or lost productivity. PagerDuty, an operations performance platform, solves this problem by helping operations engineers and developers more effectively manage and resolve incidents across a company's global operations. …

  • Live Event Date: December 18, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this upcoming webcast …

Most Popular Programming Stories

More for Developers

RSS Feeds