Thursday, March 6, 2008

Getting Ctrl-Right Arrow to respect Camel Case

The normal behaviour for Ctrl-LeftArrow and Ctrl-RightArrow in visual studio is "move left/right one word". This is a useful way of navigating the code quickly, however I often wished that when doing this it would stop between the individual words of an IdentifierWithManyWordsInsideIt. This style of capitalization is known as "CamelCase". I think Eclipse implements their ctrl-arrow movement this way.

The following macros implement this behaviour. You can remap Ctrl-Right and Ctrl-Left to MoveRightCamelCaseWord and MoveLeftCamelCaseWord.

I've also provided macros that mimic the behaviour of shift-ctrl-arrow, which extend the selection by one word.

Sub MoveRightCamelCaseWord()
MoveCamelCase(1, False)
End Sub

Sub MoveLeftCamelCaseWord()
MoveCamelCase(-1, False)
End Sub

Sub ExtendSelectionRightCamelCaseWord()
MoveCamelCase(1, True)
End Sub

Sub ExtendSelectionLeftCamelCaseWord()
'extending the selection to the left doesn't seem to work.
MoveCamelCase(-1, True)

' so instead I'll move to the left, then select the word to the right.
'MoveCamelCase(-1, False)
'MoveCamelCase(1, True)
End Sub

Function IsCharTypeDifferent(ByVal prevChar As Char, ByVal nextChar As Char)

If (Char.IsLetterOrDigit(prevChar) And Not Char.IsLetterOrDigit(nextChar)) Then
Return True
End If

If (Char.IsWhiteSpace(prevChar) And Not Char.IsWhiteSpace(nextChar)) Then
Return True
End If

If (Char.IsPunctuation(prevChar) And Not Char.IsPunctuation(nextChar)) Then
Return True
End If

Return False

End Function

' move the cursor to the left/right by one word, in this case the definition of a 'word' is changed
' to respect CamelCase. moveDirection is -1 or 1 depending on whether we're moving left or right
Sub MoveCamelCase(ByVal moveDirection As Integer, ByVal extend As Boolean)
Dim sel As TextSelection = DTE.ActiveDocument().Selection()
Dim objAnchor As VirtualPoint = sel.AnchorPoint

Dim finished As Boolean

While (Not finished)

Dim selPoint As VirtualPoint
If (moveDirection > 0) Then
selPoint = sel.BottomPoint()
Else
selPoint = sel.TopPoint()
End If


Dim leftChar, rightChar As Char
Dim editPoint As EditPoint

editPoint = selPoint.CreateEditPoint()

' Move the caret to the right by one
If (moveDirection > 1) Then
editPoint.CharRight(moveDirection)
Else
editPoint.CharLeft(-moveDirection)
End If

sel.MoveToPoint(editPoint, extend)

' Get the two characters surrounding the new caret position.
editPoint.CharLeft(1)
leftChar = editPoint.GetText(1)
editPoint.CharRight(1)
rightChar = editPoint.GetText(1)

' Which character out of these two is the 'prev' and 'next' depends on which direction we're going.
Dim prevChar, nextChar As Char
If (moveDirection > 0) Then
prevChar = leftChar
nextChar = rightChar
Else
prevChar = rightChar
nextChar = leftChar
End If

' Keep repeating the loop until we meet the right conditions for stopping
If (IsCharTypeDifferent(prevChar, nextChar)) Then
finished = True
Else
' Stop when the character to the right is uppercase letter and character to left is lower case.
If (Char.IsLetter(leftChar) And Char.IsLetter(rightChar)) Then
If (Char.IsLower(leftChar) And Char.IsUpper(rightChar)) Then
finished = True
End If
End If

End If

End While

End Sub

2 comments:

macbirdie said...

Hey! Great macros, you saved me from the MovingCursorThroughCamelCase nightmare!

The macro balloon notification was annoying and I found a solution to disable it:
http://blogs.msdn.com/johnls/archive/2006/01/17/visual-studio-macro-balloon.aspx

Andrey Epifantsev said...

I cannot understand: how to install this great macro to Visual Studio?