January 20, 2021
Hot Topics:

Prime Programming Proficiency, Part 3: Lines-of-code Counter

  • By Paul Kimmel
  • Send Email »
  • More Articles »

Implementing the Line Counter

The LineCounter is a completely separate class. As a result, I can stub it out with an imperfect implementation and improve it at a later date or outsource it to someone who may specialize in the counting of source lines of code. Because the LineCounter (see Listing 8) does not know about the visitor and hosts, another developer will not need my entire implementation to do his or her job, implementing the LineCounter.

Listing 8: The LineCounter class sub-divides lines of a separate utility.

Imports EnvDTE
Imports System
Imports System.IO
Imports System.Diagnostics
Imports System.Text.RegularExpressions

Public Module LineCounter

   Public Function GetLineCount(ByVal Item As ProjectItem)
      If (Not IsValidItem(Item)) Then Return 0

      Return CountLines(Item)
   End Function

   Private Function IsValidItem(ByVal item As ProjectItem) As Boolean
      Return IsValidItem(item.Name)
   End Function

   Private Function IsValidItem(ByVal FileName As String) As Boolean
      Return Regex.IsMatch(FileName, "^\w+.cs$") Or _
      Regex.IsMatch(FileName, "^\w+.ascx.cs$") _
         Or Regex.IsMatch(FileName, "^\w+.aspx.cs$") Or _
         Regex.IsMatch(FileName, "^\w+.aspx$") _
         Or Regex.IsMatch(FileName, "^\w+.ascx")
   End Function

   Private Function CountLines(ByVal item As ProjectItem) As Long
         Return DoCountLines(item)
      Catch ex As Exception
         Return DoManualCount(item.FileNames(1))
      End Try
   End Function

   Private Function DoManualCount(ByVal FileName As String) As Long
      Dim reader As TextReader = New StreamReader(FileName)
      Dim all As String = reader.ReadToEnd()

      Return Regex.Matches(all, vbCrLf).Count()
   End Function

   Private Function DoCountLines(ByVal item As ProjectItem) As Long

      Dim Count As Long = 0

         Dim s As TextSelection = item.Document.Selection()

         Count = s.ActivePoint.Line()


      End Try

      Return Count
   End Function

   Private WasOpen As Boolean = False
   Private Current As Long = 0
   Private Sub Open(ByVal item As ProjectItem)
      WasOpen = item.IsOpen
      If (Not WasOpen) Then item.Open()
   End Sub

   Private Sub Close(ByVal item As ProjectItem)
      If (Not WasOpen) Then
      End If
   End Sub

   Private Sub StoreOffset(ByVal selection As TextSelection)
      Current = selection.ActivePoint.Line
   End Sub

   Private Sub RestoreOffset(ByVal selection As TextSelection)
      If (WasOpen) Then
         selection.MoveToLineAndOffset(Current, 0)
      End If
   End Sub
End Module

Oddly enough, the extensibility object model does not seem to have a straightforward property for getting the number of lines in a project item. (Perhaps a means of doing this exits, but I just can't find it.)

Two methods in this class try to count the number of lines: DoCountLines and DoManualCount. DoCountLines uses the ProjectItem passed to the constructor, opens the file represented by ProjectItem, stores the current position in the file, moves the cursor to the end of the document, and then asks what the active line is. Finally, it restores the offset and closes the ProjectItem. Both Open and Close take into account whether the ProjectItem was already opened or not and close only ProjectItems that the class opened.

If an exception occurs in DoCountLines, the caller, CountLines, catches the exception and calls DoManualCount. DoManualCount opens the ProjectItem and attempts to count carriage return and line feed pairs. DoManualCount is a lot slower then DoCountLines, but together they seem to form a resilient pair.

Note: Kevin McFarlane sent me an e-mail that mentions Oz Solomnovich's Project Line Counter add-in. You should be able to find the source here: http://wndtabs.com/plc/. I haven't looked at the source, but the GUI looks good.

Page 5 of 6

This article was originally published on August 12, 2004

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date