Archive for the ‘visual basic’ Category

How to choose functions in Visual Studio with the keyboard

Wednesday, September 12th, 2007

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

Post to Twitter Tweet This Post