【问题标题】:Excel VBA - How to Turn On Line Numbers in Code EditorExcel VBA - 如何在代码编辑器中打开行号
【发布时间】:2016-11-21 23:17:02
【问题描述】:

请帮助:如何在 Excel VBA 代码编辑器中打开行号?我使用的是 Excel 2013 版本。

谢谢。

【问题讨论】:

  • 据我所知你不能,除非你做一些代码肌肉改造
  • 如果不下载 MZ Tools 之类的东西(无论如何都值得),请不要以为你能做到。
  • 它已经在帮助下方的功能区上。当您在编辑器中移动光标时,它会显示您所在的行号。

标签: excel vba


【解决方案1】:

这是我在 VBE IDE 中添加行号的代码。这是 Excel MVP mikerickson 提供的here 解决方案的改进。我已经解决了这个问题,因为在我已经遇到的一些极少数情况下,VBE 无法进入调试模式,例如当您的代码中有 .ReplaceLine 方法时。事实上,一旦执行,您就无法进入调试模式,因此 Erl 可能对调试有用(而不是 Debug.Print)。我添加了几个功能,例如:

  • 可以将行号添加为标签:10: Dim foo as bar,也可以添加为通过制表符与代码分隔的单个数字:10 Dim foo as bar
  • 可以将行号添加到过程结束语句,并在编号后将过程声明行的缩进与其结束语句行匹配。或者不。
  • 是否可以在空行中添加行号
  • [WIP] 可以将行号添加到模块中的特定过程
  • [WIP] 将代码行的所有缩进与行号匹配以匹配最后一行缩进的缩进。如果最后一行是200: End Sub,则30: With ActiveSheet 行将重新缩进为30: ActiveSheet
  • [WIP] 添加 VBE IDE 命令以直接以当前模块/proc 作为参数进行调用
Public Enum vbLineNumbers_LabelTypes
    vbLabelColon    ' 0
    vbLabelTab      ' 1
End Enum

Public Enum vbLineNumbers_ScopeToAddLineNumbersTo
    vbScopeAllProc  ' 1
    vbScopeThisProc ' 2
End Enum

Sub AddLineNumbers(ByVal wbName As String, _
                   ByVal vbCompName As String, _
                   ByVal LabelType As vbLineNumbers_LabelTypes, _
                   ByVal AddLineNumbersToEmptyLines As Boolean, _
                   ByVal AddLineNumbersToEndOfProc As Boolean, _
                   ByVal Scope As vbLineNumbers_ScopeToAddLineNumbersTo, _
                   Optional ByVal thisProcName As String)

' USAGE RULES
' DO NOT MIX LABEL TYPES FOR LINE NUMBERS! IF ADDING LINE NUMBERS AS COLON TYPE, ANY LINE NUMBERS AS VBTAB TYPE MUST BE REMOVE BEFORE, AND RECIPROCALLY ADDING LINE NUMBERS AS VBTAB TYPE

    Dim i As Long
    Dim j As Long
    Dim procName As String
    Dim startOfProcedure As Long
    Dim lengthOfProcedure As Long
    Dim endOfProcedure As Long
    Dim strLine As String

    With Workbooks(wbName).VBProject.VBComponents(vbCompName).CodeModule
        .CodePane.Window.Visible = False

