Region Tools for Visual Studio

The C# language offers the #region and #endregion statements as a means of code organization. Microsoft Visual Studio lets users visually collapse such regions to a single line, and automatically collapses all regions when a C# source code file is opened for the first time.

Unfortunately, this is the total extent of Visual Studio support for regions. The IDE does not otherwise distinguish between C# regions and other constructs with outlining support. Collapsing and expanding commands operate on all supported constructs within the scope of the command, not just regions. This is usually undesirable – who would want to collapse all for loops or XML comments, for example? There is an old MSDN Feedback item asking for dedicated #region support but Microsoft appears in no hurry to do anything about it.

On the other hand, one could write a Visual Studio macro that provides such functionality, and that’s exactly what Roland Weigelt did in 2003. In the comment section of the linked weblog, Andrew Eno posted a revised version that deals with nested regions. This version forms the basis for the code shown below.

The Code

Simply copy & paste the code below into a project called “RegionTools” within the Visual Studio Macros IDE. You can then assign each of the three macros a keyboard shortcut within the main IDE. The code is intended for Visual Studio 2010; change the indicated lines for Visual Studio 2008 (search for “VS2008”). Visual Studio 2012 does not support macros (in any edition) and therefore cannot run this code at all, sorry!

Known Issues: ToggleParentRegion does nothing if the cursor is at the end of a collapsed #region line. In that case, SelectLine inadvertently expands the region which is immediately re-collapsed by the following call to ToggleOutliningExpansion.

Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports EnvDTE90a ' remove for VS2008
Imports EnvDTE100 ' remove for VS2008
Imports System.Diagnostics

' Macros for improving keyboard support for "#region ... #endregion"
' Original version written by Roland Weigelt, last modified 2003-08-14
' Original source: http://weblogs.asp.net/rweigelt/archive/2003/07/06/9741.aspx
' Includes changes by Andrew Eno, posted on 2003-10-08 at the same location

Public Module RegionTools

    ' Toggles the current region surrounding the cursor
    Sub ToggleParentRegion()
        DTE.SuppressUI = True
        Dim objSelection As TextSelection = DTE.ActiveDocument.Selection
        Dim objPosition As EnvDTE.TextPoint = objSelection.AnchorPoint
        ' select current line in case it contains a #region
        objSelection.SelectLine()
        Dim regionFound As Boolean = InStr(objSelection.Text.ToLower(), "#region") > 0 _
            OrElse objSelection.FindText("#region", vsFindOptions.vsFindOptionsBackwards)
        ' clear previous line selection
        objSelection.MoveToPoint(objPosition)
        If regionFound Then
            DTE.ExecuteCommand("Edit.ToggleOutliningExpansion")
            objSelection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn)
        End If
        DTE.SuppressUI = False
    End Sub

    ' Expands all regions in the current document
    Sub ExpandAllRegions()
        DTE.SuppressUI = True
        Dim objSelection As TextSelection = DTE.ActiveDocument.Selection()
        objSelection.StartOfDocument()
        While objSelection.FindText("#region", vsFindOptions.vsFindOptionsMatchInHiddenText)
            ' do nothing since FindText automatically expands any found #region
        End While
        objSelection.StartOfDocument()
        DTE.SuppressUI = False
    End Sub

    ' Collapses all regions in the current document
    Sub CollapseAllRegions()
        ExpandAllRegions()
        DTE.SuppressUI = True
        Dim objSelection As TextSelection = DTE.ActiveDocument.Selection
        objSelection.EndOfDocument()
        ' search backward to find innermost nested regions first
        While objSelection.FindText("#region", vsFindOptions.vsFindOptionsBackwards)
            DTE.ExecuteCommand("Edit.ToggleOutliningExpansion")
            ' objSelection.EndOfDocument() ' uncomment for VS2008
        End While
        objSelection.StartOfDocument()
        DTE.SuppressUI = False
    End Sub
End Module