VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
Persistable = 0 'NotPersistable
DataBindingBehavior = 0 'vbNone
DataSourceBehavior = 0 'vbNone
MTSTransactionMode = 0 'NotAnMTSObject
END
Attribute VB_Name = "CPretty"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Attribute VB_Ext_KEY = "RVB_UniqueId" ,"379617B10166"
'----------------------
'Provides a filter through which lines can be pretty printed.
'Create one of these then pass text into the pretty method.
'Copy the output of pretty to a string. After the last call to
'pretty call flush to get any pending text. If the text passed to
'pretty was a complete block then flush should return nothing.
'----------------------
Option Explicit
'--Private Module Constants
'-- State variables
Private mlIndent As Long
Private msLastLine As String
Private moLines As Collection
Private moPoint As CPoint
Private msCurrentLine As String
Private msCurrentChar As String
Private mbALLOWSINGLELINEIF As Boolean
Private moExcursions As Collection
'-- End state variables
'-- Types
Private Type tExcursion
lLineNumber As Long
lColumnNumber As Long
End Type
';--------------------------
';Usage...: Set number of spaces to be added or subtracted for each indent level
';--------------------------
Public Property Get INDENT() As Long
INDENT = mlIndent
End Property
Public Property Let INDENT(ByVal vlValue As Long)
mlIndent = vlValue
End Property
';--------------------------
';Usage...: True if single line if statements are to be recognized
';--------------------------
Public Property Get ALLOWSINGLELINEIF() As Boolean
ALLOWSINGLELINEIF = mbALLOWSINGLELINEIF
End Property
Public Property Let ALLOWSINGLELINEIF(ByVal vbValue As Boolean)
mbALLOWSINGLELINEIF = vbValue
End Property
';----------------------
';Notes...: Feed lines into this and get indented lines back. Modelled on
'; visual-basic-mode.el.
';----------------------
Public Function Prettify(ByVal vsLine As String) As String
Dim sLine As String
moLines.Add vsLine
CURRENTLINENUMBER = moLines.Count
CURRENTCOLUMNNUMBER = 1
sLine = xIndentLine
With moLines
.Remove .Count
.Add sLine
End With
msLastLine = sLine
Prettify = sLine
End Function
';----------------------
';Usage...: Classify lines by regular expression
';----------------------
Private Function xLookingAt(ByRef roRegexp As RegExp) As Boolean
xLookingAt = roRegexp.Test(msCurrentLine)
End Function
';----------------------
'Feed lines into this and get indented lines back. Modelled on
'; visual-basic-mode.el. Relies on the state of previous lines.
';----------------------
Private Function xIndentLine() As String
xIndentLine = xIndentToColumn(xCalcIndent)
End Function
';----------------------
'Feed lines into this and get indented lines back. Modelled on
'; visual-basic-mode.el.
'----------------------
Private Function xIndentToColumn(ByVal vlIndent As Long) As String
xIndentToColumn = Space$(vlIndent) & Trim$(msCurrentLine)
End Function
';----------------------
'Calculate the indent for this line based on it and the previous
'; line (or lines).
'; VB does not short circuit so we split into separate if..then
'; clauses what is a big OR statement in the elisp on which this is
'; based.
'----------------------
Private Function xCalcIndent() As Long
Dim lIndent As Long
Dim oOriginalPoint As CPoint
Set oOriginalPoint = moPoint.Copy
xSaveExcursion
If PDefunStartRE.Test(msCurrentLine) Or PLabelRE.Test(msCurrentLine) _
Or PDefunEndRE.Test(msCurrentLine) Then
xCalcIndent = 0
ElseIf PElseRE.Test(msCurrentLine) Or PEndIfRE.Test(msCurrentLine) Then
xCalcIndent = xIndentOf(xFindMatchingIf)
ElseIf PNextRE.Test(msCurrentLine) Then
xCalcIndent = xIndentOf(xFindMatchingFor)
ElseIf PLoopRE.Test(msCurrentLine) Then
xCalcIndent = xIndentOf(xFindMatchingDo)
ElseIf PWendRE.Test(msCurrentLine) Then
xCalcIndent = xIndentOf(xFindMatchingWhile)
ElseIf PEndWithRE.Test(msCurrentLine) Then
xCalcIndent = xIndentOf(xFindMatchingWith)
ElseIf PSelectEndRE.Test(msCurrentLine) Then
xCalcIndent = xIndentOf(xFindMatchingSelect)
ElseIf PCaseRE.Test(msCurrentLine) Then
xCalcIndent = INDENT + xIndentOf(xFindMatchingSelect)
ElseIf PEndBeginRE.Test(msCurrentLine) Then
xCalcIndent = xIndentOf(xFindMatchingBegin)
Else
xPreviousLineOfCode
Do While xLookingAt(PLabelRE)
xPreviousLineOfCode
Loop
If xLookingAt(PContinuationRE) Then
'deal with continuation lines separately
xFindOriginalStatement
' Indent continuation line under matching open paren,
'or else one word in.
Dim oOrigStmt As CPoint
Set oOrigStmt = moPoint.Copy
Dim lMatchingOpenParen As Long
lMatchingOpenParen = -1
xSaveExcursion
xGotoChar oOriginalPoint
xBackwardUpList
';; Only if point is now w/in cont. block.
If (oOrigStmt.LE(moPoint)) Then
lMatchingOpenParen = moPoint.COLUMN
End If
xRestoreExcursion
If lMatchingOpenParen > 0 Then
xCalcIndent = lMatchingOpenParen
Else
' ;; Else, after first word on original line.
xBackToIndentation
xForwardWord
'''Do While xLookingAt(PWhiteSpaceCharRE)
Do While PWhiteSpaceCharRE.Test(msCurrentChar)
If Not xForwardChar Then
Exit Do
End If
Loop
xCalcIndent = CURRENTCOLUMNNUMBER - 1 ' subtract one because we want to know how many spaces to add not the column number of the first-non-space
End If
Else
xFindOriginalStatement
lIndent = xIndentOf(msCurrentLine)
If PDefunStartRE.Test(msCurrentLine) Then
xCalcIndent = lIndent + INDENT
ElseIf (PIfRE.Test(msCurrentLine) _
Or PElseRE.Test(msCurrentLine)) _
And (Not (ALLOWSINGLELINEIF _
And PIfThenRE.Test(msCurrentLine))) Then
xCalcIndent = lIndent + INDENT
ElseIf (PSelectRE.Test(msCurrentLine) _
Or PCaseRE.Test(msCurrentLine)) Then
xCalcIndent = lIndent + INDENT
ElseIf (PSelectRE.Test(msCurrentLine) _
Or PCaseRE.Test(msCurrentLine)) Then
xCalcIndent = lIndent + INDENT
ElseIf (PDoRE.Test(msCurrentLine)) Then
xCalcIndent = lIndent + INDENT
ElseIf (PForRE.Test(msCurrentLine)) Then
xCalcIndent = lIndent + INDENT
ElseIf (PWhileRE.Test(msCurrentLine)) Then
xCalcIndent = lIndent + INDENT
ElseIf (PWithRE.Test(msCurrentLine)) Then
xCalcIndent = lIndent + INDENT
ElseIf (PBeginRE.Test(msCurrentLine)) Then
xCalcIndent = lIndent + INDENT
Else
xCalcIndent = lIndent
End If
End If
End If
xRestoreExcursion
End Function
'----------------------
'Clear the collection - release all objects
'----------------------
Public Sub Clear()
Set moLines = New Collection
msLastLine = ""
Set moExcursions = New Collection
Set moPoint = New CPoint
End Sub
'======= Class Events =======
Private Sub Class_Initialize()
appCreateREs
Clear
End Sub
'======= Private Methods =======
';----------------------
'Parms...: roOpenRE = Regular expression matching opening bracket
'; roCloseRE = Regular expression matching closing bracket
';Returns.: string = line found
';Usage...: Use to find a matching indenting statement
';Notes...: Always returns a line even if that line is simply the first in
'; the list.
'----------------------
Private Function xFindMatchingStatement(ByRef roOpenRE As RegExp, _
ByRef roCloseRE As RegExp) As String
Dim lLevel As Long
lLevel = 0
Do While (CURRENTLINENUMBER > 1) And (lLevel >= 0)
xPreviousLineOfCode
xFindOriginalStatement
If roCloseRE.Test(msCurrentLine) Then
lLevel = lLevel + 1
ElseIf roOpenRE.Test(msCurrentLine) Then
lLevel = lLevel - 1
End If
Loop
xFindMatchingStatement = msCurrentLine
End Function
Private Function xFindMatchingIf() As String
xFindMatchingIf = xFindMatchingStatement(PIfRE, PEndIfRE)
End Function
Private Function xFindMatchingSelect() As String
xFindMatchingSelect = xFindMatchingStatement(PSelectRE, PSelectEndRE)
End Function
Private Function xFindMatchingFor() As String
xFindMatchingFor = xFindMatchingStatement(PForRE, PNextRE)
End Function
Private Function xFindMatchingDo() As String
xFindMatchingDo = xFindMatchingStatement(PDoRE, PLoopRE)
End Function
Private Function xFindMatchingWhile() As String
xFindMatchingWhile = xFindMatchingStatement(PWhileRE, PWendRE)
End Function
Private Function xFindMatchingWith() As String
xFindMatchingWith = xFindMatchingStatement(PWithRE, PEndWithRE)
End Function
Private Function xFindMatchingBegin() As String
Dim lOriginalLine As Long
lOriginalLine = CURRENTLINENUMBER
xFindMatchingBegin = xFindMatchingStatement(PBeginRE, PEndBeginRE)
If CURRENTLINENUMBER = 1 Then
CURRENTLINENUMBER = lOriginalLine
xFindMatchingBegin = xPreviousLineOfCode
End If
End Function
';----------------------
'Parms...: vsLine = string to be examined
'Find the indent of this line.
'----------------------
Private Function xIndentOf(ByVal vsLine As String) As Long
Dim lIndent As Long
lIndent = 1
Do While (Mid(vsLine, lIndent, 1) = " ")
lIndent = lIndent + 1
If (lIndent > Len(vsLine)) Then
Exit Do
End If
Loop
xIndentOf = lIndent - 1
End Function
';----------------------
'Move point to previous code line
'----------------------
Private Function xPreviousLineOfCode() As String
If CURRENTLINENUMBER > 1 Then
CURRENTLINENUMBER = CURRENTLINENUMBER - 1
Do While (CURRENTLINENUMBER > 1) _
And (xLookingAt(PBlankRE) Or (xLookingAt(PCommentRE)))
CURRENTLINENUMBER = CURRENTLINENUMBER - 1
Loop
End If
xPreviousLineOfCode = msCurrentLine
End Function
';----------------------
'Move point to start of multiline statement
'----------------------
Private Function xFindOriginalStatement() As String
Dim lHere As Long
lHere = CURRENTLINENUMBER
xPreviousLineOfCode
If ((CURRENTLINENUMBER > 1) Or Not xLookingAt(PContinuationRE)) Then
Do While ((CURRENTLINENUMBER > 1) And xLookingAt(PContinuationRE))
lHere = CURRENTLINENUMBER
xPreviousLineOfCode
Loop
CURRENTLINENUMBER = lHere
End If
End Function
Public Property Get CURRENTLINENUMBER() As Long
CURRENTLINENUMBER = moPoint.LINE
End Property
Public Property Let CURRENTLINENUMBER(ByVal vlValue As Long)
moPoint.LINE = vlValue
If vlValue > 0 Then
msCurrentLine = moLines(vlValue)
End If
End Property
Public Property Get CURRENTCOLUMNNUMBER() As Long
CURRENTCOLUMNNUMBER = moPoint.COLUMN
End Property
Public Property Let CURRENTCOLUMNNUMBER(ByVal vlValue As Long)
moPoint.COLUMN = vlValue
If vlValue < 1 Then
msCurrentChar = ""
Else
msCurrentChar = Mid(msCurrentLine, vlValue, 1)
End If
End Property
Private Function xSaveExcursion() As tExcursion
moExcursions.Add moPoint.Copy
End Function
Private Function xRestoreExcursion() As Boolean
With moExcursions
With .Item(.Count)
CURRENTLINENUMBER = .LINE
CURRENTCOLUMNNUMBER = .COLUMN
End With
.Remove .Count
End With
End Function
';----------------------
';Notes...: Do _not_ just copy the reference because this will make oPoint and moPoint the same thing.
';----------------------
Private Function xGotoChar(ByRef oPoint As CPoint) As Boolean
CURRENTLINENUMBER = oPoint.LINE
CURRENTCOLUMNNUMBER = oPoint.COLUMN
End Function
';----------------------
'Find beginning of bracketed expression
'Move backwards until an opening bracket is found.
'----------------------
Private Function xBackwardUpList() As Long
Dim lLevel As Long
Dim sBracket As String
lLevel = 0
Do
If xBackwardChar Then
sBracket = Mid(msCurrentLine, CURRENTCOLUMNNUMBER, 1)
If sBracket = "(" Then
lLevel = lLevel - 1
ElseIf sBracket = ")" Then
lLevel = lLevel + 1
End If
If lLevel < 0 Then
Exit Do
End If
Else
Exit Do
End If
Loop
End Function
';----------------------
'True if successful, false if reached start of buffer
'Find beginning of bracketed expression
'----------------------
Private Function xBackwardChar() As Boolean
xBackwardChar = True
If CURRENTCOLUMNNUMBER > 1 Then
CURRENTCOLUMNNUMBER = CURRENTCOLUMNNUMBER - 1
ElseIf CURRENTLINENUMBER > 1 Then
CURRENTLINENUMBER = CURRENTLINENUMBER - 1
If msCurrentLine = "" Then
msCurrentLine = " " ' quick fix for problems caused by zero length lines
End If
CURRENTCOLUMNNUMBER = Len(msCurrentLine)
Else
xBackwardChar = False
End If
End Function
';----------------------
'Move point to first non whitespace character on the line
'----------------------
Private Function xBackToIndentation() As Boolean
xBackToIndentation = True
CURRENTCOLUMNNUMBER = xIndentOf(msCurrentLine)
End Function
';----------------------
'Move point to next word
'Word is any alpha numeric
'----------------------
Private Function xForwardWord() As Boolean
xForwardWord = True
Do While Not PWordConstituentRE.Test(msCurrentChar)
If Not xForwardChar Then
Exit Do
End If
Loop
Do While PWordConstituentRE.Test(msCurrentChar)
If Not xForwardChar Then
Exit Do
End If
Loop
End Function
';----------------------
'True if successful
'Move point to next character
'----------------------
Private Function xForwardChar() As Boolean
xForwardChar = True
If CURRENTCOLUMNNUMBER < Len(msCurrentLine) Then
CURRENTCOLUMNNUMBER = CURRENTCOLUMNNUMBER + 1
ElseIf CURRENTLINENUMBER < moLines.Count Then
CURRENTLINENUMBER = CURRENTLINENUMBER + 1
CURRENTCOLUMNNUMBER = 1
Else
xForwardChar = False
End If
End Function