If Scope = vbScopeAllProc Then

        For i = 1 To .CountOfLines

            strLine = .Lines(i, 1)
            procName = .ProcOfLine(i, vbext_pk_Proc) ' Type d'argument ByRef incompatible ~~> Requires VBIDE library as a Reference for the VBA Project

            If procName <> vbNullString Then
                startOfProcedure = .ProcStartLine(procName, vbext_pk_Proc)
                bodyOfProcedure = .ProcBodyLine(procName, vbext_pk_Proc)
                countOfProcedure = .ProcCountLines(procName, vbext_pk_Proc)

                prelinesOfProcedure = bodyOfProcedure - startOfProcedure
                'postlineOfProcedure = ??? not directly available since endOfProcedure is itself not directly available.

                lengthOfProcedure = countOfProcedure - prelinesOfProcedure ' includes postlinesOfProcedure !
                'endOfProcedure = ??? not directly available, each line of the proc must be tested until the End statement is reached. See below.

                If endOfProcedure <> 0 And startOfProcedure < endOfProcedure And i > endOfProcedure Then
                    GoTo NextLine
                End If

                If i = bodyOfProcedure Then InProcBodyLines = True

                If bodyOfProcedure < i And i < startOfProcedure + countOfProcedure Then
                    If Not (.Lines(i - 1, 1) Like "* _") Then

                        InProcBodyLines = False

                        PreviousIndentAdded = 0

                        If Trim(strLine) = "" And Not AddLineNumbersToEmptyLines Then GoTo NextLine

                        If IsProcEndLine(wbName, vbCompName, i) Then
                            endOfProcedure = i
                            If AddLineNumbersToEndOfProc Then
                                Call IndentProcBodyLinesAsProcEndLine(wbName, vbCompName, LabelType, endOfProcedure)
                            Else
                                GoTo NextLine
                            End If
                        End If

                        If LabelType = vbLabelColon Then
                            If HasLabel(strLine, vbLabelColon) Then strLine = RemoveOneLineNumber(.Lines(i, 1), vbLabelColon)
                            If Not HasLabel(strLine, vbLabelColon) Then
                                temp_strLine = strLine
                                .ReplaceLine i, CStr(i) & ":" & strLine
                                new_strLine = .Lines(i, 1)
                                If Len(new_strLine) = Len(CStr(i) & ":" & temp_strLine) Then
                                    PreviousIndentAdded = Len(CStr(i) & ":")
                                Else
                                    PreviousIndentAdded = Len(CStr(i) & ": ")
                                End If
                            End If
                        ElseIf LabelType = vbLabelTab Then
                            If Not HasLabel(strLine, vbLabelTab) Then strLine = RemoveOneLineNumber(.Lines(i, 1), vbLabelTab)
                            If Not HasLabel(strLine, vbLabelColon) Then
                                temp_strLine = strLine
                                .ReplaceLine i, CStr(i) & vbTab & strLine
                                PreviousIndentAdded = Len(strLine) - Len(temp_strLine)
                            End If
                        End If

                    Else
                        If Not InProcBodyLines Then
                            If LabelType = vbLabelColon Then
                                .ReplaceLine i, Space(PreviousIndentAdded) & strLine
                            ElseIf LabelType = vbLabelTab Then
                                .ReplaceLine i, Space(4) & strLine
                            End If
                        Else
                        End If
                    End If

                End If

            End If

NextLine:
        Next i

ElseIf AddLineNumbersToEmptyLines And Scope = vbScopeThisProc Then

End If

        .CodePane.Window.Visible = True
    End With

End Sub

Function IsProcEndLine(ByVal wbName As String, _
                   ByVal vbCompName As String, _
                   ByVal Line As Long) As Boolean

With Workbooks(wbName).VBProject.VBComponents(vbCompName).CodeModule
    If Trim(.Lines(Line, 1)) Like "End Sub*" _
    Or Trim(.Lines(Line, 1)) Like "End Function*" _
    Or Trim(.Lines(Line, 1)) Like "End Property*" _
    Then IsProcEndLine = True
End With

End Function

Sub IndentProcBodyLinesAsProcEndLine(ByVal wbName As String, ByVal vbCompName As String, ByVal LabelType As vbLineNumbers_LabelTypes, ByVal ProcEndLine As Long)
    Dim procName As String
    Dim startOfProcedure As Long
    Dim endOfProcedure As Long

    With Workbooks(wbName).VBProject.VBComponents(vbCompName).CodeModule

        procName = .ProcOfLine(ProcEndLine, vbext_pk_Proc)
        bodyOfProcedure = .ProcBodyLine(procName, vbext_pk_Proc)
        endOfProcedure = ProcEndLine
        strEnd = .Lines(endOfProcedure, 1)

        j = bodyOfProcedure
        Do Until Not .Lines(j - 1, 1) Like "* _" And j <> bodyOfProcedure

            strLine = .Lines(j, 1)

            If LabelType = vbLabelColon Then
                If Mid(strEnd, Len(CStr(endOfProcedure)) + 1 + 1 + 1, 1) = " " Then
                    .ReplaceLine j, Space(Len(CStr(endOfProcedure)) + 1) & strLine
                Else
                    .ReplaceLine j, Space(Len(CStr(endOfProcedure)) + 2) & strLine
                End If
            ElseIf LabelType = vbLabelTab Then
                If endOfProcedure < 1000 Then
                    .ReplaceLine j, Space(4) & strLine
                Else
                    Debug.Print "This tool is limited to 999 lines of code to work properly."
                End If
            End If

            j = j + 1
        Loop

    End With
End Sub

