using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Microsoft.VisualStudio.CommandBars;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using LineCounterAddin.Interfaces;
namespace LineCounterAddin
{
/// <summary>
/// Line Counter add-in user interface.
/// </summary>
public partial class LineCounterBrowser : UserControl
{
#region Nested Classes
abstract class ListViewItemComparer : System.Collections.IComparer
{
public abstract int Compare(ListViewItem item1, ListViewItem item2);
public ListView SortingList;
public int Column;
#region IComparer Members
int System.Collections.IComparer.Compare(object x, object y)
{
if (x is ListViewItem && y is ListViewItem)
{
int diff = Compare((ListViewItem)x, (ListViewItem)y);
if (SortingList.Sorting == SortOrder.Descending)
diff *= -1;
return diff;
}
else
{
throw new ArgumentException("One or both of the arguments are not ListViewItem objects.");
}
}
#endregion
}
/// <summary>
/// Compares items based on file name.
/// </summary>
class FileNameComparer : ListViewItemComparer
{
public override int Compare(ListViewItem item1, ListViewItem item2)
{
return String.Compare(item1.Text, item2.Text, false);
}
}
/// <summary>
/// Compares items based on total lines primarily, and
/// the filename secondarily.
/// </summary>
class FileLinesComparer : ListViewItemComparer
{
public override int Compare(ListViewItem item1, ListViewItem item2)
{
string string1 = item1.SubItems[Column].Text;
string string2 = item2.SubItems[Column].Text;
if (string1 != null && string2 != null)
{
int total1 = int.Parse(string1);
int total2 = int.Parse(string2);
// Compare the totals...
int diff = total1 - total2;
// If totals are equal...
if (diff == 0)
{
// Compare the filenames
diff = String.Compare(item1.Text, item2.Text, false);
}
return diff;
}
return 0;
}
}
/// <summary>
/// Compares items based on file extension.
/// </summary>
class FileExtensionComparer : ListViewItemComparer
{
public override int Compare(ListViewItem item1, ListViewItem item2)
{
string string1 = item1.SubItems[4].Text;
string string2 = item2.SubItems[4].Text;
return String.Compare(string1, string2, true);
}
}
#endregion
#region Constructor
/// <summary>
/// Construct the line counter user interface and
/// the countable file type mappings (to icons and
/// counting algorithms).
/// </summary>
public LineCounterBrowser()
{
InitializeComponent();
m_cfgMgr = ConfigManager.Instance;
#region Obsolete Initialization
// Map project types to icons for use in the projects list
/*m_projIconMappings = new Dictionary<string, int>();
m_projIconMappings.Add("{00000000-0000-0000-0000-000000000000}", 0);
m_projIconMappings.Add(CodeModelLanguageConstants.vsCMLanguageCSharp, 1);
m_projIconMappings.Add(CodeModelLanguageConstants.vsCMLanguageVB, 2);
m_projIconMappings.Add(CodeModelLanguageConstants.vsCMLanguageMC, 3);
m_projIconMappings.Add(CodeModelLanguageConstants.vsCMLanguageVC, 3);
m_projIconMappings.Add("{00000001-0000-0000-0000-000000000000}", 5);*/
// List all the countable file types (so we don't try to count .dll's,
// images, executables, etc.
/*m_countableTypes = new System.Collections.Specialized.StringCollection();
m_countableTypes.Add("*");
m_countableTypes.Add(".cs");
m_countableTypes.Add(".vb");
m_countableTypes.Add(".vj");
m_countableTypes.Add(".cpp");
m_countableTypes.Add(".cc");
m_countableTypes.Add(".cxx");
m_countableTypes.Add(".c");
m_countableTypes.Add(".hpp");
m_countableTypes.Add(".hh");
m_countableTypes.Add(".hxx");
m_countableTypes.Add(".h");
m_countableTypes.Add(".js");
m_countableTypes.Add(".cd");
m_countableTypes.Add(".resx");
m_countableTypes.Add(".res");
m_countableTypes.Add(".css");
m_countableTypes.Add(".htm");
m_countableTypes.Add(".html");
m_countableTypes.Add(".xml");
m_countableTypes.Add(".xsl");
m_countableTypes.Add(".xslt");
m_countableTypes.Add(".xsd");
m_countableTypes.Add(".config");
m_countableTypes.Add(".asax");
m_countableTypes.Add(".ascx");
m_countableTypes.Add(".asmx");
m_countableTypes.Add(".aspx");
m_countableTypes.Add(".ashx");
m_countableTypes.Add(".idl");
m_countableTypes.Add(".odl");
m_countableTypes.Add(".txt");
m_countableTypes.Add(".sql");*/
// Map file extensions to icons for use in the file list
/*m_fileIconMappings = new Dictionary<string, int>(33);
m_fileIconMappings.Add("*", 0);
m_fileIconMappings.Add(".cs", 1);
m_fileIconMappings.Add(".vb", 2);
m_fileIconMappings.Add(".vj", 3);
m_fileIconMappings.Add(".cpp", 4);
m_fileIconMappings.Add(".cc", 4);
m_fileIconMappings.Add(".cxx", 4);
m_fileIconMappings.Add(".c", 5);
m_fileIconMappings.Add(".hpp", 6);
m_fileIconMappings.Add(".hh", 6);
m_fileIconMappings.Add(".hxx", 6);
m_fileIconMappings.Add(".h", 6);
m_fileIconMappings.Add(".js", 7);
m_fileIconMappings.Add(".cd", 8);
m_fileIconMappings.Add(".resx", 9);
m_fileIconMappings.Add(".res", 9);
m_fileIconMappings.Add(".css", 10);
m_fileIconMappings.Add(".htm", 11);
m_fileIconMappings.Add(".html", 11);
m_fileIconMappings.Add(".xml", 12);
m_fileIconMappings.Add(".xsl", 13);
m_fileIconMappings.Add(".xslt", 13);
m_fileIconMappings.Add(".xsd", 14);
m_fileIconMappings.Add(".config", 15);
m_fileIconMappings.Add(".asax", 16);
m_fileIconMappings.Add(".ascx", 17);
m_fileIconMappings.Add(".asmx", 18);
m_fileIconMappings.Add(".aspx", 19);
m_fileIconMappings.Add(".ashx", 0);
m_fileIconMappings.Add(".idl", 0);
m_fileIconMappings.Add(".odl", 0);
m_fileIconMappings.Add(".txt", 0);
m_fileIconMappings.Add(".sql", 0);*/
// Prepare counting algorithm mappings
/*CountParserDelegate countLinesGeneric = new CountParserDelegate(CountLinesGeneric);
CountParserDelegate countLinesCStyle = new CountParserDelegate(CountLinesCStyle);
CountParserDelegate countLinesVBStyle = new CountParserDelegate(CountLinesVBStyle);
CountParserDelegate countLinesXMLStyle = new CountParserDelegate(CountLinesXMLStyle);
m_countAlgorithms = new Dictionary<string, CountParserDelegate>(33);
m_countAlgorithms.Add("*", countLinesGeneric);
m_countAlgorithms.Add(".cs", countLinesCStyle);
m_countAlgorithms.Add(".vb", countLinesVBStyle);
m_countAlgorithms.Add(".vj", countLinesCStyle);
m_countAlgorithms.Add(".js", countLinesCStyle);
m_countAlgorithms.Add(".cpp", countLinesCStyle);
m_countAlgorithms.Add(".cc", countLinesCStyle);
m_countAlgorithms.Add(".cxx", countLinesCStyle);
m_countAlgorithms.Add(".c", countLinesCStyle);
m_countAlgorithms.Add(".hpp", countLinesCStyle);
m_countAlgorithms.Add(".hh", countLinesCStyle);
m_countAlgorithms.Add(".hxx", countLinesCStyle);
m_countAlgorithms.Add(".h", countLinesCStyle);
m_countAlgorithms.Add(".idl", countLinesCStyle);
m_countAlgorithms.Add(".odl", countLinesCStyle);
m_countAlgorithms.Add(".txt", countLinesGeneric);
m_countAlgorithms.Add(".xml", countLinesXMLStyle);
m_countAlgorithms.Add(".xsl", countLinesXMLStyle);
m_countAlgorithms.Add(".xslt", countLinesXMLStyle);
m_countAlgorithms.Add(".xsd", countLinesXMLStyle);
m_countAlgorithms.Add(".config", countLinesXMLStyle);
m_countAlgorithms.Add(".res", countLinesGeneric);
m_countAlgorithms.Add(".resx", countLinesXMLStyle);
m_countAlgorithms.Add(".aspx", countLinesXMLStyle);
m_countAlgorithms.Add(".ascx", countLinesXMLStyle);
m_countAlgorithms.Add(".ashx", countLinesXMLStyle);
m_countAlgorithms.Add(".asmx", countLinesXMLStyle);
m_countAlgorithms.Add(".asax", countLinesXMLStyle);
m_countAlgorithms.Add(".htm", countLinesXMLStyle);
m_countAlgorithms.Add(".html", countLinesXMLStyle);
m_countAlgorithms.Add(".css", countLinesCStyle);
m_countAlgorithms.Add(".sql", countLinesGeneric);
m_countAlgorithms.Add(".cd", countLinesGeneric);*/
#endregion
}
#endregion
#region Variables
private DTE2 m_dte; // Reference to the Visual Studio DTE object
private List<LineCountSummary> m_summaryList;
//private Dictionary<string, int> m_projIconMappings;
//private Dictionary<string, int> m_fileIconMappings;
private Dictionary<string, CountParserDelegate> m_countAlgorithms;
private System.Collections.Specialized.StringCollection m_countableTypes;
private ConfigManager m_cfgMgr;
#endregion
#region Properties
/// <summary>
/// Recieves the VS DTE object
/// </summary>
public DTE2 DTE
{
set
{
m_dte = value;
}
}
#endregion
#region Handlers
private int lastSortColumn = -1; // Track the last clicked column
/// <summary>
/// Sorts the ListView by the clicked column, automatically
/// reversing the sort order on subsequent clicks of the
/// same column.
/// </summary>
/// <param name="sender"></param>
/// <param name="e">Provides the index of the clicked column.</param>
private void lvFileList_ColumnClick(object sender, ColumnClickEventArgs e)
{
// Define a variable of the abstract (generic) comparer
ListViewItemComparer comparer = null;
// Create an instance of the specific comparer in the 'comparer'
// variable. Since each of the explicit comparer classes is
// derived from the abstract case class, polymorphism applies.
switch (e.Column)
{
// Line count columns
case 1:
case 2:
case 3:
comparer = new FileLinesComparer();
break;
// The file extension column
case 4:
comparer = new FileExtensionComparer();
break;
// All other columns sort by file name
default:
comparer = new FileNameComparer();
break;
}
// Set the sorting order
if (lastSortColumn == e.Column)
{
if (lvFileList.Sorting == SortOrder.Ascending)
{
lvFileList.Sorting = SortOrder.Descending;
}
else
{
lvFileList.Sorting = SortOrder.Ascending;
}
}
else
{
lvFileList.Sorting = SortOrder.Ascending;
}
lastSortColumn = e.Column;
// Send the comparer the list view and column being sorted
comparer.SortingList = lvFileList;
comparer.Column = e.Column;
// Attach the comparer to the list view and sort
lvFileList.ListViewItemSorter = comparer;
lvFileList.Sort();
}
/// <summary>
/// Sets the file listing mode to No Grouping,
/// and recounts.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks>FIX THIS: Add a helper to simply repopulate
/// the list view with existing count data if it exists!!!</remarks>
private void tsmiNoGrouping_Click(object sender, EventArgs e)
{
tsmiGroupByProj.Checked = false;
tsmiGroupByType.Checked = false;
//SumSolution();
ReGroupFileListing();
}
/// <summary>
/// Sets the file listing mode to Group by Project,
/// and recounts.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks>FIX THIS: Add a helper to simply repopulate
/// the list view with existing count data if it exists!!!</remarks>
private void tsmiGroupByProj_Click(object sender, EventArgs e)
{
tsmiGroupByType.Checked = false;
tsmiNoGrouping.Checked = false;
//SumSolution();
ReGroupFileListing();
}
/// <summary>
/// Sets the file listing mode to Group by Type,
/// and recounts.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <remarks>FIX THIS: Add a helper to simply repopulate
/// the list view with existing count data if it exists!!!</remarks>
private void tsmiGroupByType_Click(object sender, EventArgs e)
{
tsmiGroupByProj.Checked = false;
tsmiNoGrouping.Checked = false;
//SumSolution();
ReGroupFileListing();
}
/// <summary>
/// Forces a recount of all files in all projects, and
/// updates the view.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsmiRecalculate_FileList_Click(object sender, EventArgs e)
{
ScanSolution();
SumSolution();
}
/// <summary>
/// Displays summary details for the selected project.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsmiViewDetailsProject_Click(object sender, EventArgs e)
{
if (lvSummary.SelectedItems.Count > 0)
{
LineCountSummary summary = (LineCountSummary)lvSummary.SelectedItems[0].Tag;
ProjectDetails.Show(summary);
}
}
#endregion
#region Helpers
#region Line Counting Methods
/*
/// <summary>
/// Count each line in a text file, logging
/// blank lines.
/// </summary>
/// <param name="info">The file information data to use.</param>
private void CountLinesGeneric(LineCountInfo info)
{
StreamReader sr = new StreamReader(info.FileName);
string line;
while ((line = sr.ReadLine()) != null)
{
info.LineCountInfoDetails.Add("TotalLines", 1);
if (line.Trim() == string.Empty)
{
info.LineCountInfoDetails.Add("BlankLines", 1);
}
info.SumMode = "Generic";
}
sr.Close();
}
/// <summary>
/// Count each line in a c-style source file, scanning
/// for single and multi-line comments, code, and blank lines.
/// </summary>
/// <param name="info">The file information data to use.</param>
/// <remarks>This algorithm was originally created by Oz Solomon,
/// for his PLC line counter add-in for Visual Studio 2002/2003.</remarks>
private void CountLinesCStyle(LineCountInfo info)
{
try
{
StreamReader reader = new StreamReader(info.FileName);
string line;
bool multiLineComment = false;
bool hasCode = false;
bool hasComments = false;
while ((line = reader.ReadLine()) != null)
{
ParseCLine(line, ref multiLineComment, ref hasCode, ref hasComments);
if (hasComments)
{
info.LineCountInfoDetails.Add("normcmtlines", 1);
}
if (hasCode)
{
info.LineCountInfoDetails.Add("codelines", 1);
}
if (!hasCode && !hasComments)
{
info.LineCountInfoDetails.Add("blanklines", 1);
}
info.LineCountInfoDetails.Add("totallines", 1);
}
reader.Close();
info.SumMode = "C-Style";
}
catch
{
}
}
/// <summary>
/// Count each line in a vb-style source file, scanning
/// for comments, code, and blank lines.
/// </summary>
/// <param name="info">The file information data to use.</param>
/// <remarks>This algorithm was originally created by Oz Solomon,
/// for his PLC line counter add-in for Visual Studio 2002/2003.</remarks>
private void CountLinesVBStyle(LineCountInfo info)
{
try
{
StreamReader reader = new StreamReader(info.FileName);
string line;
bool multiLineComment = false;
bool hasCode = false;
bool hasComments = false;
while ((line = reader.ReadLine()) != null)
{
ParseVBLine(line, ref multiLineComment, ref hasCode, ref hasComments);
if (hasComments)
{
info.LineCountInfoDetails.Add("normcmtlines", 1);
}
if (hasCode)
{
info.LineCountInfoDetails.Add("codelines", 1);
}
if (!hasCode && !hasComments)
{
info.LineCountInfoDetails.Add("blanklines", 1);
}
info.LineCountInfoDetails.Add("totallines", 1);
}
reader.Close();
info.SumMode = "Visual Basic";
}
catch
{
}
}
/// <summary>
/// Count each line in an xml source file, scanning
/// for comments, code, and blank lines.
/// </summary>
/// <param name="info">The file information data to use.</param>
/// <remarks>This algorithm is based on one created by Oz Solomon,
/// for his PLC line counter add-in for Visual Studio 2002/2003.</remarks>
private void CountLinesXMLStyle(LineCountInfo info)
{
try
{
StreamReader reader = new StreamReader(info.FileName);
string line;
bool multiLineComment = false;
bool hasCode = false;
bool hasComments = false;
while ((line = reader.ReadLine()) != null)
{
ParseXMLLine(line, ref multiLineComment, ref hasCode, ref hasComments);
if (hasComments)
{
info.LineCountInfoDetails.Add("normcmtlines", 1);
}
if (hasCode)
{
info.LineCountInfoDetails.Add("codelines", 1);
}
if (!hasCode && !hasComments)
{
info.LineCountInfoDetails.Add("blanklines", 1);
}
info.LineCountInfoDetails.Add("totallines", 1);
}
reader.Close();
info.SumMode = "XML";
}
catch
{
}
}
/// <summary>
/// Determines if the two input characters ch and chNext
/// match the given pair of characters a and b.
/// </summary>
/// <param name="a">First char requirement.</param>
/// <param name="b">Second char requirement.</param>
/// <param name="ch">First char to test.</param>
/// <param name="chNext">Second char to test.</param>
/// <returns></returns>
private bool IsPair(char a, char b, char ch, char chNext)
{
return (ch == a && chNext == b);
}
/// <summary>
/// Parses a c-style code line for comments, code, and blanks.
/// </summary>
/// <param name="line"></param>
/// <param name="multiLineComment"></param>
/// <param name="hasCode"></param>
/// <param name="hasComments"></param>
/// <remarks>This algorithm was originally created by Oz Solomon,
/// for his PLC line counter add-in for Visual Studio 2002/2003.</remarks>
private void ParseCLine(string line, ref bool multiLineComment, ref bool hasCode, ref bool hasComments)
{
bool inString = false;
bool inTwoPairSequence = false;
hasComments = multiLineComment;
hasCode = false;
for (int i = 0; i < line.Length; i++)
{
char ch = line[i];
char chNext = (i < line.Length - 1 ? line[i + 1] : '\0');
// Process a single-line comment
if (IsPair('/', '/', ch, chNext) && !multiLineComment && !inString)
{
hasComments = true;
return;
}
// Process start of a multiline comment
else if (IsPair('/', '*', ch, chNext) && !multiLineComment && !inString)
{
multiLineComment = true;
hasComments = true;
++i;
}
// Process end of a multiline comment
else if (IsPair('*', '/', ch, chNext) && !inString)
{
multiLineComment = false;
++i;
}
// Process escaped character
else if (ch == '\\' && !multiLineComment)
{
++i;
hasCode = true;
}
// Process string
else if (ch == '"' && !multiLineComment)
{
inString = !inString;
hasCode = true;
}
else if (!multiLineComment)
{
if (!Char.IsWhiteSpace(ch))
{
hasCode = true;
}
}
}
}
/// <summary>
/// Parses a vb-style code line for comments, code and blanks.
/// </summary>
/// <param name="line"></param>
/// <param name="multiLineComment"></param>
/// <param name="hasCode"></param>
/// <param name="hasComments"></param>
/// <remarks>This algorithm was originally created by Oz Solomon,
/// for his PLC line counter add-in for Visual Studio 2002/2003.</remarks>
private void ParseVBLine(string line, ref bool multiLineComment, ref bool hasCode, ref bool hasComments)
{
bool inString = false;
bool inTwoPairSequence = false;
multiLineComment = false;
line = line.Trim();
if (line.Length == 0)
{
hasCode = false;
hasComments = false;
return;
}
if (line[0] == '\'')
{
hasCode = false;
hasComments = true;
return;
}
if (line.IndexOf('\'') != -1)
{
hasCode = true;
hasComments = true;
return;
}
hasCode = true;
hasComments = true;
}
/// <summary>
/// Parses an xml-style code line for comments, markup, and blanks.
/// </summary>
/// <param name="line"></param>
/// <param name="multiLineComment"></param>
/// <param name="hasCode"></param>
/// <param name="hasComments"></param>
/// <remarks>This algorithm is based on one created by Oz Solomon,
/// for his PLC line counter add-in for Visual Studio 2002/2003.</remarks>
private void ParseXMLLine(string line, ref bool multiLineComment, ref bool hasCode, ref bool hasComments)
{
bool inString = false;
bool inTwoPairSequence = false;
hasComments = multiLineComment;
hasCode = false;
for (int i = 0; i < line.Length; i++)
{
char ch1 = line[i];
char ch2 = (i < line.Length-1 ? line[i + 1] : '\0');
char ch3 = (i+1 < line.Length-1 ? line[i + 2] : '\0');
char ch4 = (i+2 < line.Length-1 ? line[i + 3] : '\0');
// Process start of XML comment
if (IsPair('<', '!', ch1, ch2) && IsPair('-', '-', ch3, ch4) && !multiLineComment && !inString)
{
multiLineComment = true;
hasComments = true;
i += 3;
}
// Process end of XML comment
else if (IsPair('-', '-', ch1, ch2) && ch3 == '>' && !inString)
{
multiLineComment = false;
i += 2;
}
// Process string
else if (ch3 == '"' && !multiLineComment)
{
inString = !inString;
hasCode = true;
}
else if (!multiLineComment)
{
if (!Char.IsWhiteSpace(ch1))
{
hasCode = true;
}
}
}
}
*/
#endregion
#region Scanning and Summation Methods
/// <summary>
/// Scans the solution and creates a hierarchy of
/// support objects for each project and file
/// within each project.
/// </summary>
private void ScanSolution()
{
if (m_dte == null)
{
MessageBox.Show("The Visual Studio Design Time Environment was not found.", "Line Counter");
return;
}
if (m_summaryList == null)
m_summaryList = new List<LineCountSummary>();
m_summaryList.Clear();
Solution2 solution = (Solution2)m_dte.Solution;
if (solution.IsOpen)
{
FileInfo fiSolution = new FileInfo(solution.FullName);
LineCountSummary summary = new LineCountSummary("All Projects", m_cfgMgr.MapProjectIconIndex("{00000000-0000-0000-0000-000000000000}", imgProjectTypes) /*m_projIconMappings["{00000000-0000-0000-0000-000000000000}"]*/);
m_summaryList.Add(summary);
// Configure progress bars
tsprgTotal.Minimum = 0;
tsprgTotal.Step = 1;
tsprgTask.Minimum = 0;
tsprgTask.Step = 1;
Projects projects = solution.Projects;
tsprgTotal.Maximum = projects.Count;
tsprgTask.Value = 0;
ScanProjects(projects, summary);
tsprgTask.Value = tsprgTask.Maximum;
tsprgTotal.Value = tsprgTotal.Maximum;
}
else
{
//MessageBox.Show("There is no solution open in Visual Studio.", "Line Counter");
}
}
/// <summary>
/// Scans all of the Project objects in a
/// Projects collection. Properly recognizes
/// solution folders, and recursively processes
/// any files, projects, and child solution
/// folders within.
/// </summary>
/// <param name="projects">The Projects collection to process.</param>
/// <param name="allSummary">The summary object for all projects.</param>
private void ScanProjects(Projects projects, LineCountSummary allSummary)
{
for (int p = 0; p < projects.Count; p++)
{
tsprgTotal.PerformStep();
Project project = projects.Item(p + 1);
Debug.WriteLine("ScanProjects():Project.Kind = " + project.Kind);
if (project.Kind == ProjectKinds.vsProjectKindSolutionFolder)
{
ScanSolutionFolder(project, allSummary);
}
else if (project.Kind == "{67294A52-A4F0-11D2-AA88-00C04F688DDE}")
{
// Unavailable project...skip
}
else
{
ScanProject(project, allSummary);
}
}
}
/// <summary>
/// Recursively scans a solution folder item
/// in a solution. Will handle any level of nested
/// solution folders, and all of the projects
/// and files within them.
/// </summary>
/// <param name="project">The Project object representing a solution folder.</param>
/// <param name="allSummary">The summary object for all projects.</param>
private void ScanSolutionFolder(Project project, LineCountSummary allSummary)
{
ProjectItems items = project.ProjectItems;
tsprgTotal.Maximum += items.Count;
for (int i = 0; i < items.Count; i++)
{
tsprgTotal.PerformStep();
ProjectItem item = items.Item(i + 1);
Debug.WriteLine("ScanSolutionFolder():ProjectItem.Kind = " + item.Kind);
if (item.Kind == "{66A26722-8FB5-11D2-AA7E-00C04F688DDE}")
{
Project subProj = (Project)item.Object;
if (subProj.Kind == "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}")
{
ScanSolutionFolder(subProj, allSummary);
}
else if (subProj.Kind == "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}")
{
ScanProject(subProj, allSummary);
}
}
else if (item.Kind == "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}")
{
Project solnFolder = (Project)item.Object;
ScanSolutionFolder(solnFolder, allSummary);
}
}
ScanProjectItems(items, allSummary);
}
/// <summary>
/// Scans a single Project object for all of the
/// countable ProjectItem objects inside.
/// </summary>
/// <param name="project">The Project object to scan.</param>
/// <param name="allSummary">The summary object for all projects.</param>
private void ScanProject(Project project, LineCountSummary allSummary)
{
string projName = "";
string lang = "{00000000-0000-0000-0000-000000000000}";
try
{
//if (File.Exists(project.FullName))
//{
if (project.FullName.IndexOf("://") != -1)
{
Uri uri = new Uri(project.FullName);
projName = project.FullName;
lang = "{00000001-0000-0000-0000-000000000000}";
}
else
{
FileInfo fiProject = new FileInfo(project.FullName);
projName = fiProject.Name;
CodeModel codeModel = project.CodeModel;
lang = codeModel.Language;
}
LineCountSummary summary = new LineCountSummary(projName, m_cfgMgr.MapProjectIconIndex(lang, imgProjectTypes) /*m_projIconMappings[lang]*/);
summary.ReferencedProject = project;
m_summaryList.Add(summary);
ProjectItems projectItems = project.ProjectItems;
tsprgTask.Maximum = 0;
tsprgTotal.Value = 0;
ScanProjectItems(projectItems, summary);
//}
}
catch (Exception ex)
{
Debug.WriteLine("ScanProject():" + ex.Message);
Debug.WriteLine("ScanProject():" + ex.StackTrace);
}
}
/// <summary>
/// Scans the project items (files, usually) of
/// a project's ProjectItems collection. Recursively
/// calls itself for nested files in the solution,
/// such as a Form.cs/Form.Designer.cs+Form.resx
/// set in Visual Studio 2005.
/// </summary>
/// <param name="projectItems">The ProjectItems collection to scan.</param>
/// <param name="summary">The root summary data object that these
/// files belong to.</param>
private void ScanProjectItems(ProjectItems projectItems, LineCountSummary summary)
{
if (projectItems == null)
return;
// {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} = Project.Kind -> C# Project
// {E24C65DC-7377-472b-9ABA-BC803B73C61A} = Project.Kind -> Web Project
if (projectItems.Count == 0)
return;
tsprgTask.Maximum += projectItems.Count;
for (int i = 0; i < projectItems.Count; i++)
{
tsprgTask.PerformStep();
ProjectItem projectItem = projectItems.Item(i + 1);
Debug.WriteLine("ScanProjectItems():ProjectItem.Name=" + projectItem.Name);
Debug.WriteLine("ScanProjectItems():ProjectItem.Kind=" + projectItem.Kind);
// Skip solution folders and project nodes
if (projectItem.Kind == "{66A26722-8FB5-11D2-AA7E-00C04F688DDE}" || projectItem.Kind == "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}")
continue;
// Process if item is not a directory
if (projectItem.Kind != "{6BB5F8EF-4483-11D3-8BCF-00C04F8EC28C}")
{
for (short f = 0; f < projectItem.FileCount; f++)
{
try
{
string projectFile = projectItem.get_FileNames(f);
if (!Directory.Exists(projectFile))
{
int iconIndex = m_cfgMgr.MapFileTypeIconIndex(Path.GetExtension(projectFile), imgFileTypes); //m_fileIconMappings[Path.GetExtension(projectFile)];
LineCountInfo lineCountInfo = new LineCountInfo(projectFile, iconIndex, summary);
summary.FileLineCountInfo.Add(lineCountInfo);
}
}
catch (Exception ex)
{
Debug.WriteLine("ScanProjectItems():Exception:" + ex.Message);
Debug.WriteLine("ScanProjectItems():Exception:" + ex.StackTrace);
}
}
}
ScanProjectItems(projectItem.ProjectItems, summary);
}
}
/// <summary>
/// Performs a complete counting and summation of all lines
/// in all projects and files.
/// </summary>
private void SumSolution()
{
try
{
lvSummary.BeginUpdate();
lvFileList.BeginUpdate();
// Clean the list
lvSummary.Items.Clear();
lvFileList.Items.Clear();
lvFileList.Groups.Clear();
// Configure progress bars
tsprgTotal.Minimum = 0;
tsprgTotal.Step = 1;
tsprgTask.Minimum = 0;
tsprgTask.Step = 1;
// Skip if there are no projects
if (m_summaryList == null || (m_summaryList != null && m_summaryList.Count == 1))
{
MessageBox.Show("There are no projects loaded to summarize.", "Line Counter");
return;
}
// Get all projects summary
LineCountSummary allProjects = m_summaryList[0];
allProjects.LineCountSummaryDetails.Reset();
AddSummaryListItem(allProjects, lvSummary.Groups["lvgAllProj"]);
tsprgTotal.Maximum = m_summaryList.Count;
tsprgTotal.Value = 0;
for (int s = 1; s < m_summaryList.Count; s++)
{
tsprgTotal.PerformStep();
LineCountSummary summary = m_summaryList[s];
summary.LineCountSummaryDetails.Reset();
AddSummaryListItem(summary, lvSummary.Groups["lvgEachProj"]);
tsprgTask.Maximum = summary.FileLineCountInfo.Count;
tsprgTask.Value = 0;
for (int i = 0; i < summary.FileLineCountInfo.Count; i++)
{
tsprgTask.PerformStep();
LineCountInfo info = summary.FileLineCountInfo[i];
// Count lines
if (m_cfgMgr.IsFor(info.FileType, "count") /*m_countableTypes.Contains(info.FileType)*/)
{
info.LineCountInfoDetails.Reset();
CountParserDelegate counter = MapCountAlgorithm(info.FileType); //m_countAlgorithms[info.FileType];
counter(info);
info.LineCountInfoDetails.Summarize();
allProjects.LineCountSummaryDetails.Add(info.LineCountInfoDetails);
summary.LineCountSummaryDetails.Add(info.LineCountInfoDetails);
tstxtLinesCounted.Text = allProjects.LineCountSummaryDetails.TotalLines.ToString();
AddFileListItem(info);
}
// Track file volume
if (m_cfgMgr.IsFor(info.FileType, "volume"))
{
summary.FileVolumeDetails.Add(info.FileType, 1);
allProjects.FileVolumeDetails.Add(info.FileType, 1);
}
}
summary.LineCountSummaryDetails.Summarize();
LineCountDetails details = summary.LineCountSummaryDetails;
summary.LinkedListViewItem.SubItems[1].Text = details.TotalLines.ToString();
summary.LinkedListViewItem.SubItems[2].Text = details.CodeLines.ToString();
summary.LinkedListViewItem.SubItems[3].Text = details.CommentLines.ToString();
summary.LinkedListViewItem.SubItems[4].Text = details.BlankLines.ToString();
summary.LinkedListViewItem.SubItems[5].Text = details.NetLines.ToString();
details = null;
}
allProjects.LineCountSummaryDetails.Summarize();
LineCountDetails totals = allProjects.LineCountSummaryDetails;
allProjects.LinkedListViewItem.SubItems[1].Text = totals.TotalLines.ToString();
allProjects.LinkedListViewItem.SubItems[2].Text = totals.CodeLines.ToString();
allProjects.LinkedListViewItem.SubItems[3].Text = totals.CommentLines.ToString();
allProjects.LinkedListViewItem.SubItems[4].Text = totals.BlankLines.ToString();
allProjects.LinkedListViewItem.SubItems[5].Text = totals.NetLines.ToString();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
Debug.WriteLine(ex.StackTrace);
}
tsprgTotal.Value = tsprgTotal.Maximum;
lvSummary.EndUpdate();
lvFileList.EndUpdate();
}
private CountParserDelegate MapCountAlgorithm(string extension)
{
CountParserDelegate countParser = m_cfgMgr.MapCountParser("countLinesGeneric");
string method = m_cfgMgr.AllowedMethod(extension, "count", 0);
if (method != null)
{
CountParserDelegate tempParser = m_cfgMgr.MapCountParser(method);
if (tempParser != null)
countParser = tempParser;
}
return countParser;
}
/// <summary>
/// Adds a summary item to the projects list view.
/// </summary>
/// <param name="summary">The summary data object to reference.</param>
/// <param name="group">The summary list view group this item
/// should be listed under.</param>
private void AddSummaryListItem(LineCountSummary summary, ListViewGroup group)
{
ListViewItem lvi = new ListViewItem();
lvi.Text = summary.ProjectName;
lvi.SubItems.Add("0");
lvi.SubItems.Add("0");
lvi.SubItems.Add("0");
lvi.SubItems.Add("0");
lvi.SubItems.Add("0");
lvi.Tag = summary;
lvi.ImageIndex = summary.IconIndex;
//lvi.StateImageIndex = summary.IconIndex;
lvi.Group = group;
summary.LinkedListViewItem = lvi;
lvSummary.Items.Add(lvi);
}
/// <summary>
/// Adds a file information item to the file list view.
/// </summary>
/// <param name="info">The file information data object.</param>
private void AddFileListItem(LineCountInfo info)
{
FileInfo fileInfo = new FileInfo(info.FileName);
ListViewItem lvi = new ListViewItem();
lvi.Text = fileInfo.Name;
lvi.SubItems.Add(info.LineCountInfoDetails.TotalLines.ToString());
lvi.SubItems.Add(info.LineCountInfoDetails.CodeLines.ToString());
lvi.SubItems.Add(info.LineCountInfoDetails.CommentLines.ToString());
lvi.SubItems.Add(info.FileType);
lvi.SubItems.Add(info.SumMode);
lvi.Tag = info;
// Add image index setting
int iconIndex = info.IconIndex;// m_cfgMgr.MapProjectIconIndex(info.FileType, imgFileTypes); //m_fileIconMappings[info.FileType];
lvi.ImageIndex = iconIndex;
//lvi.StateImageIndex = iconIndex;
if (tsmiGroupByType.Checked)
{
ListViewGroup group = lvFileList.Groups["groupType" + info.FileType.Substring(1)];
if (group == null)
{
group = new ListViewGroup("groupType" + info.FileType.Substring(1), info.FileType.Substring(1).ToUpper() + " Files");
lvFileList.Groups.Add(group);
}
lvi.Group = group;
}
else if (tsmiGroupByProj.Checked)
{
ListViewGroup group = lvFileList.Groups["groupProj" + info.ProjectSummary.ProjectName];
if (group == null)
{
group = new ListViewGroup("groupProj" + info.ProjectSummary.ProjectName, info.ProjectSummary.ProjectName + " Files");
lvFileList.Groups.Add(group);
}
lvi.Group = group;
}
lvFileList.Items.Add(lvi);
}
private void GatherTMVStatistics()
{
}
private void ReGroupFileListing()
{
lvFileList.Groups.Clear();
lvFileList.BeginUpdate();
for (int i = 0; i < lvFileList.Items.Count; i++)
{
ListViewItem lvi = lvFileList.Items[i];
LineCountInfo info = (LineCountInfo)lvi.Tag;
if (tsmiGroupByType.Checked)
{
ListViewGroup group = lvFileList.Groups["groupType" + info.FileType.Substring(1)];
if (group == null)
{
group = new ListViewGroup("groupType" + info.FileType.Substring(1), info.FileType.Substring(1).ToUpper() + " Files");
lvFileList.Groups.Add(group);
}
lvi.Group = group;
}
else if (tsmiGroupByProj.Checked)
{
ListViewGroup group = lvFileList.Groups["groupProj" + info.ProjectSummary.ProjectName];
if (group == null)
{
group = new ListViewGroup("groupProj" + info.ProjectSummary.ProjectName, info.ProjectSummary.ProjectName + " Files");
lvFileList.Groups.Add(group);
}
lvi.Group = group;
}
}
lvFileList.EndUpdate();
}
#endregion
#endregion
private void lvSummary_DoubleClick(object sender, EventArgs e)
{
}
private void lvSummary_ItemActivate(object sender, EventArgs e)
{
tsmiViewDetailsProject_Click(sender, e);
}
private void tsmiTypeMemberVolume_Click(object sender, EventArgs e)
{
}
}
}