« Fast struct/class copy and exchange in C++A lot of refactoring »

Upper to camel case constant/enum conversion macro

Here's VS macro for searching and converting upper cased constants and enumerations to camel case style. For example, you can convert string like "ENTITY_FLAG_VISIBLE" to "EntityFlagVisible" or even better to "Entity::FlagVisible" in whole solution. Very useful for refactoring. Released under GNU GPL.

Function FindAndReplaceInSolution(ByVal FindWhat As String, ByVal ReplaceWith As String, Optional ByVal MatchCase As Boolean = True, Optional ByVal WholeWord As Boolean = True) As Boolean
    'DTE.ExecuteCommand("Edit.Replace")
    DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
    DTE.Find.FindWhat = FindWhat
    DTE.Find.ReplaceWith = ReplaceWith
    DTE.Find.Target = vsFindTarget.vsFindTargetSolution
    DTE.Find.MatchCase = MatchCase
    DTE.Find.MatchWholeWord = WholeWord
    DTE.Find.MatchInHiddenText = False
    DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
    DTE.Find.KeepModifiedDocumentsOpen = False
    DTE.Find.FilesOfType = ""
    DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
    If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
        Return False 'Throw New System.Exception("vsFindResultNotFound")
    End If

    Return True
End Function

Function FindInDocument(ByVal FindWhat As String, Optional ByVal MatchCase As Boolean = False, Optional ByVal WholeWord As Boolean = False) As Boolean
    'DTE.ExecuteCommand("Edit.Find")
    DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocument
    DTE.Find.MatchWholeWord = WholeWord
    DTE.Find.MatchCase = MatchCase
    DTE.Find.FindWhat = FindWhat
    DTE.Find.Action = vsFindAction.vsFindActionFind
    DTE.Find.Backwards = False
    DTE.Find.MatchInHiddenText = False
    DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
    If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
        Return False 'Throw New System.Exception("vsFindResultNotFound")
    End If

    Return True
End Function

Function FindAndNormalizeConstantName(Optional ByVal PrefixToDelete As String = "", Optional ByVal PrefixReplaceWith As String = "", Optional ByVal WholeSolution As Boolean = False) As Boolean
    ' Converts CONSTANT_NAME to ConstantName

    Dim Document As EnvDTE.Document = DTE.ActiveDocument

    If (Not FindInDocument("_", False, False)) Then
        Return False
    End If

    Document.Selection.WordLeft()
    Document.Selection.WordRight(True)

    Dim Name As String = Document.Selection.Text

    For Index = Name.Length - 1 To 0 Step -1
        If (Not Char.IsWhiteSpace(Name.Chars(Index))) Then
            Dim WhiteSpaceCount As Integer = Name.Length - (Index + 1)
            If (WhiteSpaceCount > 0) Then
                Name = Name.Trim() ' Remove any whitespaces
                Document.Selection.CharLeft(WhiteSpaceCount) ' Fit selection
            End If
            Exit For
        End If
    Next

    If ((Name.Chars(0) <> "_") And (Char.IsUpper(Name.Chars(1)))) Then
        'Its not begining with "_" prefix, and so it's constant or enum.

        Dim Result As New System.Text.StringBuilder()
        Dim Original As String = Name ' Make backup

        If (PrefixToDelete.Length > 0) Then
            Name = Name.Remove(0, PrefixToDelete.Length) ' Delete prefix
            Result.Append(PrefixReplaceWith) ' Write new prefix to result
        End If

        Dim Uppercase As Boolean = True

        For Index = 0 To Name.Length - 1
            Dim Value As Char = Name.Chars(Index)

            If (Name.Chars(Index) = "_") Then
                Uppercase = True
            Else
                If (Uppercase) Then
                    Result.Append(Char.ToUpper(Value))
                    Uppercase = False
                Else
                    Result.Append(Char.ToLower(Value))
                End If
            End If
        Next

        If (WholeSolution) Then
            FindAndReplaceInSolution(Original, Result.ToString(), True, True)
        Else
            Document.Selection.Text = Result.ToString()
        End If

    Else
        ' Skip it
        Document.Selection.WordRight()
    End If

    Return True
End Function

Example usage, replaces "ENTITY_SOMETHING" with "Entity::Something":

Sub TemporaryMacro()
    ' Run macro 10 times when pressed Ctrl+Shift+P
    For Index = 1 To 10
        If (Not FindAndNormalizeConstantName("ENTITY_", "Entity::", False)) Then
            Exit For
        End If
    Next
End Sub

Another example, convert everything (in opened document only):

Sub TemporaryMacro()
    FindAndNormalizeConstantName("", "", False)
End Sub

Also, I should let you know that it works only when the names contains underscore character (one is enough) and that if you have guard macros that doesn't begin with underscore, they will be replaced as well. All my guard macros begins with underscore (e.g. _AGATE_XML_DOCUMENT_HPP_) so if you're using different style, you should modify the macro, or run it manually, like in the usage example.


Hope it helps.


Edit: If you want a faster way how to refactor, load the whole document into memory and use string class to replace all occurences. It's much faster than using find/replace dialog.


Categories: Programming, Snippets

No feedback yet