Sub RemoveLineNumbers(ByVal wbName As String, ByVal vbCompName As String, ByVal LabelType As vbLineNumbers_LabelTypes)
    Dim i As Long
    With Workbooks(wbName).VBProject.VBComponents(vbCompName).CodeModule

        For i = 1 To .CountOfLines

            procName = .ProcOfLine(i, vbext_pk_Proc)

            If procName <> vbNullString Then

                If i = .ProcBodyLine(procName, vbext_pk_Proc) Then InProcBodyLines = True

                LenghtBefore = Len(.Lines(i, 1))
                If Not .Lines(i - 1, 1) Like "* _" Then
                    InProcBodyLines = False
                    .ReplaceLine i, RemoveOneLineNumber(.Lines(i, 1), LabelType)
                Else
                    If IsInProcBodyLines Then
                        ' do nothing
                    Else
                        .ReplaceLine i, Mid(.Lines(i, 1), RemovedChars_previous_i + 1)
                    End If
                End If
                LenghtAfter = Len(.Lines(i, 1))

                LengthBefore_previous_i = LenghtBefore
                LenghtAfter_previous_i = LenghtAfter
                RemovedChars_previous_i = LengthBefore_previous_i - LenghtAfter_previous_i

                If Trim(.Lines(i, 1)) Like "End Sub*" Or Trim(.Lines(i, 1)) Like "End Function" Or Trim(.Lines(i, 1)) Like "End Property" Then

                    LenOfRemovedLeadingCharacters = LenghtBefore - LenghtAfter

                    procName = .ProcOfLine(i, vbext_pk_Proc)
                    bodyOfProcedure = .ProcBodyLine(procName, vbext_pk_Proc)

                    j = bodyOfProcedure
                    strLineBodyOfProc = .Lines(bodyOfProcedure, 1)
                    Do Until Not strLineBodyOfProc Like "* _"
                        j = j + 1
                        strLineBodyOfProc = .Lines(j, 1)
                    Loop
                    LastLineBodyOfProc = j
                    strLastLineBodyOfProc = strLineBodyOfProc

                    strLineEndOfProc = .Lines(i, 1)
                    For k = bodyOfProcedure To j
                        .ReplaceLine k, Mid(.Lines(k, 1), 1 + LenOfRemovedLeadingCharacters)
                    Next k

                    i = i + (j - bodyOfProcedure)
                    GoTo NextLine

                End If
            Else
            ' GoTo NextLine
            End If
NextLine:
        Next i
    End With
End Sub

Function RemoveOneLineNumber(ByVal aString As String, ByVal LabelType As vbLineNumbers_LabelTypes)
    RemoveOneLineNumber = aString
    If LabelType = vbLabelColon Then
        If aString Like "#:*" Or aString Like "##:*" Or aString Like "###:*" Then
            RemoveOneLineNumber = Mid(aString, 1 + InStr(1, aString, ":", vbTextCompare))
            If Left(RemoveOneLineNumber, 2) Like " [! ]*" Then RemoveOneLineNumber = Mid(RemoveOneLineNumber, 2)
        End If
    ElseIf LabelType = vbLabelTab Then
        If aString Like "#   *" Or aString Like "##  *" Or aString Like "### *" Then RemoveOneLineNumber = Mid(aString, 5)
        If aString Like "#" Or aString Like "##" Or aString Like "###" Then RemoveOneLineNumber = ""
    End If
End Function

Function HasLabel(ByVal aString As String, ByVal LabelType As vbLineNumbers_LabelTypes) As Boolean
    If LabelType = vbLabelColon Then HasLabel = InStr(1, aString & ":", ":") < InStr(1, aString & " ", " ")
    If LabelType = vbLabelTab Then
        HasLabel = Mid(aString, 1, 4) Like "#   " Or Mid(aString, 1, 4) Like "##  " Or Mid(aString, 1, 4) Like "### "
    End If
End Function

Function RemoveLeadingSpaces(ByVal aString As String) As String
    Do Until Left(aString, 1) <> " "
        aString = Mid(aString, 2)
    Loop
    RemoveLeadingSpaces = aString
End Function

Function WhatIsLineIndent(ByVal aString As String) As String
    i = 1
    Do Until Mid(aString, i, 1) <> " "
        i = i + 1
    Loop
    WhatIsLineIndent = i
End Function

Function HowManyLeadingSpaces(ByVal aString As String) As String
    HowManyLeadingSpaces = WhatIsLineIndent(aString) - 1
End Function

你可以这样打电话:

Sub AddLineNumbers_vbLabelColon()
    AddLineNumbers wbName:="EvaluateCall.xlsm", vbCompName:="ModLineNumbers_testDest", LabelType:=vbLabelColon, AddLineNumbersToEmptyLines:=True, AddLineNumbersToEndOfProc:=True, Scope:=vbScopeAllProc
End Sub

Sub AddLineNumbers_vbLabelTab()
    AddLineNumbers wbName:="EvaluateCall.xlsm", vbCompName:="ModLineNumbers_testDest", LabelType:=vbLabelTab, AddLineNumbersToEmptyLines:=True, AddLineNumbersToEndOfProc:=True, Scope:=vbScopeAllProc
End Sub

