From 4ecf6fc168b55cc6d5304c130939a5b0d3f9edc1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 30 Jun 2016 14:56:26 -0700 Subject: [PATCH 1/6] Add Additional Project info to diagnostics Window Bug For perf investigations, we would like to be able to understand what type of projects users are working with. Fix Add some basic info to the Diagnostics window. This info capture the types of tiles in a project and the average file length for each of these. --- Common/Product/SharedProject/ProjectNode.cs | 6 ++ .../Nodejs/Commands/DiagnosticsCommand.cs | 66 +++++++++++++++++++ 2 files changed, 72 insertions(+) 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..cb7bdffd5 100644 --- a/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs +++ b/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs @@ -23,6 +23,7 @@ using Microsoft.NodejsTools.Logging; using Microsoft.VisualStudioTools; using Microsoft.VisualStudioTools.Project; +using System.Collections.Generic; namespace Microsoft.NodejsTools.Commands { internal sealed class DiagnosticsCommand : Command { @@ -131,6 +132,7 @@ private static string GetProjectPropertiesInfo(EnvDTE.Project project) { return res.ToString(); } + private static string GetNodeJsProjectProperties(Project.NodejsProjectNode project) { var res = new StringBuilder(); @@ -141,9 +143,73 @@ private static string GetNodeJsProjectProperties(Project.NodejsProjectNode proje jsAnalyzer.DumpLog(writer); } } + res.AppendLine("Files:"); + res.AppendLine(Indent(4, 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) { + if (node.Value.ItemNode?.IsExcluded ?? true) { + continue; + } + var file = node.Key; + var ext = Path.GetExtension(file).ToLowerInvariant(); + if (string.IsNullOrWhiteSpace(ext)) { + continue; + } + + FileTypeInfo record; + if (!fileTypeInfo.TryGetValue(ext, out record)) { + record = fileTypeInfo[ext] = new FileTypeInfo(); + } + record.UpdateForFile(file); + } + + var res = new StringBuilder(); + foreach (var entry in fileTypeInfo) { + res.AppendLine(entry.Key + ":"); + res.AppendLine(Indent(4, "Number of Files: " + entry.Value.Count)); + res.AppendLine(Indent(4, "Average Line Count: " + entry.Value.AverageLineLength)); + res.AppendLine(Indent(4, "Max Line Count: " + entry.Value.MaxLineLength)); + } return res.ToString(); } + private static string GetProjectProperty(EnvDTE.Project project, string name) { try { var item = project.Properties.Item(name); From ab2b001bf90e587081849b1b685e5ad75cded9f1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Jul 2016 13:24:15 -0700 Subject: [PATCH 2/6] using order --- Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs b/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs index cb7bdffd5..981ec1991 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; @@ -23,7 +24,6 @@ using Microsoft.NodejsTools.Logging; using Microsoft.VisualStudioTools; using Microsoft.VisualStudioTools.Project; -using System.Collections.Generic; namespace Microsoft.NodejsTools.Commands { internal sealed class DiagnosticsCommand : Command { From 2cc68c133e1b9a3222b077cf6f5d3e98e0a54a23 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Jul 2016 17:37:27 -0700 Subject: [PATCH 3/6] Pivot based on included / excluded from project --- .../Nodejs/Commands/DiagnosticsCommand.cs | 53 +++++++++++-------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs b/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs index 981ec1991..c2e4c25a0 100644 --- a/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs +++ b/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs @@ -35,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 { @@ -76,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(); @@ -106,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(); } @@ -143,8 +152,7 @@ private static string GetNodeJsProjectProperties(Project.NodejsProjectNode proje jsAnalyzer.DumpLog(writer); } } - res.AppendLine("Files:"); - res.AppendLine(Indent(4, GetProjectFileInfo(project))); + res.AppendLine(GetProjectFileInfo(project)); return res.ToString(); } @@ -183,28 +191,27 @@ public void UpdateForFile(string file) { private static string GetProjectFileInfo(Project.NodejsProjectNode project) { var fileTypeInfo = new Dictionary(); foreach (var node in project.DiskNodes) { - if (node.Value.ItemNode?.IsExcluded ?? true) { - continue; - } var file = node.Key; - var ext = Path.GetExtension(file).ToLowerInvariant(); - if (string.IsNullOrWhiteSpace(ext)) { - continue; - } + var matchedExt = interestingFileExtensions.Where(ext => file.EndsWith(ext, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); - FileTypeInfo record; - if (!fileTypeInfo.TryGetValue(ext, out record)) { - record = fileTypeInfo[ext] = new FileTypeInfo(); + 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); } - record.UpdateForFile(file); } var res = new StringBuilder(); + + res.AppendLine("Project Info:"); foreach (var entry in fileTypeInfo) { - res.AppendLine(entry.Key + ":"); - res.AppendLine(Indent(4, "Number of Files: " + entry.Value.Count)); - res.AppendLine(Indent(4, "Average Line Count: " + entry.Value.AverageLineLength)); - res.AppendLine(Indent(4, "Max Line Count: " + entry.Value.MaxLineLength)); + 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(); @@ -233,7 +240,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(); @@ -245,7 +252,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))); } @@ -270,7 +277,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); From 59edebc2da9a4bf1e7c22ad460415d2c091a04b1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 6 Jul 2016 11:34:22 -0700 Subject: [PATCH 4/6] Add basic node module info --- .../Product/Nodejs/Commands/DiagnosticsCommand.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs b/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs index c2e4c25a0..4e841a414 100644 --- a/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs +++ b/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs @@ -152,6 +152,7 @@ private static string GetNodeJsProjectProperties(Project.NodejsProjectNode proje jsAnalyzer.DumpLog(writer); } } + res.AppendLine(GetProjectNpmInfo(project)); res.AppendLine(GetProjectFileInfo(project)); return res.ToString(); } @@ -217,6 +218,19 @@ private static string GetProjectFileInfo(Project.NodejsProjectNode project) { 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); From 8b2fb06da394d6ca4a91797c12e9deff6e477d7d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 6 Jul 2016 11:35:55 -0700 Subject: [PATCH 5/6] Remove extra space --- Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs b/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs index 4e841a414..73e11973c 100644 --- a/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs +++ b/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs @@ -141,7 +141,6 @@ private static string GetProjectPropertiesInfo(EnvDTE.Project project) { return res.ToString(); } - private static string GetNodeJsProjectProperties(Project.NodejsProjectNode project) { var res = new StringBuilder(); From 3b34a416d1163cb23bc80d93aad347dd546c6428 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 7 Jul 2016 11:09:01 -0700 Subject: [PATCH 6/6] Prepend vars with _ --- Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs b/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs index 73e11973c..f26d253a0 100644 --- a/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs +++ b/Nodejs/Product/Nodejs/Commands/DiagnosticsCommand.cs @@ -27,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", @@ -35,7 +35,7 @@ internal sealed class DiagnosticsCommand : Command { "CommandLineArguments" }; - private static readonly string[] interestingFileExtensions = new[] { + private static readonly string[] _interestingFileExtensions = new[] { ".js", ".jsx", ".tsx", @@ -128,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(); @@ -192,7 +192,7 @@ 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(); + 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");