Link to home
Start Free TrialLog in
Avatar of thenelson
thenelson

asked on

find and move curser to code in VBE

I would like a piece of code to find code in the VBE and move to it, simular to the manual Ctl-F function.  I am familiar with the find method:
 Dim mdl As Module
 Set mdl = Modules(strModuleName)
mdl.Find(strSearchText, lngSLine, lngSCol, lngELine, lngECol)

but this only works within one module.

I suppose I could use:
For Each mdl In Modules
    If mdl.Find(strSearchText, lngSLine, lngSCol, lngELine, lngECol) Then  <move to there>
Next mdl

But then how do I move to there?
Avatar of Werokh
Werokh

Forgive my ignorance by why on earth would you want to do that? Are you trying to automate code replacement?

Code manipulation via code always scares me. :-)
Avatar of Jim Horn
To expand on Werokh's already-correct answer, huh?  IMHO, programming code shouldn't be built to edit itself at runtime.

If you're looking for some tools to help you develop/maintain your code, there's lots out there without having to build it yourself.  Rick Fisher's 'Find and Replace' utility at http://www.rickworld.com, and Speed Ferret at http://www.moshannon.com come to mind.
Avatar of thenelson

ASKER

Not modify code.  I have a error log routine that stores module, prcedure, line number and other stuff.  Being lazy, I would like to click on the error displayed in the form and have code jump me there.
The technique I use is to have a centralised error handler.  Generally my apps have two modes i.e. prodcution and development.
In development mode the error handler gives the option to return ErrRetry (after a STOP) which causes a resume statement to execute in the calling procedure.  This then means it only takes a few F8s to get to the offending line
ASKER CERTIFIED SOLUTION
Avatar of stevbe
stevbe

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
steveb

Cool function dude!  I haven't tested it but it appears to be right on.
Steve,