Sub RemoveLineNumbers_vbLabelColon()
    RemoveLineNumbers wbName:="EvaluateCall.xlsm", vbCompName:="ModLineNumbers_testDest", LabelType:=vbLabelColon
End Sub

Sub RemoveLineNumbers_vbLabelTab()
    RemoveLineNumbers wbName:="EvaluateCall.xlsm", vbCompName:="ModLineNumbers_testDest", LabelType:=vbLabelTab
End Sub

提醒一下,这里有一些关于行号的编译规则:

  • 不允许在子/函数声明语句之前
  • 不允许在 proc 之外
  • 不允许出现在续行符“_”(下划线)之后的行中
  • 每个代码行不允许有多个标签/行号 ~~> 必须测试除行号之外的现有标签,否则尝试强制行号时会出现编译错误。
  • 不允许使用已经具有特殊 VBA 含义的字符 ~~> 允许使用的字符有 [a-Z]、[0-9]、é、è、ô、ù、€、£、§ 甚至单独的“:” !
  • 编译器会修剪标签前的任何空格~~>所以如果有标签,则行的第一个字符是标签的第一个字符,不能是空格。
  • 用冒号附加行号将导致在“:”和第一个下一个字符之间插入一个空格(如果没有)
  • 在行号附加制表符/空格时,最后一个数字和第一个下一个字符之间必须至少有一个空格,编译器不会像使用冒号分隔符的标签那样添加它
  • .ReplaceLine 方法将覆盖编译规则,而不会像在设计模式下选择新行或手动重新启动编译时那样显示任何编译错误
  • 编译器“比 VBA 环境/系统更快”:例如,在使用 .ReplaceLine 插入带有冒号且没有任何空格的行号之后,如果调用 .Lines 属性来获取新字符串,空格(在冒号字符和字符串的第一个字符之间)已附加在该字符串中!
  • 在调用 .ReplaceLine 后(从正在编辑的模块内部或外部)无法进入调试模式,直到代码运行并重置执行。

【讨论】:

  • 无论如何,another Microsoft Excel MVP 坚决反对在 VBA 中使用Erl 和行号。此点列表缺少一些注意事项:1) Erl 具有 Integer 分辨率,但 Long 行号是完全合法的,这使得 Erl 定义中断(-12345 是哪一行?),以及 2) Erl 返回遇到的最后一个行号,这意味着您需要无处不在的行号和看起来像过去的 Commodore-64 爆炸的代码才能使用它。
  • 如果您有一个成功运行的编号行,然后是一个引发错误的未编号行,Erl 将返回成功运行的行的行号。因此,要让Erl“工作”,您需要每一个逻辑行都有一个行号,读起来像 1980 ;-)
  • 啊,好吧,你说得对,这不是一个很好的解决方法,反正我从来没有在其他任何地方使用过它,也不是 Commodore-64 ;)
【解决方案2】:

简答 for excel 2016,2013年还没试过。

