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 higher 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.

Comments

# Arnoud 2007-03-28 10:37
Very useful, makes code much more readable. Thanks.
# Klavs Madsen 2007-05-09 18:54
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!
# Ilya Tchivilev 2009-11-27 05:35
Awesome. Works fine in Visual Studio 2008 as well by the way. Great work.
# recep 2010-02-24 15:44
instead you can use ctrl + m + l for expand all and ctrl + m + o for collapse all without writing any macro :)
# Peter Macej 2010-02-24 16:23
To recep:
You cannot. The ctrl + m + l/ ctrl + m + o (Toggle All Outlining/Colla pse to Definitions) will expand/collapse ALL outlining in the file, including regions, method bodies, etc. This is not what we want. My macro toggles only XML comments. Everything else is left untouched.
# Josh 2010-06-14 21:11
ctrl + m + l/ ctrl + m + o worked well for me :)

just remember to use "ctrl + m + o" to collapse and "ctrl + m + l" to expand!
# Peter Macej 2010-06-14 21:56
Josh, have you seen my previos comment? Toggle All Outlining/Colla pse to Definitions is not the same as Expand/Collapse just XML comments.
# Ausome 2010-11-18 02:09
With all the Comment Hiding ideas this is probably the best solution so far.. until M$ can build in a complete Hiding Solution.

Thanks
# Alexei Malinovski 2011-05-28 17:12
///
///
/// Bla bla bla
/// Bla bla bla
///
///

Notice first empty /// line . It allows to hide text from XML comments when collapsed.
this solution is useful when using MS Visual Studio Express where Macroses are disable :(
# Volker von Einem 2011-11-07 08:20
Thank you! Thank you! Thank you!

Just what I was looking for!
# Volker von Einem 2011-11-07 08:21
Works also with VS2010
# Adam Ralph 2012-04-21 10:54
Very nice work - thanks, this will be hugely useful for me.
# Vineet tiwari 2012-06-14 13:58
This code is more helpful for me if it collapse all comments inside the property or Fucntion/Method
thanks and advance
# Harin 2017-01-05 07:24
How to get this working with VS2015?

Comments are now closed for this entry

 

Start generating your .NET documentation now!
DOWNLOAD
Free, fully functional trial