VBCommons/dev/DevTools.bas
2024-06-07 20:46:40 +03:00

195 lines
6.0 KiB
QBasic

Attribute VB_Name = "DevTools"
'================ Developer Tools =============
' Shared module version: 20220811
' Depends on: ex_WinAPI
' Required reference: Scripting, VBIDE
Option Private Module
Option Explicit
Private Const TEST_SUITE_MASK = "s_*"
Private Const TEST_NAME_MASK = "t_*"
Private Const COMPILATION_DELAY As Long = 250 ' ms
Public Function CompileVBProject(target As VBIDE.VBProject) As Boolean
Const CONTROL_ID_COMPILE = 578
Const MSGBOX_CLASS_ID As String = "#32770"
Const MSGBOX_NAME As String = "Microsoft Visual Basic for Applications"
Const WINDOW_MESSAGE_CLOSE As Long = &H10
target.VBE.MainWindow.Visible = True
With target.VBComponents(1).CodeModule
Call .CodePane.Show
Call .InsertLines(1, "123")
Call .DeleteLines(1, 1)
End With
Dim btnCompile As CommandBarControl: Set btnCompile = target.VBE.CommandBars.FindControl(ID:=CONTROL_ID_COMPILE)
If Not btnCompile.Enabled Then
CompileVBProject = False
Exit Function
End If
Call btnCompile.Execute
Call Sleep(COMPILATION_DELAY)
Dim nHwnd&: nHwnd = FindWindow(MSGBOX_CLASS_ID, MSGBOX_NAME)
CompileVBProject = nHwnd = 0
If Not CompileVBProject Then _
Call PostMessage(nHwnd, WINDOW_MESSAGE_CLOSE, 0&, 0&)
End Function
Public Function UpdateVersionStamp(target As VBIDE.VBProject, sVersion$) As Boolean
UpdateVersionStamp = False
On Error Resume Next
Dim iModule As VBComponent: Set iModule = target.VBComponents("Main")
If iModule Is Nothing Then _
Exit Function
Dim iCode As CodeModule: Set iCode = iModule.CodeModule
Dim nLineStart&: nLineStart = 1
Dim nLineEnd&: nLineEnd = iCode.CountOfLines
Dim nColumnStart&: nColumnStart = 1
Dim nColumnEnd&: nColumnEnd = 255
If Not iCode.Find("Public Const PRODUCT_VERSION", nLineStart, nColumnStart, nLineEnd, nColumnEnd) Then _
Exit Function
Call iCode.ReplaceLine(nLineEnd, "Public Const PRODUCT_VERSION = """ & sVersion & """")
UpdateVersionStamp = True
End Function
Public Function PrepareSkeletonFor(target As Object)
On Error Resume Next
Call target.Application.Run("'" & target.Name & "'" & "!Dev_PrepareSkeleton")
End Function
Public Function ExtractProperties(sLines() As String, ByRef nCurrent&, ByRef oSink As Scripting.Dictionary)
ExtractProperties = False
Do While nCurrent <= UBound(sLines, 1)
Dim sLine$: sLine = sLines(nCurrent)
nCurrent = nCurrent + 1
If sLine Like "%%*" Then _
Exit Do
Dim nSeparator&: nSeparator = VBA.InStr(1, sLine, "=")
If nSeparator = 0 Then _
GoTo NEXT_LINE
If VBA.Left(sLine, 1) = "#" Then _
GoTo NEXT_LINE
Dim sKey$: sKey = VBA.Trim(VBA.Left(sLine, nSeparator - 1))
Dim sValue$: sValue = VBA.Trim(Right(sLine, VBA.Len(sLine) - nSeparator))
If oSink.Exists(sKey) Then _
Exit Function
Call oSink.Add(sKey, sValue)
NEXT_LINE:
Loop
ExtractProperties = True
End Function
Public Function Dev_CountPublicAPI(target As VBIDE.CodeModule) As Long
Dim nLine&
For nLine = 1 To target.CountOfLines Step 1
If InStr(1, target.Lines(nLine, 1), "Public") <> 0 Then _
Dev_CountPublicAPI = Dev_CountPublicAPI + 1
Next nLine
End Function
Public Function Dev_ScanFunctions(target As VBIDE.CodeModule) As Collection ' of CDS_InfoFunction
Dim cResult As New Collection
Dim currentFunc As CDS_InfoFunction
Dim lastFunc As New CDS_InfoFunction
Dim nLine&
For nLine = 1 To target.CountOfLines Step 1
Set currentFunc = ExtractProcedure(target, nLine)
If lastFunc.name_ <> currentFunc.name_ And currentFunc.name_ <> vbNullString Then
currentFunc.isPublic_ = IsPublicFunc(target, currentFunc)
Call cResult.Add(currentFunc)
End If
Set lastFunc = currentFunc
Next nLine
Set Dev_ScanFunctions = cResult
End Function
Public Function Dev_ScanTests(target As VBIDE.VBProject, ByRef cTestsDB As Scripting.Dictionary) As Long
Dim nCount&: nCount = 0
Dim aComponent As VBIDE.VBComponent
Dim iComponents As VBIDE.VBComponents: Set iComponents = target.VBComponents
Dim tests As Collection
For Each aComponent In iComponents
If aComponent.Type <> vbext_ct_ClassModule Then _
GoTo NEXT_COMPONENT
If Not aComponent.Name Like TEST_SUITE_MASK Then _
GoTo NEXT_COMPONENT
Set tests = New Collection
Dim funcs As Collection: Set funcs = Dev_ScanFunctions(aComponent.CodeModule)
Dim aFunc As CDS_InfoFunction
For Each aFunc In funcs
If aFunc.name_ Like TEST_NAME_MASK And aFunc.isPublic_ Then _
Call tests.Add(aFunc.name_)
Next aFunc
If tests.Count > 0 Then
Call cTestsDB.Add(aComponent.Name, tests)
nCount = nCount + tests.Count
End If
NEXT_COMPONENT:
Next aComponent
Set iComponents = Nothing
Set aComponent = Nothing
Dev_ScanTests = nCount
End Function
Public Function Dev_RemoveDebugCode(target As VBIDE.VBProject)
Dim selectedComps As New Collection
Dim aComponent As VBIDE.VBComponent
For Each aComponent In target.VBComponents
If Dev_IsTestingModule(aComponent.Name) Then _
Call selectedComps.Add(aComponent)
Next aComponent
For Each aComponent In selectedComps
Call target.VBComponents.Remove(aComponent)
Next aComponent
End Function
Public Function Dev_IsTestingModule(sName$) As Boolean
Dev_IsTestingModule = True
If sName Like TEST_SUITE_MASK Then _
Exit Function
If sName Like "DevHelper*" Then _
Exit Function
If sName Like "DevTester*" Then _
Exit Function
If sName Like "DevTesterUI*" Then _
Exit Function
If sName Like "API_MockInteraction*" Then _
Exit Function
Dev_IsTestingModule = False
End Function
' =====
Private Function ExtractProcedure(target As VBIDE.CodeModule, nLine&) As CDS_InfoFunction
Set ExtractProcedure = New CDS_InfoFunction
ExtractProcedure.name_ = target.ProcOfLine(nLine, ExtractProcedure.type_)
End Function
Private Function IsPublicFunc(target As VBIDE.CodeModule, func As CDS_InfoFunction) As Boolean
Dim nStart&: nStart = target.ProcBodyLine(func.name_, func.type_)
Dim sTxt$: sTxt = target.Lines(nStart, 1)
IsPublicFunc = InStr(1, sTxt, "Private") = 0
End Function