Saturday, September 22, 2007

In my last article on opening a file in Visual Studio, I have described how to write a macro to open a file given it's name. Now we can take it one step further. How about opening a file containing a type name.

Unlike opening a file, where there is a built in function, searching for a type is a complicated task. We need to loop over all projects, all items, and then analyze the code. Other than that, the code is rather simple if you understand recursion:

Sub OpenType()
    Dim typeName As String = InputBox("Type name:").ToLower()
    If String.IsNullOrEmpty(typeName) Then
        Return
    End If
    Dim str As String
    Dim projectItemContainingType As ProjectItem
    For Each project As Project In DTE.Solution.Projects
        projectItemContainingType = FindProjectItemWithType(typeName, project.ProjectItems)
        If projectItemContainingType IsNot Nothing Then
            projectItemContainingType.Open()
            projectItemContainingType.Document.Activate()
            Return
        End If
    Next
    MsgBox("Type not found", MsgBoxStyle.Information)
End Sub
Private Function FindProjectItemWithType(ByVal typeName As String, ByVal projectItems As ProjectItems) As ProjectItem
    Dim projectItemContainingType As ProjectItem
    For Each projectItem As ProjectItem In projectItems
        Dim codeModel As FileCodeModel = projectItem.FileCodeModel
        If codeModel IsNot Nothing Then
            projectItemContainingType = FindProjectItemWithType(typeName, codeModel.CodeElements)
        End If
        If projectItemContainingType Is Nothing AndAlso projectItem.ProjectItems IsNot Nothing AndAlso projectItem.ProjectItems.Count > 0 Then
            projectItemContainingType = FindProjectItemWithType(typeName, projectItem.ProjectItems)
        End If
        If projectItemContainingType IsNot Nothing Then
            Return projectItemContainingType
        End If
    Next
    Return Nothing
End Function
Private Function FindProjectItemWithType(ByVal typeName As String, ByVal codeElements As CodeElements) As ProjectItem
    Dim projectItem As ProjectItem
    For Each codeElement As CodeElement In codeElements
        If codeElement.Kind = vsCMElement.vsCMElementClass OrElse codeElement.Kind = vsCMElement.vsCMElementEnum Then
            If codeElement.Name.ToLower() = typeName Then
                projectItem = codeElement.ProjectItem
                Return projectItem
            End If
            Continue For
        End If
        If (codeElement.Children.Count > 0) Then
            projectItem = FindProjectItemWithType(typeName, codeElement.Children)
            If projectItem IsNot Nothing Then
                Return projectItem
            End If
        End If
    Next
    Return Nothing
End Function

Some shortcut to OpenType procedure (like Ctrl + o, Ctrl + t) and you are ready (I describe how to assign a shortcut in my previous article). It finds top level classes and enums. I have found that searching for inner types - like class or enum defined inside another class, makes it run very slowly - but that is nothing we cannot handle.

If you think you like working with macros and need a bit of practice, there is one thing that can be done that will speed things a lot: instead of searching the whole solution for a type, every time, just build a dictionary of typename/projectitem. That will make your searches lightning fast. Just keep in mind that you have to update this cache every time someone adds or removes types. This is not an easy thing to do! Not by far!s

kick it on DotNetKicks.com