|
| 1 | +Attribute VB_Name = "Formatter" |
| 2 | +Option Explicit |
| 3 | + |
| 4 | +Private Const BEG_SUB = "Sub " |
| 5 | +Private Const END_SUB = "End Sub" |
| 6 | +Private Const BEG_PB_SUB = "Public Sub " |
| 7 | +Private Const BEG_PV_SUB = "Private Sub " |
| 8 | + |
| 9 | +Private Const BEG_FUN = "Function " |
| 10 | +Private Const END_FUN = "End Function" |
| 11 | +Private Const BEG_PB_FUN = "Public Function " |
| 12 | +Private Const BEG_PV_FUN = "Private Function " |
| 13 | + |
| 14 | +Private Const BEG_PROP = "Property " |
| 15 | +Private Const END_PROP = "End Property" |
| 16 | +Private Const BEG_PB_PROP = "Public Property " |
| 17 | +Private Const BEG_PV_PROP = "Private Property " |
| 18 | + |
| 19 | +Private Const BEG_IF = "If " |
| 20 | +Private Const END_IF = "End If" |
| 21 | +Private Const BEG_WITH = "With " |
| 22 | +Private Const END_WITH = "End With" |
| 23 | + |
| 24 | +Private Const BEG_FOR = "For " |
| 25 | +Private Const END_FOR = "Next " |
| 26 | +Private Const BEG_DOWHILE = "Do While " |
| 27 | +Private Const BEG_DOUNTIL = "Do Until " |
| 28 | + |
| 29 | +Private Const BEG_TYPE = "Type " |
| 30 | +Private Const END_TYPE = "End Type" |
| 31 | +Private Const BEG_PB_TYPE = "Public Type " |
| 32 | +Private Const BEG_PV_TYPE = "Private Type " |
| 33 | + |
| 34 | +' Single words that must exactly match the entire line |
| 35 | +Private Const ONEWORD_ELSE = "Else" |
| 36 | +Private Const BEG_END_ELSEIF = "ElseIf" |
| 37 | +Private Const ONEWORD_END_FOR = "Next" |
| 38 | +Private Const ONEWORD_END_LOOP = "Loop" |
| 39 | + |
| 40 | +Private Const INDENT = " " |
| 41 | + |
| 42 | +Private words As Dictionary 'Keys are Strings, Value is an Integer indicating change in indentation |
| 43 | +Private indentation(0 To 20) As Variant ' Prevent repeatedly building the same strings by looking them up in here |
| 44 | + |
| 45 | +Private Sub initialize() |
| 46 | + initializeWords |
| 47 | + initializeIndentation |
| 48 | +End Sub |
| 49 | + |
| 50 | +Private Sub initializeIndentation() |
| 51 | + Dim indentString As String |
| 52 | + indentString = "" |
| 53 | + Dim i As Integer |
| 54 | + For i = 0 To UBound(indentation) |
| 55 | + indentation(i) = indentString |
| 56 | + indentString = indentString & INDENT |
| 57 | + Next |
| 58 | +End Sub |
| 59 | + |
| 60 | +Private Sub initializeWords() |
| 61 | + Dim w As Dictionary |
| 62 | + Set w = New Dictionary |
| 63 | + |
| 64 | + w.Add BEG_SUB, 1 |
| 65 | + w.Add END_SUB, -1 |
| 66 | + w.Add BEG_PB_SUB, 1 |
| 67 | + w.Add BEG_PV_SUB, 1 |
| 68 | + |
| 69 | + w.Add BEG_FUN, 1 |
| 70 | + w.Add END_FUN, -1 |
| 71 | + w.Add BEG_PB_FUN, 1 |
| 72 | + w.Add BEG_PV_FUN, 1 |
| 73 | + |
| 74 | + w.Add BEG_PROP, 1 |
| 75 | + w.Add END_PROP, -1 |
| 76 | + w.Add BEG_PB_PROP, 1 |
| 77 | + w.Add BEG_PV_PROP, 1 |
| 78 | + |
| 79 | + w.Add BEG_IF, 1 |
| 80 | + w.Add END_IF, -1 |
| 81 | + w.Add BEG_WITH, 1 |
| 82 | + w.Add END_WITH, -1 |
| 83 | + |
| 84 | + w.Add BEG_FOR, 1 |
| 85 | + w.Add END_FOR, -1 |
| 86 | + w.Add BEG_DOWHILE, 1 |
| 87 | + w.Add BEG_DOUNTIL, 1 |
| 88 | + |
| 89 | + w.Add BEG_TYPE, 1 |
| 90 | + w.Add END_TYPE, -1 |
| 91 | + w.Add BEG_PB_TYPE, 1 |
| 92 | + w.Add BEG_PV_TYPE, 1 |
| 93 | + |
| 94 | + Set words = w |
| 95 | +End Sub |
| 96 | + |
| 97 | + |
| 98 | +Private Property Get vbaWords() As Dictionary |
| 99 | + If words Is Nothing Then |
| 100 | + initialize |
| 101 | + End If |
| 102 | + Set vbaWords = words |
| 103 | +End Property |
| 104 | + |
| 105 | +Public Sub format() |
| 106 | + 'Debug.Print Application.VBE.ActiveCodePane.codeModule.Parent.Name |
| 107 | + 'Debug.Print Application.VBE.ActiveWindow.caption |
| 108 | + formatCode Application.VBE.ActiveCodePane.codeModule |
| 109 | + Debug.Print "format" |
| 110 | +End Sub |
| 111 | + |
| 112 | +Public Sub testFormatting() |
| 113 | + If words Is Nothing Then |
| 114 | + initialize |
| 115 | + End If |
| 116 | + |
| 117 | + Dim projName As String, moduleName As String |
| 118 | + projName = "vbaDeveloper" |
| 119 | + moduleName = "Test2" |
| 120 | + Dim vbaProject As VBProject |
| 121 | + Set vbaProject = Application.VBE.VBProjects(projName) |
| 122 | + Dim code As codeModule |
| 123 | + Set code = vbaProject.VBComponents(moduleName).codeModule |
| 124 | + |
| 125 | + 'removeIndentation code |
| 126 | + formatCode code |
| 127 | +End Sub |
| 128 | + |
| 129 | +Public Sub formatCode(codeModule As codeModule) |
| 130 | + On Error GoTo formatCodeError |
| 131 | + Dim lineCount As Integer |
| 132 | + lineCount = codeModule.CountOfLines |
| 133 | + |
| 134 | + Dim indentLevel As Integer, nextLevel As Integer, levelChange As Integer |
| 135 | + indentLevel = 0 |
| 136 | + Dim lineNr As Integer |
| 137 | + For lineNr = 1 To lineCount |
| 138 | + Dim line As String |
| 139 | + line = Trim(codeModule.Lines(lineNr, 1)) |
| 140 | + If Not line = "" Then |
| 141 | + If isEqual(ONEWORD_ELSE, line) Or lineStartsWith(BEG_END_ELSEIF, line) Then |
| 142 | + levelChange = 1 |
| 143 | + indentLevel = -1 + indentLevel |
| 144 | + ElseIf isLabel(line) Then |
| 145 | + levelChange = indentLevel |
| 146 | + indentLevel = 0 |
| 147 | + ElseIf isEqual(ONEWORD_END_FOR, line) Or isEqual(ONEWORD_END_LOOP, line) Then |
| 148 | + levelChange = -1 |
| 149 | + Else |
| 150 | + levelChange = indentChange(line) |
| 151 | + End If |
| 152 | + |
| 153 | + nextLevel = indentLevel + levelChange |
| 154 | + If levelChange = -1 Then |
| 155 | + indentLevel = nextLevel |
| 156 | + End If |
| 157 | + |
| 158 | + line = indentation(indentLevel) + line |
| 159 | + indentLevel = nextLevel |
| 160 | + End If |
| 161 | + Call codeModule.ReplaceLine(lineNr, line) |
| 162 | + Next |
| 163 | + Exit Sub |
| 164 | +formatCodeError: |
| 165 | + Debug.Print "Error while formatting " & codeModule.Parent.name |
| 166 | + Debug.Print Err.Number & " " & Err.Description |
| 167 | + Debug.Print " on line " & lineNr & ": " & line |
| 168 | + Debug.Print "indentLevel: " & indentLevel & " , levelChange: " & levelChange |
| 169 | +End Sub |
| 170 | + |
| 171 | + |
| 172 | +Public Sub removeIndentation(codeModule As codeModule) |
| 173 | + Dim lineCount As Integer |
| 174 | + lineCount = codeModule.CountOfLines |
| 175 | + |
| 176 | + Dim lineNr As Integer |
| 177 | + For lineNr = 1 To lineCount |
| 178 | + Dim line As String |
| 179 | + line = codeModule.Lines(lineNr, 1) |
| 180 | + line = Trim(line) |
| 181 | + Call codeModule.ReplaceLine(lineNr, line) |
| 182 | + Next |
| 183 | +End Sub |
| 184 | + |
| 185 | +Private Function indentChange(ByVal line As String) As Integer |
| 186 | + indentChange = 0 |
| 187 | + Dim w As Dictionary |
| 188 | + Set w = vbaWords |
| 189 | + |
| 190 | + If isEqual(line, ONEWORD_END_FOR) Or isEqual(line, ONEWORD_END_LOOP) Then |
| 191 | + indentChange = -1 'vbaWords(ONEWORD_END_FOR) |
| 192 | + End If |
| 193 | + Dim word As String |
| 194 | + Dim vord As Variant |
| 195 | + For Each vord In w.Keys |
| 196 | + word = vord |
| 197 | + If lineStartsWith(word, line) Then |
| 198 | + indentChange = vbaWords(word) |
| 199 | + GoTo hell |
| 200 | + End If |
| 201 | + Next |
| 202 | +hell: |
| 203 | +End Function |
| 204 | + |
| 205 | +' Returns true if both strings are equal, ignoring case |
| 206 | +Private Function isEqual(first As String, second As String) As Boolean |
| 207 | + isEqual = (StrComp(first, second, vbTextCompare) = 0) |
| 208 | +End Function |
| 209 | + |
| 210 | +' Returns True if strToCheck begins with begin, ignoring case |
| 211 | +Private Function lineStartsWith(begin As String, strToCheck As String) As Boolean |
| 212 | + lineStartsWith = False |
| 213 | + Dim beginLength As Integer |
| 214 | + beginLength = Len(begin) |
| 215 | + If Len(strToCheck) >= beginLength Then |
| 216 | + lineStartsWith = isEqual(begin, left(strToCheck, beginLength)) |
| 217 | + End If |
| 218 | +End Function |
| 219 | + |
| 220 | + |
| 221 | +Private Function isLabel(line As String) As Boolean |
| 222 | + isLabel = (right(line, 1) = ":") |
| 223 | +End Function |
0 commit comments