diff --git a/Common/Product/SharedProject/ProjectNode.cs b/Common/Product/SharedProject/ProjectNode.cs index eb8d110d8..492a05b2a 100644 --- a/Common/Product/SharedProject/ProjectNode.cs +++ b/Common/Product/SharedProject/ProjectNode.cs @@ -423,6 +423,12 @@ public MSBuildExecution.ProjectInstance CurrentConfig { } } + public Dictionary DiskNodes { + get { + return _diskNodes; + } + } + #region overridden properties public override bool CanOpenCommandPrompt { diff --git a/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs b/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs index 059b0ed51..f26d253a0 100644 --- a/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs +++ b/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs @@ -15,6 +15,7 @@ //*********************************************************// using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; @@ -26,7 +27,7 @@ namespace Microsoft.NodejsTools.Commands { internal sealed class DiagnosticsCommand : Command { - private static readonly string[] interestingDteProperties = new[] { + private static readonly string[] _interestingDteProperties = new[] { "StartupFile", "WorkingDirectory", "PublishUrl", @@ -34,6 +35,15 @@ internal sealed class DiagnosticsCommand : Command { "CommandLineArguments" }; + private static readonly string[] _interestingFileExtensions = new[] { + ".js", + ".jsx", + ".tsx", + ".d.ts", + ".ts", + ".html" + }; + public DiagnosticsCommand(IServiceProvider serviceProvider) { } public override int CommandId { @@ -75,7 +85,7 @@ private static string GetSolutionInfo() { res.AppendLine("Projects:"); foreach (EnvDTE.Project project in dte.Solution.Projects) { - res.AppendLine(Indent(4, GetProjectInfo(project))); + res.AppendLine(Indent(1, GetProjectInfo(project))); } return res.ToString(); @@ -105,12 +115,12 @@ private static string GetProjectInfo(EnvDTE.Project project) { // about the exception. We'll add it to the output, // rather than crashing. res.AppendLine("Project: " + ex.Message); - res.AppendLine(Indent(8, "Kind: Node.js")); + res.AppendLine(Indent(2, "Kind: Node.js")); } return res.ToString(); } res.AppendLine("Project: " + name); - res.AppendLine(Indent(4, GetProjectPropertiesInfo(project))); + res.AppendLine(Indent(1, GetProjectPropertiesInfo(project))); return res.ToString(); } @@ -118,7 +128,7 @@ private static string GetProjectPropertiesInfo(EnvDTE.Project project) { var res = new StringBuilder(); if (Utilities.GuidEquals(Guids.NodejsBaseProjectFactoryString, project.Kind)) { res.AppendLine("Kind: Node.js"); - foreach (var prop in interestingDteProperties) { + foreach (var prop in _interestingDteProperties) { res.AppendLine(prop + ": " + GetProjectProperty(project, prop)); } var njsProj = project.GetNodejsProject(); @@ -141,9 +151,85 @@ private static string GetNodeJsProjectProperties(Project.NodejsProjectNode proje jsAnalyzer.DumpLog(writer); } } + res.AppendLine(GetProjectNpmInfo(project)); + res.AppendLine(GetProjectFileInfo(project)); + return res.ToString(); + } + + /// + /// Stores information about a collection of files of a given type. + /// + private class FileTypeInfo { + private int _count = 0; + private int _maxLineLength = 0; + private int _totalLineLength = 0; + + public int Count { + get { return _count; } + } + + public int MaxLineLength { + get { return _maxLineLength; } + } + + public int AverageLineLength { + get { return _count > 0 ? _totalLineLength / _count : 0; } + } + + public void UpdateForFile(string file) { + try { + int length = File.ReadLines(file).Count(); + ++_count; + _totalLineLength += length; + _maxLineLength = Math.Max(_maxLineLength, length); + } catch (IOException) { + // noop + } + } + } + + private static string GetProjectFileInfo(Project.NodejsProjectNode project) { + var fileTypeInfo = new Dictionary(); + foreach (var node in project.DiskNodes) { + var file = node.Key; + var matchedExt = _interestingFileExtensions.Where(ext => file.EndsWith(ext, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); + + if (!string.IsNullOrEmpty(matchedExt)) { + var recordKey = string.Format("{0} ({1})", matchedExt, node.Value.ItemNode?.IsExcluded ?? true ? "excluded from project" : "included in project"); + FileTypeInfo record; + if (!fileTypeInfo.TryGetValue(recordKey, out record)) { + record = fileTypeInfo[recordKey] = new FileTypeInfo(); + } + record.UpdateForFile(file); + } + } + + var res = new StringBuilder(); + + res.AppendLine("Project Info:"); + foreach (var entry in fileTypeInfo) { + res.AppendLine(Indent(1, entry.Key + ":")); + res.AppendLine(Indent(2, "Number of Files: " + entry.Value.Count)); + res.AppendLine(Indent(2, "Average Line Count: " + entry.Value.AverageLineLength)); + res.AppendLine(Indent(2, "Max Line Count: " + entry.Value.MaxLineLength)); + } return res.ToString(); } + + private static string GetProjectNpmInfo(Project.NodejsProjectNode project) { + var modules = project?.ModulesNode?.RootPackage?.Modules; + if (modules == null) { + return ""; + } + + var res = new StringBuilder(); + res.AppendLine("Npm Info:"); + res.AppendLine(Indent(1, string.Format("Number of Top Level Modules: " + modules.Count()))); + + return res.ToString(); + } + private static string GetProjectProperty(EnvDTE.Project project, string name) { try { var item = project.Properties.Item(name); @@ -167,7 +253,7 @@ private static string GetEventsAndStatsInfo() { if (ex.IsCriticalException()) { throw; } - res.AppendLine(Indent(4, "Failed to access event log.")); + res.AppendLine(Indent(1, "Failed to access event log.")); res.AppendLine(ex.ToString()); } return res.ToString(); @@ -179,7 +265,7 @@ private static string GetLoadedAssemblyInfo() { foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().OrderBy(assem => assem.FullName)) { var assemFileVersion = assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false).OfType().FirstOrDefault(); - res.AppendLine(Indent(4, string.Format("{0}, FileVersion={1}", + res.AppendLine(Indent(1, string.Format("{0}, FileVersion={1}", assembly.FullName, assemFileVersion == null ? "(null)" : assemFileVersion.Version))); } @@ -204,7 +290,7 @@ private static string GetAnalysisLevelInfo() { } private static string Indent(int count, string text) { - var indent = new string(' ', count); + var indent = new string(' ', count * 4); var lines = text.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); var indentedText = lines.Select(line => string.IsNullOrWhiteSpace(line) ? line : indent + line);