做一次:

  1. final Module2 中的大代码粘贴到您工作簿的此答案中。
  2. final Module3 的代码粘贴到您的工作簿中。
  3. final Module4 的代码粘贴到您的工作簿中。
  4. 然后粘贴Global allow_for_line_addition As String这行,这样你就可以自动在每行的上方/第一行添加行号 模块。
  5. 删除每个模块末尾的所有空行(因此在模块的最后一个end subend functionEnd Property 之后没有输)。
  6. 在 VBA 编辑器中,未运行代码且未处于“中断”模式时:单击工具>参考>标记:`Microsoft Visual Basic for Applications Extensibility 5.3"

每次修改代码时执行:

  1. *运行 final Module3 的代码以删除工作簿中所有模块的行号。
  2. *运行 final Module4 的代码,为工作簿中的所有模块添加行号。

(*因为有时如果你剪掉或移动它们会出现错误(例如,将line 2440: 放在line 2303: 上方)。通过删除并重新添加它们,行号会自动再次正确)

长答案(包括学习步骤和尝试)——对我来说,实现 hymced 的答案并不简单,所以我记录了在 VBA 代码中向模块添加行号所需的步骤编辑器(*并再次删除它们)。我按照以下步骤使其正常工作。

  1. this link我了解到vbcomponent可以是一个模块。
  2. 我将给出的第一个代码复制到 temporary Module2,将 hymced 给出的第二个代码复制到 temporary Module3
  3. 然后将temporaryModule3中第二行代码的第一行修改为:

    AddLineNumbers wbName:="Book1.xlsm", vbCompName:="Module1", LabelType:=vbLabelColon, AddLineNumbersToEmptyLines:=True, AddLineNumbersToEndOfProc:=True, Scope:=vbScopeAllProc
    
  4. 我遇到了一个错误:

    procName = .ProcOfLine(i, vbext_pk_Proc) ` Type d`argument ByRef incompatible ~~> Requires VBIDE library as a Reference for the VBA Project
    
  5. 所以我读到我必须启用 VBIDE 库。

  6. 于是我停止了代码,点击工具>引用,找不到VBIDE库。

  7. this forum 上,我发现通过添加对 VBIDE 的引用已启用 VBA 扩展库:

单击 VBE 中的工具-参考,然后向下滚动并勾选 Microsoft Visual Basic for Applications Extensibility 5.3 的条目。

因此,在这样做之后,第一个错误消失了,它没有突出显示任何行,而是给出了错误“无效的过程调用或参数”。

  1. 由于我仍然不确定 vbCompName,我认为它可能需要知道 sub 而不是模块,所以我尝试将 temporary Module3 中的第二个代码修改为:

    AddLineNumbers wbName:="Book1.xlsm", vbCompName:="learn", LabelType:=vbLabelColon, AddLineNumbersToEmptyLines:=True, AddLineNumbersToEndOfProc:=True, Scope:=vbScopeAllProc
    
  2. 突出显示的行:

    With Workbooks(wbName).VBProject.VBComponents(vbCompName).CodeModule 
    

说:subscript out of range。 (所以我也尝试了:Module1.learnModule1:learn,产生了 subscript out of range-error。

事实证明,

AddLineNumbers wbName:="Book1.xlsm", vbCompName:="Module1", LabelType:=vbLabelColon, AddLineNumbersToEmptyLines:=True, AddLineNumbersToEndOfProc:=True, Scope:=vbScopeAllProc

如果要提供行号的 sub 位于名为 Module1 的模块中,则这是调用替换 sub 的正确方法。第一个错误描述发生,但它确实将行号添加到代码中(除了包含sub ... 的第一行和包含end sub 的最后一行。在名为@ 的excel 2016 工作簿的Module1 中测试名为sub learn() 987654352@。为了完整性,learn 包括:

Sub learn()
    ThisWorkbook.Worksheets("Sheet1").Activate
    Range("A1").Activate
    Range("A1").Select
    Range("A1").Value = Range("A1").Value + 1
End Sub

但是,在返回的路上,删除行号,它产生了一个错误,因为它在Sub AddLineNumbers... 中询问procName 的.lines(0,1)

  1. 所以我修改它以排除 .lines(0,1) 将下面的更改代码放入 final Module2:

    Public Enum vbLineNumbers_LabelTypes
        vbLabelColon    ' 0
        vbLabelTab      ' 1
    End Enum
    
    Public Enum vbLineNumbers_ScopeToAddLineNumbersTo
        vbScopeAllProc  ' 1
        vbScopeThisProc ' 2
    End Enum
              Sub AddLineNumbers(ByVal wbName As String, _
                                                          ByVal vbCompName As String, _
                                                          ByVal LabelType As vbLineNumbers_LabelTypes, _
                                                          ByVal AddLineNumbersToEmptyLines As Boolean, _
                                                          ByVal AddLineNumbersToEndOfProc As Boolean, _
                                                          ByVal Scope As vbLineNumbers_ScopeToAddLineNumbersTo, _
                                                          Optional ByVal thisProcName As String)
    
    ' USAGE RULES
    ' DO NOT MIX LABEL TYPES FOR LINE NUMBERS! IF ADDING LINE NUMBERS AS COLON TYPE, ANY LINE NUMBERS AS VBTAB TYPE MUST BE REMOVE BEFORE, AND RECIPROCALLY ADDING LINE NUMBERS AS VBTAB TYPE
    
        Dim i As Long
        Dim j As Long
        Dim procName As String
        Dim startOfProcedure As Long
        Dim lengthOfProcedure As Long
        Dim endOfProcedure As Long
        Dim strLine As String
    
        With Workbooks(wbName).VBProject.VBComponents(vbCompName).CodeModule
            .CodePane.Window.Visible = False
    
    If Scope = vbScopeAllProc Then
    
            For i = 1 To .CountOfLines - 1
    
                strLine = .Lines(i, 1)
                procName = .ProcOfLine(i, vbext_pk_Proc) ' Type d'argument ByRef incompatible ~~> Requires VBIDE library as a Reference for the VBA Project
    
                If procName <> vbNullString Then
                    startOfProcedure = .ProcStartLine(procName, vbext_pk_Proc)
                    bodyOfProcedure = .ProcBodyLine(procName, vbext_pk_Proc)
                    countOfProcedure = .ProcCountLines(procName, vbext_pk_Proc)
    
                    prelinesOfProcedure = bodyOfProcedure - startOfProcedure
                    'postlineOfProcedure = ??? not directly available since endOfProcedure is itself not directly available.
    
                    lengthOfProcedure = countOfProcedure - prelinesOfProcedure ' includes postlinesOfProcedure !
                    'endOfProcedure = ??? not directly available, each line of the proc must be tested until the End statement is reached. See below.
    
                    If endOfProcedure <> 0 And startOfProcedure < endOfProcedure And i > endOfProcedure Then
                        GoTo NextLine
                    End If
    
                    If i = bodyOfProcedure Then inprocbodylines = True
    
                    If bodyOfProcedure < i And i < startOfProcedure + countOfProcedure Then
                        If Not (.Lines(i - 1, 1) Like "* _") Then
    
                            inprocbodylines = False
    
                            PreviousIndentAdded = 0
    
                            If Trim(strLine) = "" And Not AddLineNumbersToEmptyLines Then GoTo NextLine
    
                            If IsProcEndLine(wbName, vbCompName, i) Then
                                endOfProcedure = i
                                If AddLineNumbersToEndOfProc Then
                                    Call IndentProcBodyLinesAsProcEndLine(wbName, vbCompName, LabelType, endOfProcedure)
                                Else
                                    GoTo NextLine
                                End If
                            End If
    
                            If LabelType = vbLabelColon Then
                                If HasLabel(strLine, vbLabelColon) Then strLine = RemoveOneLineNumber(.Lines(i, 1), vbLabelColon)
                                If Not HasLabel(strLine, vbLabelColon) Then
                                    temp_strLine = strLine
                                    .ReplaceLine i, CStr(i) & ":" & strLine
                                    new_strLine = .Lines(i, 1)
                                    If Len(new_strLine) = Len(CStr(i) & ":" & temp_strLine) Then
                                        PreviousIndentAdded = Len(CStr(i) & ":")
                                    Else
                                        PreviousIndentAdded = Len(CStr(i) & ": ")
                                    End If
                                End If
                            ElseIf LabelType = vbLabelTab Then
                                If Not HasLabel(strLine, vbLabelTab) Then strLine = RemoveOneLineNumber(.Lines(i, 1), vbLabelTab)
                                If Not HasLabel(strLine, vbLabelColon) Then
                                    temp_strLine = strLine
                                    .ReplaceLine i, CStr(i) & vbTab & strLine
                                    PreviousIndentAdded = Len(strLine) - Len(temp_strLine)
                                End If
                            End If
    
                        Else
                            If Not inprocbodylines Then
                                If LabelType = vbLabelColon Then
                                    .ReplaceLine i, Space(PreviousIndentAdded) & strLine
                                ElseIf LabelType = vbLabelTab Then
                                    .ReplaceLine i, Space(4) & strLine
                                End If
                            Else
                            End If
                        End If
    
                    End If
    
                End If
    
    NextLine:
            Next i
    
    ElseIf AddLineNumbersToEmptyLines And Scope = vbScopeThisProc Then
    
    End If
    
            .CodePane.Window.Visible = True
        End With
    
    End Sub
              Function IsProcEndLine(ByVal wbName As String, _
                  ByVal vbCompName As String, _
                  ByVal Line As Long) As Boolean
    
    With Workbooks(wbName).VBProject.VBComponents(vbCompName).CodeModule
    If Trim(.Lines(Line, 1)) Like "End Sub*" _
                Or Trim(.Lines(Line, 1)) Like "End Function*" _
                Or Trim(.Lines(Line, 1)) Like "End Property*" _
                Then IsProcEndLine = True
    End With
    
    End Function
              Sub IndentProcBodyLinesAsProcEndLine(ByVal wbName As String, ByVal vbCompName As String, ByVal LabelType As vbLineNumbers_LabelTypes, ByVal ProcEndLine As Long)
        Dim procName As String
        Dim startOfProcedure As Long
        Dim endOfProcedure As Long
    
        With Workbooks(wbName).VBProject.VBComponents(vbCompName).CodeModule
    
            procName = .ProcOfLine(ProcEndLine, vbext_pk_Proc)
            bodyOfProcedure = .ProcBodyLine(procName, vbext_pk_Proc)
            endOfProcedure = ProcEndLine
            strEnd = .Lines(endOfProcedure, 1)
    
            j = bodyOfProcedure
            Do Until Not .Lines(j - 1, 1) Like "* _" And j <> bodyOfProcedure
    
                strLine = .Lines(j, 1)
    
                If LabelType = vbLabelColon Then
                    If Mid(strEnd, Len(CStr(endOfProcedure)) + 1 + 1 + 1, 1) = " " Then
                        .ReplaceLine j, Space(Len(CStr(endOfProcedure)) + 1) & strLine
                    Else
                        .ReplaceLine j, Space(Len(CStr(endOfProcedure)) + 2) & strLine
                    End If
                ElseIf LabelType = vbLabelTab Then
                    If endOfProcedure < 1000 Then
                        .ReplaceLine j, Space(4) & strLine
                    Else
                        Debug.Print "This tool is limited to 999 lines of code to work properly."
                    End If
                End If
    
                j = j + 1
            Loop
    
        End With
    End Sub
              Sub RemoveLineNumbers(ByVal wbName As String, ByVal vbCompName As String, ByVal LabelType As vbLineNumbers_LabelTypes)
        Dim i As Long
        With Workbooks(wbName).VBProject.VBComponents(vbCompName).CodeModule
            'MsgBox ("nr of lines = " & .CountOfLines & vbNewLine & "Procname = " & procName)
                'MsgBox ("nr of lines REMEMBER MUST BE LARGER THAN 7! = " & .CountOfLines)
            For i = 1 To .CountOfLines
                procName = .ProcOfLine(i, vbext_pk_Proc)
                If procName <> vbNullString Then
                    If i > 1 Then
                            'MsgBox ("Line " & i & " is a body line " & .ProcBodyLine(procName, vbext_pk_Proc))
                        If i = .ProcBodyLine(procName, vbext_pk_Proc) Then inprocbodylines = True
                            If .Lines(i - 1, 1) <> "" Then
                                'MsgBox (.Lines(i - 1, 1))
                            End If
                        If Not .Lines(i - 1, 1) Like "* _" Then
                            'MsgBox (inprocbodylines)
                            inprocbodylines = False
                                'MsgBox ("recoginized a line that should be substituted: " & i)
                            'MsgBox ("about to replace " & .Lines(i, 1) & vbNewLine & " with: " & RemoveOneLineNumber(.Lines(i, 1), LabelType) & vbNewLine & " with label type: " & LabelType)
                            .ReplaceLine i, RemoveOneLineNumber(.Lines(i, 1), LabelType)
                        Else
                            If IsInProcBodyLines Then
                                ' do nothing
                                    'MsgBox (i)
                            Else
                                .ReplaceLine i, Mid(.Lines(i, 1), RemovedChars_previous_i + 1)
                            End If
                        End If
                    End If
                Else
                ' GoTo NextLine
                End If
    NextLine:
            Next i
        End With
    End Sub
              Function RemoveOneLineNumber(ByVal aString As String, ByVal LabelType As vbLineNumbers_LabelTypes)
        RemoveOneLineNumber = aString
        If LabelType = vbLabelColon Then
            If aString Like "#:*" Or aString Like "##:*" Or aString Like "###:*" Or aString Like "####:*" Then
                RemoveOneLineNumber = Mid(aString, 1 + InStr(1, aString, ":", vbTextCompare))
                If Left(RemoveOneLineNumber, 2) Like " [! ]*" Then RemoveOneLineNumber = Mid(RemoveOneLineNumber, 2)
            End If
        ElseIf LabelType = vbLabelTab Then
            If aString Like "#   *" Or aString Like "##  *" Or aString Like "### *" Or aString Like "#### *" Then RemoveOneLineNumber = Mid(aString, 5)
            If aString Like "#" Or aString Like "##" Or aString Like "###" Or aString Like "####" Then RemoveOneLineNumber = ""
        End If
    End Function
              Function HasLabel(ByVal aString As String, ByVal LabelType As vbLineNumbers_LabelTypes) As Boolean
        If LabelType = vbLabelColon Then HasLabel = InStr(1, aString & ":", ":") < InStr(1, aString & " ", " ")
        If LabelType = vbLabelTab Then
            HasLabel = Mid(aString, 1, 4) Like "#   " Or Mid(aString, 1, 4) Like "##  " Or Mid(aString, 1, 4) Like "### " Or Mid(aString, 1, 5) Like "#### "
        End If
    End Function
              Function RemoveLeadingSpaces(ByVal aString As String) As String
        Do Until Left(aString, 1) <> " "
            aString = Mid(aString, 2)
        Loop
        RemoveLeadingSpaces = aString
    End Function
              Function WhatIsLineIndent(ByVal aString As String) As String
        i = 1
        Do Until Mid(aString, i, 1) <> " "
            i = i + 1
        Loop
        WhatIsLineIndent = i
    End Function
    
              Function HowManyLeadingSpaces(ByVal aString As String) As String
        HowManyLeadingSpaces = WhatIsLineIndent(aString) - 1
    End Function
    

使用下面的代码在sub learn() 上调用替换,粘贴到临时 module3

    Sub AddLineNumbers_vbLabelColon()
    AddLineNumbers wbName:="Book1.xlsm", vbCompName:="Module1", LabelType:=vbLabelColon, AddLineNumbersToEmptyLines:=True, AddLineNumbersToEndOfProc:=True, Scope:=vbscopeallproc
End Sub

Sub AddLineNumbers_vbLabelTab()
    AddLineNumbers wbName:="Book1.xlsm", vbCompName:="Module1", LabelType:=vbLabelTab, AddLineNumbersToEmptyLines:=True, AddLineNumbersToEndOfProc:=True, Scope:=vbscopeallproc
End Sub

Sub RemoveLineNumbers_vbLabelColon()
    RemoveLineNumbers wbName:="Book1.xlsm", vbCompName:="Module1", LabelType:=vbLabelColon
End Sub

Sub RemoveLineNumbers_vbLabelTab()
    RemoveLineNumbers wbName:="Book1.xlsm", vbCompName:="Module1", LabelType:=vbLabelTab
End Sub

现在它工作了(添加和删除行号,调用添加/删除行号的所有 4 方法粘贴在 临时 module2 用于模块中的单个子(@987654363 @ 在示例案例中)。所以我尝试在同一个模块中将 2 个 subs 放在彼此后面。在这种情况下,代码没有将添加行号更改为 2nd sub。

  1. 所以我在Module1上方添加了以下行:

    Global allow_for_line_addition As String
    

使Module1 看起来像:

Global allow_for_line_addition As String
  Sub learn()
    ThisWorkbook.Worksheets("Sheet1").Activate
    Range("A1").Activate
    Range("A1").Select
    Range("A1").Value = Range("A1").Value + 1
End Sub
Sub learn2()
    ThisWorkbook.Worksheets("Sheet1").Activate
    Range("A1").Activate
    Range("A1").Select
    Range("A1").Value = Range("A1").Value + 1
End Sub

现在它给整个模块添加了行号,但是并没有从整个模块中移除行号,所以我编辑了hymceds answer as well and already put it in the long code of **final**Module2`的移除代码。