I agree with Arji.  It's a beautiful piece of code but it is not jumping to the correct module.  I tried it with your original code too.   I'm working on that now.  This is how I changed it so far (I still don't have it jumping to the correct module):

Public Function CodeFind(FindString As String)

Dim vbc As VBComponent
Dim cm As CodeModule

For Each vbc In VBE.VBProjects(1).VBComponents
Dim startline As Long, startcol As Long, endline As Long, endcol As Long
    Set cm = vbc.CodeModule
    startline = 1
    startcol = 1
    endline = -1   'per vba help if endline = -1 search to last line
    endcol = -1    'per vba help if endline = -1 search to end of line
    If cm.Find(FindString, startline, startcol, endline, endcol) = True Then    'return values where found
        Debug.Print vbc.Name, VBE.CodePanes.Count
        vbc.Activate
        DoCmd.RunCommand acCmdViewCode
        'per VBA help startline...endcol should highlight the selection.  Not sure how to use the codepane Find found
        'thinking ActiveCodePane might do it
        Application.VBE.ActiveCodePane.SetSelection startline, startcol, endline, endcol  
        Exit For
    End If
Next

End Function
I think I got it.  It jumps to the code (but doesn't highlight the selection).  None the less, Steve, I wish I had more than 500 points to give you!!

Public Function CodeFind(FindString As String)

Dim vbc As VBComponent
Dim cm As CodeModule

For Each vbc In VBE.VBProjects(1).VBComponents
Dim startline As Long, startcol As Long, endline As Long, endcol As Long
    Set cm = vbc.CodeModule
    startline = 1
    startcol = 1
    endline = -1
    endcol = -1
    If cm.Find(FindString, startline, startcol, endline, endcol) = True Then
        Debug.Print vbc.Name, VBE.CodePanes.Count
        vbc.CodeModule.CodePane.Show
        vbc.CodeModule.CodePane.SetSelection startline, startcol, endline, endcol
        Exit For
    End If
Next

End Function
ok ... seems like when you execute from the immediate window trhe focus in not yet on the actual code window so although the selection is actaully made you don't see it. Test this ... run the Test function from the immediate window... replace "C:\test2.txt"  with something from your database code ...


Option Compare Database
Option Explicit

Public Function Test()
    CodeFind ("C:\test2.txt")
End Function

Public Function CodeFind(FindString As String)

Dim vbc As VBComponent
Dim cm As CodeModule
Dim startline As Long
Dim startcol As Long
Dim endline As Long
Dim endcol As Long

For Each vbc In VBE.VBProjects(1).VBComponents

    Set cm = vbc.CodeModule
    startline = 1
    startcol = 1
    endline = -1   'per vba help if endline = -1 search to last line
    endcol = -1    'per vba help if endline = -1 search to end of line
    If cm.Find(FindString, startline, startcol, endline, endcol) = True Then
        'return values where found
        Debug.Print vbc.name
        vbc.Activate
        cm.CodePane.Show
        'per VBA help startline...endcol should highlight the selection.
        'Not sure how to use the codepane Find found
        'thinking ActiveCodePane might do it
        Application.VBE.ActiveCodePane.SetSelection startline, startcol, endline, endcol
        Exit For
    End If
Next

End Function

Steve
Natchiket,

I am really curious about how you do:
"In development mode the error handler gives the option to return ErrRetry (after a STOP) which causes a resume statement to execute in the calling procedure.  This then means it only takes a few F8s to get to the offending line"

I have created a question for this at:
https://www.experts-exchange.com/questions/21472284/Error-handler-to-jump-to-offending-line-Question-for-Natchiket.html
to see it work properly you need to NOT run this from the immediate window in any way shape or form ... if you do then click on any border around the actual code window ... you will see the correct code selected. You can put the cursor inside the TEST function and then press F5 ... this works :-)

The problem with using the immediate window is that when the function is done it returns to qwhere it started ... the immeiate window so you code window does not and can not have the focus...

Steve
closer and closer ...

change CoeeFind to a function that returns a boolean if found now make a form with a textbox that you enter or even have bound to your error log ... and a cmdCodeFind button ...

Private Sub cmdFindCode_Click()
    If CodeFind(Me.txtCode.Value) = True Then
        MsgBox "Found, switch tio IDE press Alt+F11"
    Else
        MsgBox "code not found, fix you err log silly"
    End If
End Sub
Steve,

We're moving in the same direction.

After reading your post, I changed my code from:
        vbc.CodeModule.CodePane.Show
        vbc.CodeModule.CodePane.SetSelection startline, startcol, endline, endcol
to:
        cm.CodePane.Show
        cm.CodePane.SetSelection startline, startcol, endline, endcol

Works great!  Now icing on the cake would be to hightlight the selection.
... this is messy but works ... change your form code to ...


Private Sub cmdFindCode_Click()
    If CodeFind(Me.txtCode.Value) = True Then
        On Error Resume Next
        DoCmd.RunCommand acCmdViewCode
    Else
        MsgBox "code not found, fix your err log !!!"
    End If
End Sub

Steve
Private Sub cmdFindCode_Click()
    If CodeFind(Me.txtCode.Value) = True Then
        On Error Resume Next
        DoCmd.RunCommand acCmdViewCode
    Else
        MsgBox "code not found, fix your err log !!!"
    End If
End Sub

Doesn't work for me.  Is that to highlight the selection?
yup ... opens my code window if closed and the code is selected. Works for standard modules, form modules, class modules ... works for them all.

I am using 2003, not sure of the results for any other version. maybe we can get some other people to test?


Steve
I have found that "cm.CodePane.SetSelection startline, startcol, endline, endcol" highlights the code if I don't call the function from the immediate window.  I figured this out before I read your " to see it work properly you need to NOT run this from the immediate window in any way shape or form".  If I wasn't so busy coding, I would have read your post sooner and saved myself some time.

I am still expanding on the code but am hung up on searching for a procedure name.  I've got:
For Each vbc In VBE.VBProjects(1).VBComponents
   If InStr(vbc.Name, FindString1) > 0 Then...

to search through module names without worrying about looking for modules, class modules, form or report objects.

Now I would like to search through procedures without worrying about subs, functions, properies, etc.

The reason is I have line numbers on all my lines and I store the module, procedure and line numbers of errors.  If I can drill down to the module> procedure, I can find the unique line number.
Can you give me an example of what your error log stores and what that really looks like in your code?
Sound like you log very specific info that you could pass as args to CodeFind.

Public Function CodeFind(ModuleName As String, ProcName As String) As Boolean

Dim vbc As VBComponent
Dim cm As CodeModule
Dim lngSL As Long
Dim lngSC As Long
Dim lngEL As Long
Dim lngEC As Long

For Each vbc In VBE.VBProjects(1).VBComponents
    If vbc.name = ModuleName Then
        Set cm = vbc.CodeModule
        lngSL = 1
        lngSC = 1
        lngEL = -1   'per vba help if lngEL = -1 search to last line
        lngEC = -1    'per vba help if lngEL = -1 search to end of line
        If cm.Find(ProcName, lngSL, lngSC, lngEL, lngEC) = True Then
            CodeFind = True
            vbc.Activate
            cm.CodePane.Show
            cm.CodePane.SetSelection lngSL, lngSC, lngEL, lngEC
            Exit For
        End If
    End If
Next

hmmm ... if your log tracks which line threw the error them maybe we can figure out how to get to that specific line ...
This is what I store in my Error Log table:
            Name                          Type                Size
            ErrorID                       Long Integer          4
            ErrDateTime                Date/Time            8
            ErrNumber                  Long Integer         4
            ErrDescription             Text                  255
            ModuleName               Text                   40
            ProcedureName           Text                   40
            ErrLineNumber            Text                     5
            Person                        Text                    2
            ComputerName           Text                   30
            RecordRef                   Text                   20

I have two routines that jump to the line number.  Here is the calling routine:
Private Sub ErrLineNumber_DblClick(Cancel As Integer)
If CodeFind([ModuleName], [ProcedureName], [ErrLineNumber], -1) = False Then _
    MsgBox "Code not found!", vbExclamation
End Sub

Here is the called function.  I am working on making it more unversal. I am working to make it more universal so that FindModule, FindProc, and FindString are all optional and type of search will look for a line number if -1, a string if 0 and find next if > 0 (the last line number).
Public Function CodeFind( _
FindModule As String, _
Optional FindProc As String = "", _
Optional FindString As String = "", _
Optional TypeOfSearch As Long = 0 _
) As Boolean

Dim vbc As VBComponent
Dim cm As CodeModule
Dim startline As Long, startcol As Long, endline As Long, endcol As Long
Dim lngFake As Long, ProcFound As Boolean

For Each vbc In VBE.VBProjects(1).VBComponents
    CodeFind = True
    Set cm = vbc.CodeModule
    startline = 1
    startcol = 1
    endline = -1
    endcol = -1
    If FindProc = "" Then
        'FindModule is a string to search
        If cm.Find(FindModule, startline, startcol, endline, endcol) = False Then
            CodeFind = False
            GoTo Nextvbc
        End If
    Else
        'FindModule is VBComponent
        If InStr(vbc.Name, FindModule) = 0 Then
            GoTo Nextvbc
        Else
            'VBComponent found
            ProcFound = False
            Do While cm.Find(FindProc, startline, startcol, endline, endcol) = True
                If InStr(cm.ProcOfLine(startline, lngFake), FindProc) Then
                    ProcFound = True
                    Exit Do
                End If
                startcol = endcol
            Loop
            If ProcFound = False Then
                CodeFind = False
                Exit Function
            ElseIf FindString <> "" Then
                If TypeOfSearch > 0 Then startline = TypeOfSearch
                startcol = 1
                endline = -1
                endcol = -1
                Do
                    If cm.Find(FindString, startline, startcol, endline, endcol) = False Then
                        CodeFind = False
                        Exit Function
                    End If
                    If TypeOfSearch >= 0 Then Exit Do
                    If startcol = 1 Then Exit Do
                    startcol = endcol
                Loop
            End If
        End If
    End If
    cm.CodePane.Show
    cm.CodePane.SetSelection startline, startcol, endline, endcol
    Exit For
Nextvbc:
Next vbc

End Function
Took me awhile but I came up with a universal routine to find a module, procedure and/or string in code:


'---------------------------------------------------------------------------------------
' Procedure : CodeFind
' DateTime  : 7/5/2005 18:32
' Author    : Nelson Hochberg
' Purpose   : Find a module, a procedure and/or a string in code and highlight it
' Returns   : 0 if not found,  line number in module if found
' Syntax    : lngReturn = CodeFind ([FindMod],[FindProc],[FindStr],[TypeOfSearch])
' Arguments : Optional FindMod As String: Part of a name of a module
'             Optional FindProc As String: Part of a name of a procedure
'             Optional FindStr As String: Part of a string to search
'             NOTE: One of the above three is required
'             Optional TypeOfSearch As Long: -1 Find line number, 0 Find string,
'                      >0 Continue search starting at line number: TypeOfSearch + 1
' Thanks    : To stevbe at Experts Exchange for the initial code.
'---------------------------------------------------------------------------------------
'
Public Function CodeFind( _
Optional FindMod As String = "", _
Optional FindProc As String = "", _
Optional FindStr As String = "", _
Optional TypeOfSearch As Long = 0 _
) As Long

Dim vbc As VBComponent
Dim cm As CodeModule
Dim startline As Long, startcol As Long, endline As Long, endcol As Long

If FindMod <> "" Then
    CodeFind = FindModule(FindMod, vbc, cm)
        If CodeFind = False Then Exit Function
    If FindProc <> "" Then
        CodeFind = FindProcedure(FindProc, startline, startcol, endline, endcol, cm)
            If CodeFind = False Then Exit Function
        If FindStr <> "" Then
            CodeFind = FindString(FindStr, startline, startcol, endline, endcol, cm, TypeOfSearch)
                If CodeFind = False Then Exit Function
        Else
            GoTo CodeLineFound
        End If
    Else
        startline = 1
        If FindStr <> "" Then
            CodeFind = FindString(FindStr, startline, startcol, endline, endcol, cm, TypeOfSearch)
            If CodeFind = False Then Exit Function
        Else
            GoTo CodeLineFound
        End If
    End If
Else
    For Each vbc In VBE.VBProjects(1).VBComponents
        Set cm = vbc.CodeModule
        If FindProc <> "" Then
            CodeFind = FindProcedure(FindProc, startline, startcol, endline, endcol, cm)
            If CodeFind = False Then GoTo Nextvbc2 Else Exit For
        Else
            startline = 1
            If FindStr <> "" Then
                CodeFind = FindString(FindStr, startline, startcol, endline, endcol, cm, TypeOfSearch)
                    If CodeFind = False Then GoTo Nextvbc2 Else Exit For
            Else
                MsgBox "CodeFind: At least one of the following is required:" & vbCrLf & _
                    "    Module" & vbCrLf & "    Procedure" & vbCrLf & "    String"
                CodeFind = False
                Exit Function
            End If
        End If
Nextvbc2:
    Next vbc
    If CodeFind <> False Then
        If FindStr <> "" Then
            CodeFind = FindString(FindStr, startline, startcol, endline, endcol, cm, TypeOfSearch)
            If CodeFind = False Then Exit Function
        Else
            GoTo CodeLineFound
        End If
    End If
End If

CodeLineFound:
If CodeFind <> False Then
    If endline = -1 Then endline = 1
    If endcol = -1 Then endcol = 1
    cm.CodePane.Show
    cm.CodePane.SetSelection startline, startcol, endline, endcol
End If

End Function

Private Function FindModule( _
FindMod As String, _
vbc As VBComponent, _
cm As CodeModule _
) As Long

FindModule = False
For Each vbc In VBE.VBProjects(1).VBComponents
    'FindMod is VBComponent
    If InStr(vbc.Name, FindMod) > 0 Then
        Set cm = vbc.CodeModule
        FindModule = 1
        Exit For
    End If
Next vbc
End Function

Private Function FindProcedure( _
FindProc As String, _
startline As Long, _
startcol As Long, _
endline As Long, _
endcol As Long, _
cm As CodeModule _
) As Long

Dim lngFake As Long

startline = 1
startcol = 1

If FindProc <> "" Then
    'search for procedure
    FindProcedure = False
   
    Do
        endline = -1
        endcol = -1
        If cm.Find(FindProc, startline, startcol, endline, endcol) = False Then Exit Do
        If InStr(cm.ProcOfLine(startline, lngFake), FindProc) Then
            FindProcedure = startline
            Exit Do
        End If
        startcol = endcol
    Loop
End If
End Function

Private Function FindString( _
FindStr As String, _
startline As Long, _
startcol As Long, _
endline As Long, _
endcol As Long, _
cm As CodeModule, _
TypeOfSearch As Long _
) As Long

If FindStr <> "" Then
    If TypeOfSearch > 0 Then startline = TypeOfSearch
    startcol = 1
    endline = -1
    endcol = -1
    Do
        If cm.Find(FindStr, startline, startcol, endline, endcol) = False Then
            FindString = False
            Exit Function
        End If
        If TypeOfSearch >= 0 Then Exit Do
        If startcol = 1 Then Exit Do
        startcol = endcol
    Loop
End If
FindString = endline
End Function