Helixoft Blog

Peter Macej - lead developer of VSdocman - talks about Visual Studio tips and automation

Collapse All XML Comments in VB .NET or C#

Today, a user of our VSdocman has asked me for interesting functionality in Visual Studio. He wants to collapse all XML comments in the file with one click. There is no such command in VS so I wrote a macro that does the job. The macro only works in VS 2005 and only for C# and VB .NET code.

In fact, there are two macros, one for collapsing the comments and one for toggling the comment outlining expansion. The first one seems to be better for our purposes. But due to limitations of VS automation, it has one drawback. When you manually collapse this comment

XML comment

you'll get the following:

Outline

You can see the summary part in collapsed section. However, collapsing macro produces the following:

Collapsed Ellipsis

When you click + sign, you'll get the summary collapsed section and you must click once again to expand the section. This will be forgotten after restarting VS.

On the other hand, the toggle macro works correctly - as manual collapsing. But of course, it doesn't collapse always, only if comment is expanded. But I think this is very common case.

The macros are named ToggleXmlComments and CollapseXmlComments.

Here is the macro code:

''' <summary>
''' Collapse XML comment for all code members
'''</summary>
Sub CollapseXmlComments()
    Try
        DTE.UndoContext.Open("Collapse XML comments")

        Dim ce As CodeElement2
        For Each ce In DTE.ActiveDocument.ProjectItem.FileCodeModel.CodeElements
            collapseSubmembers(ce, False)
        Next

        DTE.UndoContext.Close()
    Catch ex As Exception
        DTE.UndoContext.Close()
    End Try
End Sub

''' <summary>
''' Toggles the outline of XML comment for all code members.
'''</summary>
Sub ToggleXmlComments()
    Try
        DTE.UndoContext.Open("Toggle XML comments outline")

        'remember selection
        Dim oldAnchor, oldActive As EnvDTE.TextPoint
        Dim sel As TextSelection = CType(DTE.ActiveDocument.Selection, TextSelection)
        oldAnchor = sel.AnchorPoint.CreateEditPoint
        oldActive = sel.ActivePoint.CreateEditPoint

        Dim ce As CodeElement2
        For Each ce In DTE.ActiveDocument.ProjectItem.FileCodeModel.CodeElements
            collapseSubmembers(ce, True)
        Next

        'restore selection
        sel.MoveToAbsoluteOffset(oldAnchor.AbsoluteCharOffset) 'set active point
        sel.SwapAnchor() 'set anchor to active point
        sel.MoveToAbsoluteOffset(oldActive.AbsoluteCharOffset, True)

        DTE.UndoContext.Close()
    Catch ex As Exception
        DTE.UndoContext.Close()
    End Try
End Sub

''' <summary>Collapses the member and its sub members if any.</summary>
''' <param name="ce">The member.</param>
''' <param name="toggle">If True, the comment outline is toggled,
''' otherwise it is collapsed.</param>
Private Sub collapseSubmembers(ByVal ce As CodeElement2, ByVal toggle As Boolean)
    Dim memberStart, commentStart, commentEnd As EditPoint2
    Dim comChars As String

    Select Case DTE.ActiveDocument.ProjectItem.FileCodeModel.Language
        Case "{B5E9BD33-6D3E-4B5D-925E-8A43B79820B4}"
            'VB
            comChars = "'''"
        Case Else
            'C#
            comChars = "///"
    End Select

    Try
        memberStart = ce.GetStartPoint(vsCMPart.vsCMPartWholeWithAttributes).CreateEditPoint
        commentStart = getCommentStart(memberStart.CreateEditPoint, comChars)
        commentEnd = getCommentEnd(commentStart.CreateEditPoint, comChars)
        If toggle Then
            'toggle
            CType(DTE.ActiveDocument.Selection, TextSelection).MoveToPoint(commentStart)
            DTE.ExecuteCommand("Edit.ToggleOutliningExpansion")
        Else
            'collapse
            commentStart.OutlineSection(commentEnd)
        End If
    Catch ex As Exception
    End Try

    'try submembers
    If ce.IsCodeType Then
        Dim ce2 As CodeElement2
        For Each ce2 In CType(ce, CodeType).Members
            collapseSubmembers(ce2, toggle)
        Next
    ElseIf ce.Kind = vsCMElement.vsCMElementNamespace Then
        Dim ce2 As CodeElement2
        For Each ce2 In CType(ce, CodeNamespace).Members
            collapseSubmembers(ce2, toggle)
        Next
    End If
End Sub

''' <summary>Gets starting point of the comment.</summary>
''' <param name="ep">Commented member start point.</param>
''' <param name="commentChars">The comment character.
''' It is ''' for VB or /// for C#.</param>
''' <returns></returns>
Private Function getCommentStart(ByVal ep As EditPoint2, ByVal commentChars As String) As EditPoint2
    Try
        Dim line, lastCommentLine As String
        ep.StartOfLine()
        ep.CharLeft()
        While Not ep.AtStartOfDocument
            line = ep.GetLines(ep.Line, ep.Line + 1).Trim
            If line.Length = 0 Or line.StartsWith(commentChars) Then
                If line.Length> 0 Then
                    lastCommentLine = ep.Line
                End If
                ep.StartOfLine()
                ep.CharLeft()
            Else
                Exit While
            End If
        End While

        ep.MoveToLineAndOffset(lastCommentLine, 1)
        While ep.GetText(commentChars.Length) <> commentChars
            ep.CharRight()
        End While

        Return ep.CreateEditPoint
    Catch ex As Exception
    End Try
End Function

''' <summary>Gets ending point of the comment.</summary>
''' <param name="ep">Comment start point.</param>
''' <param name="commentChars">The comment character.
''' It is ''' for VB or /// for C#.</param>
''' <returns></returns>
Private Function getCommentEnd(ByVal ep As EditPoint2, ByVal commentChars As String) As EditPoint2
    Try
        Dim line As String
        Dim lastCommentPoint As EditPoint
        lastCommentPoint = ep.CreateEditPoint
        ep.EndOfLine()
        ep.CharRight()
        While Not ep.AtEndOfDocument
            line = ep.GetLines(ep.Line, ep.Line + 1).Trim
            If line.StartsWith(commentChars) Then
                lastCommentPoint = ep.CreateEditPoint
                ep.EndOfLine()
                ep.CharRight()
            Else
                Exit While
            End If
        End While

        lastCommentPoint.EndOfLine()
        Return lastCommentPoint
    Catch ex As Exception
    End Try
End Function

If you don't know it yet, you can learn how to create and run macro. You can of course create a toolbar or menu button or assign keyboard shortcut to it.

2 Responses to “Collapse All XML Comments in VB .NET or C#”

  1. Arnoud:

    Very useful, makes code much more readable. Thanks.

  2. Klavs Madsen:

    Thank F****** you!!!

    Been looking for this forever but never got around to learning how to write it myself :)

    Mail me your adress and I will send you a six-pack of good danish beer!

Leave a Reply

authimage

Can't read the code? Try reloading the page before adding your comment to get a new code image.

 

VSdocman

Code Commenter and Generator of Class Documentation for VB .NET and C#

Create XML comments, generate MSDN documentation, IntelliSense, F1 help and more for your VB .NET and C# code.

More... | Download | Buy


.



 
© Copyright 2007 Helixoft All rights reserved. | Privacy Statement