Introduction
This macro holds helpful methods for use in Visual Studio .NET. For those of you not familiar with macro writing - if you don't know how to do something, just try recording a macro and you can usually view the auto-generated code and get some idea of where to start with your own code. The methods available in this macro are:
CommentAllRoutines
Inserts comments for all the routines in the current document. Gives creation date, author, parameters, and method names.
CommentSelectedRoutines
As above, but only comments the routines you have selected.
IncrementTabIndexes
I came up with this method after getting annoyed of having to manually reset the tab indexes of ASP web controls. Select your HTML, and the first tab index will set the counter, then subsequent tab indexes will be incremented by 1.
IncrementUserText
As above, but you get an input box so that you can specify your own field to search and increment.
InsertModuleHeader
Inserts the creation date, author, and copyright info at the top of your module.
InsertProperties
Select your class level variables (must be prefixed with �_�), and this method will automatically take care of the tedious stuff of writing properties for you.
RegionToggle
Select the top or bottom of a region, and this method will take you to the other end of the region.
CloseAllOtherWindows
This macro will close all the open windows except the one you're working on.
As far as I can tell, you can only write macros in VB.NET, and you can't debug them when you're working on a C# project.
Install instructions
Save the CustomMacros.vb file to somewhere, then open the macros explorer by opening Visual Studio and pressing Alt+F11, and choosing File-->Add Existing, and choosing the saved macro.
It's all pretty self explanatory. This code comes without any guarantee but if you use it, please reference that you got it off me. Any comments appreciated. I've created these macros through a combination of editing pre-existing stuff, using the sample macros, recording macros, and plain old trial and error. I apologize if I've accidentally ripped off someone's work without acknowledging them - if I have, it certainly wasn't intentional.
Imports EnvDTE
Imports System.Diagnostics
Imports System.Text
Imports System.Environment
Imports System.Windows.Forms
Public Module CustomMacros
#Region "Class level variables"
Private Const cCommentConst As String = "//"
Private Const vbCommentConst As String = "'"
Private Const ownerLineConst As String = _
"This program and code is property" & _
" of [enter your name here]"
Private Const copyRightConst As String = _
"� 1988 to 2006. All Rights Reserved"
Private Const commentBorderConst As String = _
" --------------------------------" & _
"---------------------------------------------"
Private counter As Integer
#End Region
#Region "Insert Module Header"
Sub InsertModuleHeader()
Dim projitem As ProjectItem = DTE.ActiveDocument.ProjectItem
Dim filecm As FileCodeModel = projitem.FileCodeModel
Dim element As CodeElement = filecm.CodeElements.Item(1)
Dim celttype As CodeType
If (TypeOf element Is CodeNamespace) Then
element = element.members.item(1)
End If
If (TypeOf element Is CodeType) Then
celttype = CType(element, CodeType)
Else
Throw New System.Exception("Didn't find a type" & _
" definition as first thing in file or find" & _
" a namespace as the first thing with" & _
" a type inside the namespace.")
End If
Dim edPoint As EditPoint = _
element.GetStartPoint(vsCMPart.vsCMPartHeader).CreateEditPoint()
Dim commentStart As String = LineOrientedCommentStart()
If (commentStart.Length = 2) Then
commentStart = commentStart & commentStart.Chars(1) & " "
ElseIf (commentStart.Length = 1) Then
commentStart = commentStart & commentStart.Chars(0) & _
commentStart.Chars(0) & " "
End If
Try
DTE.UndoContext.Open("Insert Module Header")
If (element.Kind = vsCMElement.vsCMElementClass) Then
edPoint.StartOfDocument()
edPoint.StartOfLine()
edPoint.Insert(NewLine & NewLine)
edPoint.LineUp()
edPoint.Insert(commentStart & commentBorderConst & NewLine)
edPoint.Insert(commentStart & element.Name & NewLine)
edPoint.Insert(commentStart & NewLine)
edPoint.Insert(commentStart & ownerLineConst & NewLine)
edPoint.Insert(commentStart & copyRightConst & NewLine)
edPoint.Insert(commentStart & NewLine)
edPoint.Insert(commentStart & "Author:" & _
ControlChars.Tab & UserName & NewLine)
edPoint.Insert(commentStart & "Date:" & _
ControlChars.Tab & _
System.DateTime.Now.ToLongDateString() & _
NewLine)
edPoint.Insert(commentStart & commentBorderConst)
End If
Finally
DTE.UndoContext.Close()
End Try
End Sub
#End Region
#Region "CommentRoutines"
Public Sub CommentSelectedRoutines()
CommentAllRoutines(False)
End Sub
Public Sub CommentAllRoutines(Optional ByVal _
commentAll As Boolean = True)
Dim projItem As ProjectItem = DTE.ActiveDocument.ProjectItem
Dim filecm As FileCodeModel = projItem.FileCodeModel
Dim element As CodeElement = filecm.CodeElements.Item(1)
Dim elementtype As CodeType
Dim ts As TextSelection = ActiveDocument.Selection
If (TypeOf element Is CodeNamespace) Then
element = element.members.item(1)
End If
If (TypeOf element Is CodeType) Then
elementtype = CType(element, CodeType)
Else
Throw New System.Exception("Type definition" & _
" or namespace definition" & _
" not found as first thing in file.")
End If
Dim edPoint As EditPoint = _
elementtype.GetStartPoint(vsCMPart.vsCMPartHeader).CreateEditPoint()
Dim commentStart As String = LineOrientedCommentStart()
If (commentStart.Length = 2) Then
commentStart = commentStart & commentStart.Chars(1) & " "
ElseIf (commentStart.Length = 1) Then
commentStart = commentStart & _
commentStart.Chars(0) & _
commentStart.Chars(0) & " "
End If
Try
If commentAll Then
DTE.UndoContext.Open("Comment All Routines")
Else
DTE.UndoContext.Open("Comment Selected Routines")
End If
For Each element In elementtype.Members
If (element.Kind = vsCMElement.vsCMElementFunction) Then
Dim codefun As CodeFunction = element
edPoint.MoveToPoint(
codefun.GetStartPoint(vsCMPart.vsCMPartHeader))
If (edPoint.Line >= ts.TopPoint.Line And _
edPoint.Line <= ts.BottomPoint.Line) _
Or commentAll Then
Dim params As CodeElements = codefun.Parameters
edPoint.Insert(NewLine)
edPoint.LineUp()
edPoint.Insert(ControlChars.Tab & commentStart & _
commentBorderConst & NewLine)
edPoint.Insert(ControlChars.Tab & _
commentStart & "<summary>" & NewLine)
edPoint.Insert(ControlChars.Tab & commentStart & _
ControlChars.Tab & "Summary of " & _
element.Name & NewLine)
edPoint.Insert(ControlChars.Tab & _
commentStart & "</summary>")
Dim element2 As CodeElement
Dim cp As CodeParameter
For Each element2 In params
cp = element2
edPoint.Insert(NewLine)
edPoint.Insert(ControlChars.Tab & commentStart)
edPoint.Insert("<param name=" & _
cp.Name & "></param>")
Next
edPoint.Insert(NewLine)
edPoint.Insert(ControlChars.Tab & _
commentStart & "<history>" & NewLine)
edPoint.Insert(ControlChars.Tab & _
commentStart & ControlChars.Tab)
edPoint.Insert("[" & UserName & "]" & ControlChars.Tab)
edPoint.Insert(System.DateTime.Now.ToLongDateString() & _
ControlChars.Tab & "Created" & NewLine)
edPoint.Insert(ControlChars.Tab & _
commentStart & "</history>" & NewLine)
edPoint.Insert(ControlChars.Tab & _
commentStart & commentBorderConst)
End If
End If
Next
Finally
DTE.UndoContext.Close()
End Try
End Sub
#End Region
#Region "InsertProperties"
Public Sub InsertProperties()
Dim ts As TextSelection = DTE.ActiveWindow.Selection
Dim propertyList(ts.TextRanges.Count - 1) As String
Dim iIndex As Integer
Dim startPoint As EditPoint = ts.TopPoint.CreateEditPoint()
Dim endPoint As TextPoint = ts.BottomPoint
Try
Do While (startPoint.LessThan(endPoint))
propertyList(iIndex) = _
startPoint.GetText(startPoint.LineLength).Trim.Replace("New",_
"").Replace("()", "")
startPoint.LineDown()
startPoint.StartOfLine()
iIndex += 1
Loop
Catch ex As System.Exception
Debug.WriteLine(ex)
End Try
Try
DTE.UndoContext.Open("Insert Properties")
For iIndex = 0 To propertyList.GetUpperBound(0)
InsertProperty(propertyList(iIndex))
Next
Catch ex As System.Exception
Debug.WriteLine(ex)
Finally
DTE.UndoContext.Close()
End Try
End Sub
Private Sub InsertProperty(ByVal prop As String)
If prop.Length = 0 Then Exit Sub
Dim pType As String
Dim pMember As String
Dim pName As String
Dim ts As TextSelection
If LineOrientedCommentStart() = vbCommentConst Then
pMember = prop.Substring(prop.IndexOf(" "c)).Trim
If pMember.IndexOf("=") >= 0 Then
pMember = Left(pMember, pMember.IndexOf("=")).Trim
End If
pType = pMember.Substring(pMember.IndexOf("As"))
If pMember.StartsWith("_") Then
pName = pMember.Substring(1)
Else
pName = pMember
End If
pName = pName.Substring(0, pName.IndexOf("As")).Trim
pName = Left(pName, 1).ToUpper & pName.Substring(1)
pMember = pMember.Substring(0, _
pMember.Length - pType.Length).Trim
Else
pType = prop.Substring(prop.IndexOf(" "c)).Trim
If pType.IndexOf("=") >= 0 Then
pType = Left(pType, pType.IndexOf("=")).Trim
End If
pMember = pType.Substring(pType.IndexOf(" "c)).Trim
pType = Left(pType, (pType.IndexOf(" "c))).Trim
If pMember.StartsWith("_") Then
pName = pMember.Substring(1)
Else
pName = pMember.Substring(0, 1).ToUpper & _
pMember.Substring(2)
End If
End If
ts = DTE.ActiveWindow.Selection
ts.MoveToPoint(ts.BottomPoint)
ts.NewLine(2)
ts.StartOfLine()
ts.Insert(GetPropertyString(pName, pType, pMember))
End Sub
Private Function GetPropertyString(ByVal propertyName _
As String, ByVal propertyType As String, _
ByVal propertyMember As String) As String
Dim propertyString As New StringBuilder
If LineOrientedCommentStart() = vbCommentConst Then
propertyString.Append(ControlChars.Tab & _
"Public Property " & propertyName & _
"() " & propertyType & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & "Get" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
"Return " & propertyMember & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & "End Get" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & "Set (ByVal Value " & _
propertyType & ")" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
propertyMember & "= Value" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & "End Set" & NewLine)
propertyString.Append(ControlChars.Tab & _
"End Property" & NewLine)
Else
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & "public " & _
propertyType & " " & propertyName & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & "{" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
"get" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
"{" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
ControlChars.Tab & "return " & _
propertyMember & ";" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
"}" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
"set" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
"{" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
ControlChars.Tab & propertyMember & _
"=Value;" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & ControlChars.Tab & _
"}" & NewLine)
propertyString.Append(ControlChars.Tab & _
ControlChars.Tab & "}" & NewLine)
End If
Return propertyString.ToString
End Function
#End Region
#Region "IncrementFields"
Public Sub IncrementTabIndexes()
IncrementLine("tabIndex=" & """")
End Sub
Public Sub IncrementUserText()
Dim userText As String
userText = InputBox("Enter the text to search" & _
" for and increment " & NewLine & _
"(e.g. tabIndex=" & """" & ")" & NewLine _
& NewLine & "n.b. searching is" & _
" case sensitive", "Custom Increment")
If userText.Length > 0 Then
IncrementLine(userText)
End If
End Sub
Private Sub IncrementLine(ByVal searchString As String)
Dim Insertion As New StringBuilder
Dim StartLine, EndLine, Temp, numLines, i As Integer
counter = -1
Try
DTE.UndoContext.Open("Increment Fields")
StartLine = DTE.ActiveDocument().Selection.TopLine
EndLine = DTE.ActiveDocument().Selection.BottomLine
numLines = EndLine - StartLine
If EndLine < StartLine Then
Temp = StartLine
StartLine = EndLine
EndLine = Temp
End If
For i = StartLine To EndLine
ActiveDocument().Selection.GotoLine(i)
ActiveDocument().Selection.SelectLine()
Insertion.Append(GetIncrementedLine(
DTE.ActiveDocument().Selection.text(), _
searchString))
Next
ActiveDocument().Selection.GotoLine(StartLine)
ActiveDocument().Selection.SelectLine()
DTE.ActiveDocument().Selection.LineDown(True, numLines)
DTE.ActiveDocument.Selection.Delete()
DTE.ActiveDocument.Selection.Insert(Insertion.ToString)
ActiveDocument().Selection.GotoLine(StartLine)
ActiveDocument().Selection.SelectLine()
DTE.ActiveDocument().Selection.LineDown(True, _
numLines)
Catch ex As System.Threading.ThreadAbortException
Catch ex As System.Exception
MessageBox.Show("An error has occurred" & _
" in the IncrementLineMacro: - " & ex.Message)
Finally
DTE.UndoContext.Close()
End Try
counter = -1
End Sub
Private Function GetIncrementedLine(ByVal line As String, _
ByVal searchString As String) As String
Dim midString, returnString, numString, _
leftString, rightString As String
Dim startIndex As Integer = InStr(line, searchString)
Dim endIndex, endSearchString As Integer
If startIndex > 0 Then
endSearchString = startIndex + searchString.Length
endIndex = InStr(endSearchString, line, _
"""", CompareMethod.Text)
numString = Mid(line, endSearchString, _
endIndex - endSearchString)
If counter = -1 Then
counter = Integer.Parse(numString)
Else
counter = counter + 1
End If
midString = searchString & counter & """"
leftString = Left(line, startIndex - 1)
rightString = Mid(line, endIndex + 1)
returnString = leftString & _
midString & rightString
Return returnString
End If
Return line
End Function
#End Region
#Region "Region Toggle"
Public Sub RegionToggle()
Dim regionSearch As String
Dim searchUp As Boolean
Dim ts As TextSelection = DTE.ActiveWindow.Selection
ts.SelectLine()
If ts.Text.IndexOf("#End Region") >= 0 Or _
ts.Text.IndexOf("endregion") >= 0 Then
searchUp = True
End If
If searchUp Then
ts.LineUp()
ts.FindText("#", _
vsFindOptions.vsFindOptionsBackwards)
Else
ts.FindText("#")
End If
End Sub
#End Region
#Region "Close all other windows"
Public Sub CloseAllOtherWindows()
Dim currentDoc As String
currentDoc = DTE.ActiveDocument.Name()
Dim doc As EnvDTE.Document
For Each doc In DTE.Documents
If doc.Name <> currentDoc Then
doc.Close()
End If
Next
DTE.Windows.Item(currentDoc).Activate()
End Sub
#End Region
#Region "Common Helper Methods"
Private Function LineOrientedCommentStart(Optional _
ByVal doc As Document = Nothing) As String
If (doc Is Nothing) Then
doc = DTE.ActiveDocument
End If
Dim ext As String = doc.Name
If (ext.EndsWith(".cs")) Then
Return cCommentConst
ElseIf (ext.EndsWith(".cpp")) Then
Return cCommentConst
ElseIf (ext.EndsWith(".h")) Then
Return cCommentConst
ElseIf (ext.EndsWith(".vb")) Then
Return vbCommentConst
ElseIf (ext.EndsWith(".idl")) Then
Return cCommentConst
End If
End Function
Private Function IsCSharp() As Boolean
Return (LineOrientedCommentStart() = cCommentConst)
End Function
#End Region
End Module
Points of Interest
Creating macros is pretty easy to do once you get started - and the ability to record things can help enormously.
The only thing I'm not really happy with is in the incrementing routines. I am retrieving the text of each line by selecting each line. It would be better if I could select it all and split it using the carriage return character, but I found that it was prone to be buggy, and sometimes the split returned multiple lines. If anyone finds a way to improve on this, please let me know.
Cheers, Darryl Gera.