注意:如果您在子或函数结束后有空的白线,则每次运行脚本以添加行号时,它将继续添加白线(第一次运行后,只需更新行号)。这些空行号在执行代码时会导致错误,因此您应该将它们删除一次。如果 sub 末尾没有空行,则此代码也不会添加新行。

  1. 要将行号添加到工作簿中的所有模块,请将长代码保留在 final Module2 中,因为我已对其进行了修改,并替换了 temporary @ 的代码987654370@ with final Module3:

    Global allow_for_line_addition As String 'this is just so that you can automatically add linenumbers
            Sub remove_line_numbering_all_modules()
    'source: https://stackoverflow.com/questions/36791473/vba-getting-the-modules-in-workbook
    'This code numbers all the modules in your .xlsm
        Dim vbcomp As VBComponent
        Dim modules As Collection
    Set modules = New Collection
        For Each vbcomp In ThisWorkbook.VBProject.VBComponents
            'if normal or class module
            If ((vbcomp.Type = vbext_ct_StdModule) Or (vbcomp.Type = vbext_ct_ClassModule)) Then
                   'V0:
                   RemoveLineNumbers wbName:=ThisWorkbook.name, vbCompName:=vbcomp.name, LabelType:=vbLabelColon
                   'V1:
                   'Call RemoveLineNumbers(ThisWorkbook.name, vbcomp.name)
            End If
        Next vbcomp
    End Sub
    

    并将以下代码添加到 final Module4:

    Global allow_for_line_addition As String 'this is just so that you can automatically add linenumbers
    'This sub adds line numbers to all the modules after you have added the following line to every module
    'add tools references microsoft visual basic for applications (5.3) as checked
    'Source httpsstackoverflow.comquestions40731182excel-vba-how-to-turn-on-line-numbers-in-code-editor50368332#50368332
            Sub add_line_numbering_all_modules()
    'source: https://www.stackoverflow.com/questions/36791473/vba-getting-the-modules-in-workbook
    'This code numbers all the modules in your .xlsm
        Dim vbcomp As VBComponent
        Dim modules As Collection
        Set modules = New Collection
        For Each vbcomp In ThisWorkbook.VBProject.VBComponents
            'if normal or class module
            If ((vbcomp.Type = vbext_ct_StdModule) Or (vbcomp.Type = vbext_ct_ClassModule)) Then
                   'V0:
                   Call AddLineNumbers(ThisWorkbook.name, vbcomp.name, vbLabelColon, True, True, vbScopeAllProc)
                   'v1
                   'Call AddLineNumbers(ThisWorkbook.name, vbcomp.name)
            End If
        Next vbcomp
    End Sub
    

您可以将"Book1.xlsm" 替换为您自己的工作簿的名称,或使用thisworkbook(注意没有“”),反之亦然。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-23
    • 1970-01-01
    • 1970-01-01
    • 2015-11-29
    • 2016-04-13
    相关资源
    最近更新 更多