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
