In Visual Studio, there’s pretty good support for keys, but for me it lacks one significant feature. That is the ability to jump to a function from inside a class. That is, if you’ve got a class open the text editor, you want to see a list of functions in the class, choose one, and navigate to it, via the keyboard. You’ll be familiar with it from the two drop-down lists at the top of the code window; however, this drop-down can’t be activated by keyboard.
Here’s a cheap and cheerful implementation of the same features. Put it into your macro module, and map the macro to a keyboard shortcut.
Sub ChooseFunctionAndNavigateInIde()
Try
Dim functionIndex As New Dictionary(Of String, Integer)
Dim doc As Document = DTE.ActiveWindow.Document
For Each elem As CodeElement In doc.ProjectItem.FileCodeModel.CodeElements
BuildCodeElements(elem, functionIndex, 0)
Next
Dim result As Integer = FChooser.SelectItem(functionIndex)
If (result = -1) Then
‘ user cancelled
Return
Else
Dim sel As TextSelection = CType(doc.Selection, TextSelection)
sel.GotoLine(result, True)
End If
Catch ex As Exception
End Try
End Sub
Private Sub BuildCodeElements(ByVal elem As CodeElement, ByVal functionIndex As Dictionary(Of String, Integer), ByVal depth As Integer)
Try
Select Case elem.Kind
Case vsCMElement.vsCMElementClass, vsCMElement.vsCMElementDelegate, vsCMElement.vsCMElementEnum, vsCMElement.vsCMElementEvent, vsCMElement.vsCMElementEventsDeclaration, vsCMElement.vsCMElementFunction, vsCMElement.vsCMElementInterface, vsCMElement.vsCMElementModule, vsCMElement.vsCMElementProperty
Dim name As String = Space(depth * 4) & elem.Name
Dim line As Integer = elem.GetStartPoint(vsCMPart.vsCMPartWholeWithAttributes).Line
functionIndex.Add(name, line)
Case Else
End Select
Catch ex As Exception
End Try
For Each child As CodeElement In elem.Children
BuildCodeElements(child, functionIndex, depth + 1)
Next
End Sub
Private Class FChooser
Inherits Form
Dim lv As ListView
Public Shared Function SelectItem(ByVal list As Dictionary(Of String, Integer)) As Integer
Dim chooser As New FChooser(List)
chooser.BringToFront()
chooser.ShowDialog()
Return chooser.Result
End Function
Public Sub New(ByVal list As Dictionary(Of String, Integer))
lv = New ListView()
lv.Dock = DockStyle.Fill
lv.View = View.List
For Each kvp As KeyValuePair(Of String, Integer) In list
Dim lvi As ListViewItem = lv.Items.Add(kvp.Key)
lvi.Tag = kvp.Value
Next
Me.Controls.Add(lv)
AddHandler lv.KeyUp, AddressOf Key
AddHandler lv.DoubleClick, AddressOf DoubleClick
End Sub
Public Result As Integer = -1
Public Sub DoubleClick(ByVal sender As Object, ByVal e As EventArgs)
If (Me.lv.SelectedItems.Count = 1) Then
Me.Result = CType(lv.SelectedItems(0).Tag, Integer)
Me.Close()
End If
End Sub
Public Sub Key(ByVal sender As Object, ByVal e As KeyEventArgs)
If (e.KeyCode = Keys.Enter) Then
Me.Result = CType(lv.SelectedItems(0).Tag, Integer)
Me.Close()
ElseIf (e.KeyCode = Keys.Escape) Then
Result = -1
Me.Close()
Else
‘ could be a letter, and therefore a seek
Dim letter As String = e.KeyCode.ToString().ToLower()
If letter.Length = 1 And Regex.IsMatch(letter, “[a-z0-9]”) Then
‘ find the first entry starting with this letter
Dim found As ListViewItem = Nothing
For Each lvi As ListViewItem In Me.lv.Items
Dim lviText As String = lvi.Text.Trim().ToLower()
If (found Is Nothing And lviText.StartsWith(letter)) Then
found = lvi
End If
Next
If (found Is Nothing = False) Then
For Each lvi As ListViewItem In Me.lv.Items
lvi.Selected = False
Next
found.Selected = True
End If
End If
End If
End Sub
End Class