Today I decided to continue work on a Visual Studio macro I started developing on Sunday (I mainly worked on it today though - Sunday was just document reading before heading back to Munich and going out with friends).
The samples from Microsoft simply use the comments from MSDN on functions/methods that are part of a class’s interface implementation.
I ‘documented’ a few of my functions by copying the summary out of MSDN but it’s a tedious job and consequently I decided to try and automate it. Although Visual Basic ‘sucks’ or is a least quite some change from writing C/C++/C# code all the time, the Automation model is very powerful and quite nice to use.
My macro provides two methods:
- One to add comments to all methods that are part of an interface’s implementation
- One to add comments to the method the cursor currently resides in
My code currently only adds comments from interface definitions that have been defined outside the current project. It’s pretty nifty in my opinion because usually these interface comments won’t change a lot and thus it’s safe to add them to the source code, while the interfaces one has written themself can still change and the macros can’t track that and/or update the comments afterwards.
However, I’ve added a configuration boolean, so this behavior can be turned off if needed.
My code won’t remove or replace comments, it will just add the comment in front of other comments - if the comment doesn’t already exist - I’ve tried to make it quite safe, so code loss or corruption will be avoided.
I hope this code is helpful. I’m releasing it under the Microsoft Public License. If there are good reasons to use a different license, feel free to tell me so :)
Cheers,
Andreas
' Andreas 'BlackHC' Kirsch 2008
' released under Microsoft Public License
Imports System
Imports EnvDTE
Public Module CommentImplementedMethods
' we usually only want to use external interfaces because it's safer to assume those won't change anytime soon
Const UseExternalInterfaceCommentsOnly = True
Private Function GetSummaryFromDocComment(ByVal docComment As String) As String
Const summaryStartTag = "<summary>"
Const summaryEndTag = "</summary>"
Dim summaryIndex = docComment.IndexOf(summaryStartTag)
If summaryIndex = -1 Then
Return ""
End If
Dim summaryStart = docComment.Substring(summaryIndex + summaryStartTag.Length)
Return summaryStart.Substring(0, summaryStart.IndexOf(summaryEndTag))
End Function
'Private Function GetInterfaceByName(ByRef classObject As CodeClass, ByRef name As String) As CodeInterface
' For Each implementedInterface As CodeInterface In classObject.ImplementedInterfaces
' If implementedInterface.Name = name Then
' Return implementedInterface
' End If
' Next
'End Function
Private Function GetMethod(ByRef interfaceObject As CodeInterface, ByRef methodObject As CodeFunction) As CodeFunction
Dim prototypeFlags As Int32 = vsCMPrototype.vsCMPrototypeParamTypes Or vsCMPrototype.vsCMPrototypeType
Dim prototype As String = methodObject.Prototype(prototypeFlags)
Dim separationIndex = methodObject.Name.IndexOf("."c)
If separationIndex <> -1 Then
Dim interfaceName = methodObject.Name.Substring(0, separationIndex)
If interfaceName <> interfaceObject.Name Then
Return Nothing
End If
Dim realMethodName = methodObject.Name.Substring(separationIndex + 1)
prototype = prototype.Replace(methodObject.Name, realMethodName)End If
For Each member As CodeElement In interfaceObject.Members
If member.Kind <> vsCMElement.vsCMElementFunction Then
For
Continue End If
Dim method As CodeFunction = member
If method.Prototype(prototypeFlags) = prototype Then
Return method
End If
Next
Return Nothing
End Function
Private Function GetInterfaceMethod(ByRef methodObject As CodeFunction) As CodeFunction
Dim parentClass As CodeClass = methodObject.Parent
For Each implementedInterface As CodeInterface In parentClass.ImplementedInterfaces
If implementedInterface.InfoLocation = vsCMInfoLocation.vsCMInfoLocationProject And UseExternalInterfaceCommentsOnly Then
For
Continue End If
Dim matchedMethod = GetMethod(implementedInterface, methodObject)
If Not IsNothing(matchedMethod) Then
Return matchedMethod
End If
Next
End Function
Private Function GetCurrentMethod() As CodeFunction
Dim sel As TextSelection = _
CType(DTE.ActiveDocument.Selection, TextSelection)Dim pnt As TextPoint = CType(sel.ActivePoint, TextPoint)
Dim method As CodeFunction = pnt.CodeElement(vsCMElement.vsCMElementFunction)
Return method
End Function
Private Sub AddCommentToMethod(ByRef methodObject As CodeFunction, ByRef newComment As String)
If methodObject.Comment.IndexOf(newComment) <> -1 Then
' This comment has already been added
Exit Sub
End If
methodObject.Comment = newComment & vbNewLine & methodObject.CommentEnd Sub
Public Sub CommentImplementedMethod()
Try
Dim currentMethod = GetCurrentMethod()
If IsNothing(currentMethod) Then
"Not inside a method scope at the moment!")
MsgBox(Exit Sub
End If
Dim interfaceMethod = GetInterfaceMethod(currentMethod)
If IsNothing(interfaceMethod) Then
If UseExternalInterfaceCommentsOnly Then
"*External* interface method could not be found!")
MsgBox(Else
"Interface method could not be found!")
MsgBox(End If
Exit Sub
End If
Dim newComment = GetSummaryFromDocComment(interfaceMethod.DocComment)
AddCommentToMethod(currentMethod, newComment)Catch ex As Exception
MsgBox(ex.Message)End Try
End Sub
Private Sub TraverseAndCommentImplementedMethods(ByRef elements As CodeElements)
For Each child As CodeElement In elements
If child.Kind = vsCMElement.vsCMElementFunction Then
Dim currentMethod = CType(child, CodeFunction)
If IsNothing(currentMethod) Then
Exit Sub
End If
Dim interfaceMethod = GetInterfaceMethod(currentMethod)
If IsNothing(interfaceMethod) Then
Exit Sub
End If
Dim newComment = GetSummaryFromDocComment(interfaceMethod.DocComment)
AddCommentToMethod(currentMethod, newComment)End If
TraverseAndCommentImplementedMethods(child.Children)Next
End Sub
Public Sub CommentAllImplementedMethodsInFile()
Try
TraverseAndCommentImplementedMethods(DTE.ActiveDocument.ProjectItem.FileCodeModel.CodeElements)Catch ex As Exception
MsgBox(ex.Message)End Try
End Sub
End Module