KnowDotNet NetRefactor

Get XML Comments Ready for VS2005

XML Comment Magic with Macros

by Les Smith
Print this Article Discuss in Forums

Want to see a little Macro Magic produce complete XML comments in your VS2003 VB.NET programs?  Get ready for the newest release of VB.NET.

Ever just want to have some fun with a Macro or an Add-in?  I am still working in VS2003 where I currently contract.  But privately, I do a lot of work in VS2005.  One of the good new features is that XML Comments have been added to VB.NET in this newest version.  So as I am developing new code and maintaining someone else's old code, I want to add XML comments to the old code.  Since I am not yet using Whidbey where I work, I decided to play with a macro.

First, I wanted it to create the XML Comments for a method just as the new version of VB.NET would do.  Since this is a macro, I can't monitor the key strokes in the IDE.  Therefore, I have to double-click on a macro in the Macro Explorer to cause the code for this article to work, but it produces the XML comments with an added plus of picking up existing comments, which is something that even Whidbey can't do!  I always get a kick out of doing something in extensibility that Microsoft has not yet done.  The only problem is that they usually do it in the "NEXT" version of the IDE.

Anyway, here is a sample test Function before I run my macro.

   ' this is a test comment nbr 1
   ' this is comment nbr 2
   ' and comment number 3
   ' finally comment nbr 4
   Private Function TestMacro(ByVal parm1 As String, ByRef parm2 As String) As String

   End Function

Now, I will place my cursor anywhere in the Function definition line, and then double-click on the macro in the Macro Explorer.  The resulting code is shown below.

   ''' <summary>
   ''' this is a test comment nbr 1
   ''' this is comment nbr 2
   ''' and comment number 3
   ''' finally comment nbr 4
   ''' </summary>
   ''' <param name="parm1" ></param>
   ''' <param name="parm2" ></param>
   ''' <returns>String
   ''' <remarks></remarks>
   Private Function TestMacro(ByVal parm1 As String, ByRef parm2 As String) As String

   End Function

Notice that the existing comments have been picked up from the code window and placed inside the "<summary>" block of the XML comments.  It will also pick up comments right after the procedure definition line, assuming that the definition line is not continued.  Then the macro picked up the parameter names.  Finally, it determnes that the method is a Function and creates the "<returns>" XML block, and again, it picks up the return type, which Whidbey does not do. Neat, uh?  Well, I told you I just like to have fun with extensibility.  Actually, as I write this article and the code for the macro, my wife thinks I am really working.  Please don't tell her I'm just having fun after a day of working on a computer.  She already knows I'm addicted, but supper is not quite ready, but it sure smells good.

Now for the code of the macro.  You will see that there is a basic Sub for the majority of the process and a Function that is called by the sub to capture the comments and move them to the "summary" block while deleting them from their original position.  I won't explain all of the code, but it is rather straight-forward usage of the TextSelection object and a couple of simple Regexes.

Imports EnvDTE
Imports System.Diagnostics
Imports System.Text.RegularExpressions

Public Module Module1
    
Public Sub GenXMLSummaryWithParams()
        
Dim ts As TextSelection = DTE.ActiveDocument.Selection
        
Dim stLine As Integer = ts.ActivePoint.Line

        ts.StartOfLine()
        ts.EndOfLine(
True)
        
Dim line As String = ts.Text
        ts.StartOfLine()
        
Dim comments As String = GetExistingComments(ts)
        comments &= GetForwardComments(ts)

        
Dim s As String = "   ''' <summary>" & vbCrLf
        
If comments.Length > 0 Then
            s &= comments
        
Else
            s &= "   ''' " & vbCrLf
        
End If

        s &= "   ''' </summary>" & vbCrLf

        
Dim mc As MatchCollection = Regex.Matches(line, "(ByVal|ByRef)\s+(?<var>\w+)\s+As\s+\w+")

        
Dim paramCount As Integer = 0

        
For Each m As Match In mc
            
If m.Groups("var").Value.Length > 0 Then
                paramCount += 1
                s &=
"   ''' <param name=""" & m.Groups("var").Value & """ ></param>" & vbCrLf
            
End If
        Next
        If line.IndexOf("Function ") > -1 Then
            Dim m As Match = Regex.Match(line, "\)\s+As\s+(?<retval>\w+)")
            s &=
"   ''' <returns>"
            If m.Success Then
                s &= m.Groups("retval").Value
            
End If
            s &= "</returns>" & vbCrLf
        
End If
        s &= "   ''' <remarks></remarks>" & vbCrLf
        ts.Insert(s)

        
If comments.Length > 0 Then
            ts.LineUp(Count:=2 + paramCount)
            ts.StartOfLine()
            ts.EndOfLine()
        
End If
    End Sub

    Private Function GetExistingComments(ByVal ts As TextSelection) As String
        Dim comments As String = String.Empty

        
Do While True
            ts.StartOfLine()
            ts.LineUp(
True)
            
Dim line As String = ts.Text.Trim
            
If line.StartsWith("'") Then
                comments = "   ''" & line & vbCrLf & comments
                ts.Delete()
            
Else
                ts.StartOfLine()
                ts.LineDown()
                ts.StartOfLine()
                
Exit Do
            End If
        Loop
        Return comments
    
End Function
    Private Function GetForwardComments(ByVal ts As TextSelection) As String
        Dim comments As String = String.Empty
        
Do While True
            ts.EndOfLine()
            ts.LineDown(
True)
            
Dim line As String = ts.Text.Trim
            
If line.StartsWith("'") Then
                comments &= "   ''" & line & vbCrLf
                ts.Delete()
            
Else
                ts.StartOfLine()
                ts.LineUp()
                ts.StartOfLine()
                
Exit Do
            End If
        Loop
        Return comments
    
End Function
End
Module


That's all there is to it.  To use the macro, copy the entire Module to your Macro IDE and then run it as I described above.   Happy New Year!

Writing Add-Ins for Visual Studio .NET
Writing Add-ins for Visual Studio .NET
by Les Smith
Apress Publishing