diff --git a/CodeDocumentor.Analyzers/Analyzers/BaseAnalyzerSettings.cs b/CodeDocumentor.Analyzers/Analyzers/BaseAnalyzerSettings.cs index 00a06dd..960c708 100644 --- a/CodeDocumentor.Analyzers/Analyzers/BaseAnalyzerSettings.cs +++ b/CodeDocumentor.Analyzers/Analyzers/BaseAnalyzerSettings.cs @@ -1,10 +1,10 @@ -using CodeDocumentor.Analyzers.Locators; using CodeDocumentor.Common; using CodeDocumentor.Common.Helpers; using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; -namespace CodeDocumentor.Analyzers +namespace CodeDocumentor.Analyzers.Analyzers { public class BaseAnalyzerSettings { diff --git a/CodeDocumentor.Analyzers/Analyzers/Classes/ClassAnalyzer.cs b/CodeDocumentor.Analyzers/Analyzers/Classes/ClassAnalyzer.cs index 0c16bc9..54b69cb 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Classes/ClassAnalyzer.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Classes/ClassAnalyzer.cs @@ -1,13 +1,13 @@ using System.Collections.Immutable; using CodeDocumentor.Analyzers.Builders; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Analyzers.Classes +namespace CodeDocumentor.Analyzers.Analyzers.Classes { /// /// The class analyzer. diff --git a/CodeDocumentor.Analyzers/Analyzers/Classes/ClassAnalyzerSettings.cs b/CodeDocumentor.Analyzers/Analyzers/Classes/ClassAnalyzerSettings.cs index 7ee046d..8ee4972 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Classes/ClassAnalyzerSettings.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Classes/ClassAnalyzerSettings.cs @@ -2,7 +2,7 @@ using CodeDocumentor.Common.Interfaces; using Microsoft.CodeAnalysis; -namespace CodeDocumentor.Analyzers +namespace CodeDocumentor.Analyzers.Analyzers.Classes { public class ClassAnalyzerSettings : BaseAnalyzerSettings { diff --git a/CodeDocumentor.Analyzers/Analyzers/Classes/NonPublicClassAnalyzer.cs b/CodeDocumentor.Analyzers/Analyzers/Classes/NonPublicClassAnalyzer.cs index 4ffc579..b040a33 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Classes/NonPublicClassAnalyzer.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Classes/NonPublicClassAnalyzer.cs @@ -1,13 +1,13 @@ using System.Collections.Immutable; using CodeDocumentor.Analyzers.Builders; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Analyzers.Classes +namespace CodeDocumentor.Analyzers.Analyzers.Classes { /// /// The class analyzer. diff --git a/CodeDocumentor.Analyzers/Analyzers/Constructors/ConstructorAnalyzer.cs b/CodeDocumentor.Analyzers/Analyzers/Constructors/ConstructorAnalyzer.cs index 58ff751..5cecb99 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Constructors/ConstructorAnalyzer.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Constructors/ConstructorAnalyzer.cs @@ -1,13 +1,13 @@ using System.Collections.Immutable; using CodeDocumentor.Analyzers.Builders; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Analyzers.Constructors +namespace CodeDocumentor.Analyzers.Analyzers.Constructors { /// /// The constructor analyzer. diff --git a/CodeDocumentor.Analyzers/Analyzers/Constructors/ConstructorAnalyzerSettings.cs b/CodeDocumentor.Analyzers/Analyzers/Constructors/ConstructorAnalyzerSettings.cs index 514f34e..17229b8 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Constructors/ConstructorAnalyzerSettings.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Constructors/ConstructorAnalyzerSettings.cs @@ -2,7 +2,7 @@ using CodeDocumentor.Common.Interfaces; using Microsoft.CodeAnalysis; -namespace CodeDocumentor.Analyzers +namespace CodeDocumentor.Analyzers.Analyzers.Constructors { public class ConstructorAnalyzerSettings : BaseAnalyzerSettings { diff --git a/CodeDocumentor.Analyzers/Analyzers/Constructors/NonPublicConstructorAnalyzer.cs b/CodeDocumentor.Analyzers/Analyzers/Constructors/NonPublicConstructorAnalyzer.cs index c40c653..ee5657a 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Constructors/NonPublicConstructorAnalyzer.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Constructors/NonPublicConstructorAnalyzer.cs @@ -1,13 +1,13 @@ using System.Collections.Immutable; using CodeDocumentor.Analyzers.Builders; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Analyzers.Constructors +namespace CodeDocumentor.Analyzers.Analyzers.Constructors { [DiagnosticAnalyzer(LanguageNames.CSharp)] public class NonPublicConstructorAnalyzer : DiagnosticAnalyzer diff --git a/CodeDocumentor.Analyzers/Analyzers/Enums/EnumAnalyzer.cs b/CodeDocumentor.Analyzers/Analyzers/Enums/EnumAnalyzer.cs index 7e2b8cb..e9d628f 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Enums/EnumAnalyzer.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Enums/EnumAnalyzer.cs @@ -1,12 +1,12 @@ using System.Collections.Immutable; using CodeDocumentor.Analyzers.Builders; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Analyzers.Enums +namespace CodeDocumentor.Analyzers.Analyzers.Enums { /// /// The enum analyzer. diff --git a/CodeDocumentor.Analyzers/Analyzers/Enums/EnumAnalyzerSettings.cs b/CodeDocumentor.Analyzers/Analyzers/Enums/EnumAnalyzerSettings.cs index a234e20..697a7ce 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Enums/EnumAnalyzerSettings.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Enums/EnumAnalyzerSettings.cs @@ -2,7 +2,7 @@ using CodeDocumentor.Common.Interfaces; using Microsoft.CodeAnalysis; -namespace CodeDocumentor.Analyzers +namespace CodeDocumentor.Analyzers.Analyzers.Enums { public class EnumAnalyzerSettings : BaseAnalyzerSettings { diff --git a/CodeDocumentor.Analyzers/Analyzers/Fields/FieldAnalyzer.cs b/CodeDocumentor.Analyzers/Analyzers/Fields/FieldAnalyzer.cs index 9f26647..5c66c5a 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Fields/FieldAnalyzer.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Fields/FieldAnalyzer.cs @@ -1,14 +1,14 @@ using System.Collections.Immutable; using System.Linq; using CodeDocumentor.Analyzers.Builders; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Analyzers.Fields +namespace CodeDocumentor.Analyzers.Analyzers.Fields { /// /// The field analyzer. diff --git a/CodeDocumentor.Analyzers/Analyzers/Fields/FieldAnalyzerSettings.cs b/CodeDocumentor.Analyzers/Analyzers/Fields/FieldAnalyzerSettings.cs index 01a9242..8438494 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Fields/FieldAnalyzerSettings.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Fields/FieldAnalyzerSettings.cs @@ -2,7 +2,7 @@ using CodeDocumentor.Common.Interfaces; using Microsoft.CodeAnalysis; -namespace CodeDocumentor.Analyzers +namespace CodeDocumentor.Analyzers.Analyzers.Fields { public class FieldAnalyzerSettings : BaseAnalyzerSettings { diff --git a/CodeDocumentor.Analyzers/Analyzers/Fields/NonPublicFieldAnalyzer.cs b/CodeDocumentor.Analyzers/Analyzers/Fields/NonPublicFieldAnalyzer.cs index 8e1957f..102cfc7 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Fields/NonPublicFieldAnalyzer.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Fields/NonPublicFieldAnalyzer.cs @@ -1,14 +1,14 @@ using System.Collections.Immutable; using System.Linq; using CodeDocumentor.Analyzers.Builders; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Analyzers.Fields +namespace CodeDocumentor.Analyzers.Analyzers.Fields { [DiagnosticAnalyzer(LanguageNames.CSharp)] public class NonPublicFieldAnalyzer : DiagnosticAnalyzer diff --git a/CodeDocumentor.Analyzers/Analyzers/Files/FileAnalyzer.cs b/CodeDocumentor.Analyzers/Analyzers/Files/FileAnalyzer.cs index 829352e..d8659e6 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Files/FileAnalyzer.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Files/FileAnalyzer.cs @@ -3,7 +3,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Analyzers.Files +namespace CodeDocumentor.Analyzers.Analyzers.Files { /// /// The class analyzer. diff --git a/CodeDocumentor.Analyzers/Analyzers/Files/FileAnalyzerSettings.cs b/CodeDocumentor.Analyzers/Analyzers/Files/FileAnalyzerSettings.cs index 56cfedf..94af3c1 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Files/FileAnalyzerSettings.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Files/FileAnalyzerSettings.cs @@ -1,7 +1,7 @@ using CodeDocumentor.Common; using Microsoft.CodeAnalysis; -namespace CodeDocumentor.Analyzers +namespace CodeDocumentor.Analyzers.Analyzers.Files { public class FileAnalyzerSettings : BaseAnalyzerSettings { diff --git a/CodeDocumentor.Analyzers/Analyzers/Interfaces/InterfaceAnalyzer.cs b/CodeDocumentor.Analyzers/Analyzers/Interfaces/InterfaceAnalyzer.cs index 4b43795..3bb0fd3 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Interfaces/InterfaceAnalyzer.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Interfaces/InterfaceAnalyzer.cs @@ -1,13 +1,13 @@ using System.Collections.Immutable; using CodeDocumentor.Analyzers.Builders; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Analyzers.Interfaces +namespace CodeDocumentor.Analyzers.Analyzers.Interfaces { /// /// The interface analyzer. diff --git a/CodeDocumentor.Analyzers/Analyzers/Interfaces/InterfaceAnalyzerSettings.cs b/CodeDocumentor.Analyzers/Analyzers/Interfaces/InterfaceAnalyzerSettings.cs index 2daf6c4..de8008f 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Interfaces/InterfaceAnalyzerSettings.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Interfaces/InterfaceAnalyzerSettings.cs @@ -2,7 +2,7 @@ using CodeDocumentor.Common.Interfaces; using Microsoft.CodeAnalysis; -namespace CodeDocumentor.Analyzers +namespace CodeDocumentor.Analyzers.Analyzers.Interfaces { public class InterfaceAnalyzerSettings : BaseAnalyzerSettings { diff --git a/CodeDocumentor.Analyzers/Analyzers/Methods/MethodAnalyzer.cs b/CodeDocumentor.Analyzers/Analyzers/Methods/MethodAnalyzer.cs index 60307b1..ea8bf7c 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Methods/MethodAnalyzer.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Methods/MethodAnalyzer.cs @@ -1,13 +1,13 @@ using System.Collections.Immutable; using CodeDocumentor.Analyzers.Builders; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Analyzers.Methods +namespace CodeDocumentor.Analyzers.Analyzers.Methods { /// /// The method analyzer. diff --git a/CodeDocumentor.Analyzers/Analyzers/Methods/MethodAnalyzerSettings.cs b/CodeDocumentor.Analyzers/Analyzers/Methods/MethodAnalyzerSettings.cs index f6b5457..f0d2d78 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Methods/MethodAnalyzerSettings.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Methods/MethodAnalyzerSettings.cs @@ -2,7 +2,7 @@ using CodeDocumentor.Common.Interfaces; using Microsoft.CodeAnalysis; -namespace CodeDocumentor.Analyzers +namespace CodeDocumentor.Analyzers.Analyzers.Methods { public class MethodAnalyzerSettings : BaseAnalyzerSettings { diff --git a/CodeDocumentor.Analyzers/Analyzers/Methods/NonPublicMethodAnalyzer.cs b/CodeDocumentor.Analyzers/Analyzers/Methods/NonPublicMethodAnalyzer.cs index d437d35..81b91fd 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Methods/NonPublicMethodAnalyzer.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Methods/NonPublicMethodAnalyzer.cs @@ -1,13 +1,13 @@ using System.Collections.Immutable; using CodeDocumentor.Analyzers.Builders; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Analyzers.Methods +namespace CodeDocumentor.Analyzers.Analyzers.Methods { [DiagnosticAnalyzer(LanguageNames.CSharp)] public class NonPublicMethodAnalyzer : DiagnosticAnalyzer diff --git a/CodeDocumentor.Analyzers/Analyzers/Properties/NonPublicPropertyAnalyzer.cs b/CodeDocumentor.Analyzers/Analyzers/Properties/NonPublicPropertyAnalyzer.cs index 12e2e74..fb37e1f 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Properties/NonPublicPropertyAnalyzer.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Properties/NonPublicPropertyAnalyzer.cs @@ -1,13 +1,13 @@ using System.Collections.Immutable; using CodeDocumentor.Analyzers.Builders; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Analyzers.Properties +namespace CodeDocumentor.Analyzers.Analyzers.Properties { /// /// The property analyzer. diff --git a/CodeDocumentor.Analyzers/Analyzers/Properties/PropertyAnalyzer.cs b/CodeDocumentor.Analyzers/Analyzers/Properties/PropertyAnalyzer.cs index e2c10ed..27f83c7 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Properties/PropertyAnalyzer.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Properties/PropertyAnalyzer.cs @@ -1,13 +1,13 @@ using System.Collections.Immutable; using CodeDocumentor.Analyzers.Builders; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Analyzers.Properties +namespace CodeDocumentor.Analyzers.Analyzers.Properties { /// /// The property analyzer. diff --git a/CodeDocumentor.Analyzers/Analyzers/Properties/PropertyAnalyzerSettings.cs b/CodeDocumentor.Analyzers/Analyzers/Properties/PropertyAnalyzerSettings.cs index de508e0..0f2ebb8 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Properties/PropertyAnalyzerSettings.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Properties/PropertyAnalyzerSettings.cs @@ -2,7 +2,7 @@ using CodeDocumentor.Common.Interfaces; using Microsoft.CodeAnalysis; -namespace CodeDocumentor.Analyzers +namespace CodeDocumentor.Analyzers.Analyzers.Properties { public class PropertyAnalyzerSettings : BaseAnalyzerSettings { diff --git a/CodeDocumentor.Analyzers/Analyzers/Records/NonPublicRecordAnalyzer.cs b/CodeDocumentor.Analyzers/Analyzers/Records/NonPublicRecordAnalyzer.cs index 174c16f..305b06d 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Records/NonPublicRecordAnalyzer.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Records/NonPublicRecordAnalyzer.cs @@ -1,13 +1,13 @@ using System.Collections.Immutable; using CodeDocumentor.Analyzers.Builders; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Analyzers.Records +namespace CodeDocumentor.Analyzers.Analyzers.Records { /// /// The class analyzer. diff --git a/CodeDocumentor.Analyzers/Analyzers/Records/RecordAnalyzer.cs b/CodeDocumentor.Analyzers/Analyzers/Records/RecordAnalyzer.cs index 3432ab3..e7e765c 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Records/RecordAnalyzer.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Records/RecordAnalyzer.cs @@ -1,13 +1,13 @@ using System.Collections.Immutable; using CodeDocumentor.Analyzers.Builders; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Analyzers.Records +namespace CodeDocumentor.Analyzers.Analyzers.Records { /// /// The class analyzer. diff --git a/CodeDocumentor.Analyzers/Analyzers/Records/RecordAnalyzerSettings.cs b/CodeDocumentor.Analyzers/Analyzers/Records/RecordAnalyzerSettings.cs index d967157..0ed83bb 100644 --- a/CodeDocumentor.Analyzers/Analyzers/Records/RecordAnalyzerSettings.cs +++ b/CodeDocumentor.Analyzers/Analyzers/Records/RecordAnalyzerSettings.cs @@ -2,7 +2,7 @@ using CodeDocumentor.Common.Interfaces; using Microsoft.CodeAnalysis; -namespace CodeDocumentor.Analyzers +namespace CodeDocumentor.Analyzers.Analyzers.Records { public class RecordAnalyzerSettings : BaseAnalyzerSettings { diff --git a/CodeDocumentor.Analyzers/Builders/DiagnosticBuilder.cs b/CodeDocumentor.Analyzers/Builders/DiagnosticBuilder.cs index fa1da0d..3e1593b 100644 --- a/CodeDocumentor.Analyzers/Builders/DiagnosticBuilder.cs +++ b/CodeDocumentor.Analyzers/Builders/DiagnosticBuilder.cs @@ -1,6 +1,6 @@ using System; using System.Linq; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/CodeDocumentor.Analyzers/Services/PreLoadLogger.cs b/CodeDocumentor.Analyzers/Services/PreLoadLogger.cs index 9c478e1..24e7d35 100644 --- a/CodeDocumentor.Analyzers/Services/PreLoadLogger.cs +++ b/CodeDocumentor.Analyzers/Services/PreLoadLogger.cs @@ -3,7 +3,7 @@ namespace CodeDocumentor.Analyzers.Services { - internal class PreLoadLogger : IEventLogger + public class PreLoadLogger : IEventLogger { public void LogDebug(string category, string message) { diff --git a/CodeDocumentor.Analyzers/Services/PreLoadSettingService.cs b/CodeDocumentor.Analyzers/Services/PreLoadSettingService.cs index 304b485..f31abbf 100644 --- a/CodeDocumentor.Analyzers/Services/PreLoadSettingService.cs +++ b/CodeDocumentor.Analyzers/Services/PreLoadSettingService.cs @@ -2,7 +2,7 @@ using CodeDocumentor.Common.Models; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Services +namespace CodeDocumentor.Analyzers.Services { public class PreLoadSettingService : ISettingService { diff --git a/CodeDocumentor.Analyzers/Builders/DocumentationBuilder.cs b/CodeDocumentor.Common/Builders/DocumentationBuilder.cs similarity index 98% rename from CodeDocumentor.Analyzers/Builders/DocumentationBuilder.cs rename to CodeDocumentor.Common/Builders/DocumentationBuilder.cs index 19c6665..d1b7e42 100644 --- a/CodeDocumentor.Analyzers/Builders/DocumentationBuilder.cs +++ b/CodeDocumentor.Common/Builders/DocumentationBuilder.cs @@ -1,21 +1,20 @@ using System.Collections.Generic; using System.Linq; -using CodeDocumentor.Analyzers.Constructors; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; -using CodeDocumentor.Common; +using CodeDocumentor.Common.Constructors; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; using CodeDocumentor.Common.Models; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace CodeDocumentor.Analyzers.Builders +namespace CodeDocumentor.Common.Builders { public class DocumentationBuilder { private readonly DocumentationHeaderHelper _documentationHeaderHelper = ServiceLocator.DocumentationHeaderHelper; - private XmlElementSyntax _currentElement; private readonly List _list = new List(); + private XmlElementSyntax _currentElement; public SyntaxList Build() { @@ -246,7 +245,7 @@ private DocumentationBuilder WithLineEndTextSyntax() } /* /// - /// The code fix provider. + /// The code fix provider. /// /// [0] */ diff --git a/CodeDocumentor.Common/CodeDocumentor.Common.csproj b/CodeDocumentor.Common/CodeDocumentor.Common.csproj index 53d3bec..ba29a99 100644 --- a/CodeDocumentor.Common/CodeDocumentor.Common.csproj +++ b/CodeDocumentor.Common/CodeDocumentor.Common.csproj @@ -6,6 +6,7 @@ + diff --git a/CodeDocumentor.Common/Constants.cs b/CodeDocumentor.Common/Constants.cs index 167c8f2..74c2639 100644 --- a/CodeDocumentor.Common/Constants.cs +++ b/CodeDocumentor.Common/Constants.cs @@ -12,6 +12,9 @@ public static class Constants { public const DiagnosticSeverity DefaultDiagnosticSeverityOnError = DiagnosticSeverity.Info; + //This MUST match the Guid in the vsct file + public static readonly Guid CommandSetId = new Guid("d209d6a5-c17c-4c6f-b14c-c8992ef8471d"); + public const string TODO = "TODO: Add Summary"; public static class EventIds diff --git a/CodeDocumentor.Analyzers/Constructors/BaseReturnTypeCommentConstruction.cs b/CodeDocumentor.Common/Constructors/BaseReturnTypeCommentConstruction.cs similarity index 97% rename from CodeDocumentor.Analyzers/Constructors/BaseReturnTypeCommentConstruction.cs rename to CodeDocumentor.Common/Constructors/BaseReturnTypeCommentConstruction.cs index 9986b8a..cf48b66 100644 --- a/CodeDocumentor.Analyzers/Constructors/BaseReturnTypeCommentConstruction.cs +++ b/CodeDocumentor.Common/Constructors/BaseReturnTypeCommentConstruction.cs @@ -1,13 +1,13 @@ using System; using System.Linq; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; -using CodeDocumentor.Analyzers.Managers; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; +using CodeDocumentor.Common.Managers; using CodeDocumentor.Common.Models; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace CodeDocumentor.Analyzers.Constructors +namespace CodeDocumentor.Common.Constructors { public abstract class BaseReturnTypeCommentConstruction { diff --git a/CodeDocumentor.Analyzers/Constructors/ReturnCommentConstruction.cs b/CodeDocumentor.Common/Constructors/ReturnCommentConstruction.cs similarity index 97% rename from CodeDocumentor.Analyzers/Constructors/ReturnCommentConstruction.cs rename to CodeDocumentor.Common/Constructors/ReturnCommentConstruction.cs index 44d3033..c8304f6 100644 --- a/CodeDocumentor.Analyzers/Constructors/ReturnCommentConstruction.cs +++ b/CodeDocumentor.Common/Constructors/ReturnCommentConstruction.cs @@ -2,7 +2,7 @@ using CodeDocumentor.Common.Models; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace CodeDocumentor.Analyzers.Constructors +namespace CodeDocumentor.Common.Constructors { /// /// The return comment construction. diff --git a/CodeDocumentor.Analyzers/Constructors/SingleWordCommentSummaryConstruction.cs b/CodeDocumentor.Common/Constructors/SingleWordCommentSummaryConstruction.cs similarity index 97% rename from CodeDocumentor.Analyzers/Constructors/SingleWordCommentSummaryConstruction.cs rename to CodeDocumentor.Common/Constructors/SingleWordCommentSummaryConstruction.cs index f17153e..206116a 100644 --- a/CodeDocumentor.Analyzers/Constructors/SingleWordCommentSummaryConstruction.cs +++ b/CodeDocumentor.Common/Constructors/SingleWordCommentSummaryConstruction.cs @@ -2,7 +2,7 @@ using CodeDocumentor.Common.Models; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace CodeDocumentor.Analyzers.Constructors +namespace CodeDocumentor.Common.Constructors { public class SingleWordCommentSummaryConstruction : BaseReturnTypeCommentConstruction { diff --git a/CodeDocumentor.Common/Extensions/SettingsExtensions.cs b/CodeDocumentor.Common/Extensions/SettingsExtensions.cs index 2f6c787..1f21be1 100644 --- a/CodeDocumentor.Common/Extensions/SettingsExtensions.cs +++ b/CodeDocumentor.Common/Extensions/SettingsExtensions.cs @@ -29,7 +29,7 @@ public static ISettings Load(this ISettings settings) } Directory.CreateDirectory(_programDataFolder); - var json = File.ReadAllText(GetSettingsFilePath()); + var json = File.ReadAllText(GetSettingsFilePath("2022")); settings = Newtonsoft.Json.JsonConvert.DeserializeObject(json); return settings; } @@ -42,7 +42,7 @@ public static void Save(this ISettings settings) } Directory.CreateDirectory(_programDataFolder); - settings.SaveToFile(GetSettingsFilePath()); + settings.SaveToFile(GetSettingsFilePath("2022")); } public static void SaveToEditorConfig(this ISettings settings, Action setToClipboardAction) @@ -140,17 +140,80 @@ public static ISettings Update(this ISettings settings, ISettings newSettings, I return settings; } + public static IBaseSettings Update(this IBaseSettings settings, IBaseSettings newSettings, IEventLogger logger) + { + settings.IsEnabledForPublicMembersOnly = newSettings.IsEnabledForPublicMembersOnly; + settings.UseNaturalLanguageForReturnNode = newSettings.UseNaturalLanguageForReturnNode; + settings.ExcludeAsyncSuffix = newSettings.ExcludeAsyncSuffix; + settings.IncludeValueNodeInProperties = newSettings.IncludeValueNodeInProperties; + settings.UseToDoCommentsOnSummaryError = newSettings.UseToDoCommentsOnSummaryError; + settings.WordMaps = newSettings.WordMaps; + settings.PreserveExistingSummaryText = newSettings.PreserveExistingSummaryText; + settings.IsEnabledForNonPublicFields = newSettings.IsEnabledForNonPublicFields; + settings.TryToIncludeCrefsForReturnTypes = newSettings.TryToIncludeCrefsForReturnTypes; + + logger.LogInfo(JsonConvert.SerializeObject(settings), 200, 0, "Options updated"); + return settings; + } + + public static void SetFromOptionsGrid(this IBaseSettings settings, IBaseSettings optionsGrid) + { + settings.ExcludeAsyncSuffix = optionsGrid?.ExcludeAsyncSuffix ?? false; + settings.IncludeValueNodeInProperties = optionsGrid?.IncludeValueNodeInProperties ?? false; + settings.IsEnabledForPublicMembersOnly = optionsGrid?.IsEnabledForPublicMembersOnly ?? false; + settings.IsEnabledForNonPublicFields = optionsGrid?.IsEnabledForNonPublicFields ?? false; + settings.PreserveExistingSummaryText = optionsGrid?.PreserveExistingSummaryText ?? true; + settings.UseNaturalLanguageForReturnNode = optionsGrid?.UseNaturalLanguageForReturnNode ?? false; + settings.UseToDoCommentsOnSummaryError = optionsGrid?.UseToDoCommentsOnSummaryError ?? false; + settings.TryToIncludeCrefsForReturnTypes = optionsGrid?.TryToIncludeCrefsForReturnTypes ?? false; + settings.WordMaps = optionsGrid?.WordMaps ?? Constants.DEFAULT_WORD_MAPS; + } + + public static void SaveToFile(this IBaseSettings settings, string path) + { + File.WriteAllText(path, Newtonsoft.Json.JsonConvert.SerializeObject(settings)); + } + + public static IBaseSettings Load(this IBaseSettings settings) + { + if (Runtime.RunningUnitTests) + { + return new Settings2026(); + } + + Directory.CreateDirectory(_programDataFolder); + var json = File.ReadAllText(GetSettingsFilePath("2026","2026")); + settings = Newtonsoft.Json.JsonConvert.DeserializeObject(json); + return settings; + } + + public static void Save(this IBaseSettings settings) + { + if (Runtime.RunningUnitTests) + { + return; + } + + Directory.CreateDirectory(_programDataFolder); + settings.SaveToFile(GetSettingsFilePath("2026", "2026")); + } + /// /// Gets the settings file path. /// /// A string. - private static string GetSettingsFilePath() + private static string GetSettingsFilePath(string type, string suffix = "") { - const string name = "codedocumentor.json"; + string name = string.Format("codedocumentor{0}.json", suffix); var settingsPath = Path.Combine(_programDataFolder, name); if (!File.Exists(settingsPath)) { + if (type == "2026") + { + new Settings2026().SaveToFile(settingsPath); + return settingsPath; + } new Settings().SaveToFile(settingsPath); } diff --git a/CodeDocumentor.Analyzers/Helper/CommentHelper.cs b/CodeDocumentor.Common/Helper/CommentHelper.cs similarity index 99% rename from CodeDocumentor.Analyzers/Helper/CommentHelper.cs rename to CodeDocumentor.Common/Helper/CommentHelper.cs index bf7f468..2bb4b9b 100644 --- a/CodeDocumentor.Analyzers/Helper/CommentHelper.cs +++ b/CodeDocumentor.Common/Helper/CommentHelper.cs @@ -1,8 +1,8 @@ using System; using System.Linq; using System.Runtime.CompilerServices; -using CodeDocumentor.Common; using CodeDocumentor.Common.Extensions; +using CodeDocumentor.Common.Helper; using CodeDocumentor.Common.Helpers; using CodeDocumentor.Common.Models; using Microsoft.CodeAnalysis; @@ -11,7 +11,7 @@ [assembly: InternalsVisibleTo("CodeDocumentor.Test")] -namespace CodeDocumentor.Analyzers.Helper +namespace CodeDocumentor.Common.Helper { /// /// The comment helper. diff --git a/CodeDocumentor.Analyzers/Helper/DocumentationHeaderHelper.cs b/CodeDocumentor.Common/Helper/DocumentationHeaderHelper.cs similarity index 99% rename from CodeDocumentor.Analyzers/Helper/DocumentationHeaderHelper.cs rename to CodeDocumentor.Common/Helper/DocumentationHeaderHelper.cs index 04c2338..df68908 100644 --- a/CodeDocumentor.Analyzers/Helper/DocumentationHeaderHelper.cs +++ b/CodeDocumentor.Common/Helper/DocumentationHeaderHelper.cs @@ -2,17 +2,16 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; -using CodeDocumentor.Analyzers.Locators; -using CodeDocumentor.Common; using CodeDocumentor.Common.Extensions; using CodeDocumentor.Common.Helpers; using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Locators; using CodeDocumentor.Common.Models; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace CodeDocumentor.Analyzers.Helper +namespace CodeDocumentor.Common.Helper { /// /// The documentation header helper. diff --git a/CodeDocumentor.Analyzers/Helper/ListExtensions.cs b/CodeDocumentor.Common/Helper/ListExtensions.cs similarity index 95% rename from CodeDocumentor.Analyzers/Helper/ListExtensions.cs rename to CodeDocumentor.Common/Helper/ListExtensions.cs index 2716d50..ca90428 100644 --- a/CodeDocumentor.Analyzers/Helper/ListExtensions.cs +++ b/CodeDocumentor.Common/Helper/ListExtensions.cs @@ -1,12 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using CodeDocumentor.Analyzers.Constructors; -using CodeDocumentor.Common; +using CodeDocumentor.Common.Constructors; using CodeDocumentor.Common.Models; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace CodeDocumentor.Analyzers.Helper +namespace CodeDocumentor.Common.Helper { public static class ListExtensions { diff --git a/CodeDocumentor.Analyzers/Helper/PrivateMemberVerifier.cs b/CodeDocumentor.Common/Helper/PrivateMemberVerifier.cs similarity index 89% rename from CodeDocumentor.Analyzers/Helper/PrivateMemberVerifier.cs rename to CodeDocumentor.Common/Helper/PrivateMemberVerifier.cs index 4941ac2..8372f9e 100644 --- a/CodeDocumentor.Analyzers/Helper/PrivateMemberVerifier.cs +++ b/CodeDocumentor.Common/Helper/PrivateMemberVerifier.cs @@ -2,7 +2,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace CodeDocumentor.Analyzers.Helper +namespace CodeDocumentor.Common.Helper { /// /// Verifies whether a member is private. @@ -19,6 +19,16 @@ public static bool IsPrivateMember(ClassDeclarationSyntax node) return !node.Modifiers.Any(SyntaxKind.PublicKeyword); } + /// + /// Determines whether the specified enum declaration represents a private member. + /// + /// The enum declaration syntax node to evaluate for access modifiers. + /// true if the enum declaration does not have a public modifier; otherwise, false. + public static bool IsPrivateMember(EnumDeclarationSyntax node) + { + return !node.Modifiers.Any(SyntaxKind.PublicKeyword); + } + /// /// Is private member. /// diff --git a/CodeDocumentor.Analyzers/Helper/SyntaxExtensions.cs b/CodeDocumentor.Common/Helper/SyntaxExtensions.cs similarity index 99% rename from CodeDocumentor.Analyzers/Helper/SyntaxExtensions.cs rename to CodeDocumentor.Common/Helper/SyntaxExtensions.cs index 8e0a354..1732b75 100644 --- a/CodeDocumentor.Analyzers/Helper/SyntaxExtensions.cs +++ b/CodeDocumentor.Common/Helper/SyntaxExtensions.cs @@ -1,12 +1,13 @@ using System; using System.Linq; using CodeDocumentor.Common.Extensions; +using CodeDocumentor.Common.Helper; using CodeDocumentor.Common.Models; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace CodeDocumentor +namespace CodeDocumentor.Common.Helper { public static class SyntaxExtensions { diff --git a/CodeDocumentor.Analyzers/Helper/WordExtensions.cs b/CodeDocumentor.Common/Helper/WordExtensions.cs similarity index 88% rename from CodeDocumentor.Analyzers/Helper/WordExtensions.cs rename to CodeDocumentor.Common/Helper/WordExtensions.cs index 75b6969..cc89de5 100644 --- a/CodeDocumentor.Analyzers/Helper/WordExtensions.cs +++ b/CodeDocumentor.Common/Helper/WordExtensions.cs @@ -1,6 +1,6 @@ using System; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace CodeDocumentor.Analyzers.Helper +namespace CodeDocumentor.Common.Helper { public static class WordExtensions { diff --git a/CodeDocumentor.Common/Interfaces/IBaseSettings.cs b/CodeDocumentor.Common/Interfaces/IBaseSettings.cs new file mode 100644 index 0000000..2c848eb --- /dev/null +++ b/CodeDocumentor.Common/Interfaces/IBaseSettings.cs @@ -0,0 +1,59 @@ +using CodeDocumentor.Common.Models; + +namespace CodeDocumentor.Common.Interfaces +{ + public interface IBaseSettings + { + /// + /// Gets or Sets a value indicating whether exclude asynchronously suffix. + /// + /// A bool. + bool ExcludeAsyncSuffix { get; set; } + + /// + /// Gets or Sets a value indicating whether include value node in properties. + /// + /// A bool. + bool IncludeValueNodeInProperties { get; set; } + + /// + /// Gets or Sets a value indicating whether enabled for non public is fields. + /// + bool IsEnabledForNonPublicFields { get; set; } + + /// + /// Gets or Sets a value indicating whether enabled for public members is only. + /// + /// A bool. + bool IsEnabledForPublicMembersOnly { get; set; } + + /// + /// Gets or Sets a value indicating whether preserve existing summary text. + /// + bool PreserveExistingSummaryText { get; set; } + + /// + /// Gets or Sets a value indicating whether use try and include return type crefs in documentation. + /// + /// A bool. + bool TryToIncludeCrefsForReturnTypes { get; set; } + + /// + /// Gets or Sets a value indicating whether use natural language for return node. + /// + /// A bool. + bool UseNaturalLanguageForReturnNode { get; set; } + + /// + /// Gets or Sets a value indicating whether use to do comments on summary error. + /// + /// A bool. + bool UseToDoCommentsOnSummaryError { get; set; } + + /// + /// Gets or Sets the word maps. + /// + /// A list of wordmaps. + WordMap[] WordMaps { get; set; } + } +} diff --git a/CodeDocumentor.Common/Interfaces/ICommentBuilderService.cs b/CodeDocumentor.Common/Interfaces/ICommentBuilderService.cs new file mode 100644 index 0000000..0c39468 --- /dev/null +++ b/CodeDocumentor.Common/Interfaces/ICommentBuilderService.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace CodeDocumentor.Common.Interfaces +{ + public interface ICommentBuilderService + { + // Class methods (existing) + int BuildComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + int BuildComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + ClassDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, ClassDeclarationSyntax declarationSyntax); + ClassDeclarationSyntax BuildNewDeclaration(ClassDeclarationSyntax declarationSyntax); + + // Property methods + int BuildPropertyComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + int BuildPropertyComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + PropertyDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, PropertyDeclarationSyntax declarationSyntax); + PropertyDeclarationSyntax BuildNewDeclaration(PropertyDeclarationSyntax declarationSyntax); + + // Constructor methods + int BuildConstructorComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + int BuildConstructorComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + ConstructorDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, ConstructorDeclarationSyntax declarationSyntax); + ConstructorDeclarationSyntax BuildNewDeclaration(ConstructorDeclarationSyntax declarationSyntax); + + // Enum methods + int BuildEnumComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + int BuildEnumComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + EnumDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, EnumDeclarationSyntax declarationSyntax); + EnumDeclarationSyntax BuildNewDeclaration(EnumDeclarationSyntax declarationSyntax); + + // Field methods + int BuildFieldComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + int BuildFieldComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + FieldDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, FieldDeclarationSyntax declarationSyntax); + FieldDeclarationSyntax BuildNewDeclaration(FieldDeclarationSyntax declarationSyntax); + + // Method methods + int BuildMethodComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + int BuildMethodComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + MethodDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, MethodDeclarationSyntax declarationSyntax); + MethodDeclarationSyntax BuildNewDeclaration(MethodDeclarationSyntax declarationSyntax); + + // Interface methods + int BuildInterfaceComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + int BuildInterfaceComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + InterfaceDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, InterfaceDeclarationSyntax declarationSyntax); + InterfaceDeclarationSyntax BuildNewDeclaration(InterfaceDeclarationSyntax declarationSyntax); + + // Record methods + int BuildRecordComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + int BuildRecordComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace); + RecordDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, RecordDeclarationSyntax declarationSyntax); + RecordDeclarationSyntax BuildNewDeclaration(RecordDeclarationSyntax declarationSyntax); + + // Utility methods + string AddDocumentation(string fileContents); + + /// + /// Determines if a syntax node is documentable (can have XML documentation comments) + /// + bool IsDocumentableNode(SyntaxNode node); + + /// + /// Builds documentation for any supported syntax node type + /// + SyntaxNode BuildNewDocumentationNode(SyntaxNode node); + + /// + /// Calculates the number of non-empty lines in the XML documentation comments that precede the specified syntax + /// node. + /// + /// Only lines within single-line or multi-line XML documentation comments are counted. + /// Blank or whitespace-only lines are excluded from the count. + /// The syntax node whose leading XML documentation comment lines are to be counted. + /// The number of non-empty lines found in the single-line or multi-line XML documentation comments immediately + /// preceding the specified node. + int GetDocumentationLineCount(SyntaxNode node); + } +} diff --git a/CodeDocumentor.Common/Interfaces/ISettings.cs b/CodeDocumentor.Common/Interfaces/ISettings.cs index 4e5c5d8..20c18bd 100644 --- a/CodeDocumentor.Common/Interfaces/ISettings.cs +++ b/CodeDocumentor.Common/Interfaces/ISettings.cs @@ -6,88 +6,36 @@ // also https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/recommended-tags namespace CodeDocumentor.Common.Interfaces { - public interface ISettings + public interface ISettings : IBaseSettings { DiagnosticSeverity? ClassDiagnosticSeverity { get; set; } DiagnosticSeverity? ConstructorDiagnosticSeverity { get; set; } /// - /// Gets or Sets a value indicating the default severity for the code analyzer. + /// Gets or Sets a value indicating the default severity for the code analyzer. /// /// A DiagnosticSeverity. DiagnosticSeverity DefaultDiagnosticSeverity { get; set; } DiagnosticSeverity? EnumDiagnosticSeverity { get; set; } - /// - /// Gets or Sets a value indicating whether exclude asynchronously suffix. - /// - /// A bool. - bool ExcludeAsyncSuffix { get; set; } - DiagnosticSeverity? FieldDiagnosticSeverity { get; set; } - /// - /// Gets or Sets a value indicating whether include value node in properties. - /// - /// A bool. - bool IncludeValueNodeInProperties { get; set; } - DiagnosticSeverity? InterfaceDiagnosticSeverity { get; set; } - /// - /// Gets or Sets a value indicating whether enabled for non public is fields. - /// - bool IsEnabledForNonPublicFields { get; set; } - - /// - /// Gets or Sets a value indicating whether enabled for public members is only. - /// - /// A bool. - bool IsEnabledForPublicMembersOnly { get; set; } - DiagnosticSeverity? MethodDiagnosticSeverity { get; set; } - /// - /// Gets or Sets a value indicating whether preserve existing summary text. - /// - bool PreserveExistingSummaryText { get; set; } - DiagnosticSeverity? PropertyDiagnosticSeverity { get; set; } DiagnosticSeverity? RecordDiagnosticSeverity { get; set; } /// - /// Gets or Sets a value indicating whether use try and include return type crefs in documentation. - /// - /// A bool. - bool TryToIncludeCrefsForReturnTypes { get; set; } - - /// - /// Gets or Sets a value indicating whether to use the .editorconfig file for settings. + /// Gets or Sets a value indicating whether to use the .editorconfig file for settings. /// /// This will convert the existing settings to a %USERPROFILE% .editorconfig file bool UseEditorConfigForSettings { get; set; } - /// - /// Gets or Sets a value indicating whether use natural language for return node. - /// - /// A bool. - bool UseNaturalLanguageForReturnNode { get; set; } - - /// - /// Gets or Sets a value indicating whether use to do comments on summary error. - /// - /// A bool. - bool UseToDoCommentsOnSummaryError { get; set; } - - /// - /// Gets or Sets the word maps. - /// - /// A list of wordmaps. - WordMap[] WordMaps { get; set; } - ISettings Clone(); } } diff --git a/CodeDocumentor.Analyzers/Locators/ServiceLocator.cs b/CodeDocumentor.Common/Locators/ServiceLocator.cs similarity index 50% rename from CodeDocumentor.Analyzers/Locators/ServiceLocator.cs rename to CodeDocumentor.Common/Locators/ServiceLocator.cs index badca1f..d46877b 100644 --- a/CodeDocumentor.Analyzers/Locators/ServiceLocator.cs +++ b/CodeDocumentor.Common/Locators/ServiceLocator.cs @@ -1,17 +1,16 @@ -using CodeDocumentor.Analyzers.Builders; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Managers; -using CodeDocumentor.Analyzers.Services; +using CodeDocumentor.Common.Builders; +using CodeDocumentor.Common.Helper; using CodeDocumentor.Common.Interfaces; -using CodeDocumentor.Services; +using CodeDocumentor.Common.Managers; -namespace CodeDocumentor.Analyzers.Locators +namespace CodeDocumentor.Common.Locators { public static class ServiceLocator { + public static ICommentBuilderService CommentBuilderService { get; set; } public static DocumentationHeaderHelper DocumentationHeaderHelper { get; } = new DocumentationHeaderHelper(); - public static IEventLogger Logger { get; set; } = new PreLoadLogger(); //this is a temp until the real logger can be set at package load time - public static ISettingService SettingService { get; set; } = new PreLoadSettingService(); //this is a temp until the LoadAsync can finish with the real settings. but we need something due to order of events firing + public static IEventLogger Logger { get; set; } + public static ISettingService SettingService { get; set; } public static CommentHelper CommentHelper { get; } = new CommentHelper(); public static GenericCommentManager GenericCommentManager { get; } = new GenericCommentManager(); public static DocumentationBuilder DocumentationBuilder => new DocumentationBuilder(); diff --git a/CodeDocumentor.Analyzers/Managers/GenericCommentManager.cs b/CodeDocumentor.Common/Managers/GenericCommentManager.cs similarity index 98% rename from CodeDocumentor.Analyzers/Managers/GenericCommentManager.cs rename to CodeDocumentor.Common/Managers/GenericCommentManager.cs index 639645c..7025052 100644 --- a/CodeDocumentor.Analyzers/Managers/GenericCommentManager.cs +++ b/CodeDocumentor.Common/Managers/GenericCommentManager.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; using System.Text; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; using CodeDocumentor.Common.Extensions; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Locators; using CodeDocumentor.Common.Models; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace CodeDocumentor.Analyzers.Managers +namespace CodeDocumentor.Common.Managers { public class GenericCommentManager { diff --git a/CodeDocumentor.Common/Models/Settings2026.cs b/CodeDocumentor.Common/Models/Settings2026.cs new file mode 100644 index 0000000..512b5c9 --- /dev/null +++ b/CodeDocumentor.Common/Models/Settings2026.cs @@ -0,0 +1,107 @@ + +using System.Collections.Generic; +using CodeDocumentor.Common.Interfaces; + +// For definitions of XML nodes see: +// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/documentation-comments see +// also https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/recommended-tags +namespace CodeDocumentor.Common.Models +{ + public class Settings2026 : IBaseSettings + { + /// + /// Gets or Sets a value indicating whether exclude asynchronously suffix. + /// + /// A bool. + public bool ExcludeAsyncSuffix { get; set; } + + /// + /// Gets or Sets a value indicating whether include value node in properties. + /// + /// A bool. + public bool IncludeValueNodeInProperties { get; set; } + + /// + /// Gets or Sets a value indicating whether enabled for publish members is only. + /// + /// A bool. + public bool IsEnabledForPublicMembersOnly { get; set; } + + /// + /// Gets or Sets a value indicating whether enabled for non public is fields. + /// + public bool IsEnabledForNonPublicFields { get; set; } + + /// + /// Gets or Sets a value indicating whether preserve existing summary text. + /// + public bool PreserveExistingSummaryText { get; set; } = true; + + /// + /// Gets or Sets a value indicating whether use try and include crefs in method comments. + /// + /// A bool. + public bool TryToIncludeCrefsForReturnTypes { get; set; } + + /// + /// Gets or Sets a value indicating whether use natural language for return node. + /// + /// A bool. + public bool UseNaturalLanguageForReturnNode { get; set; } + + /// + /// Gets or Sets a value indicating whether use to do comments on summary error. + /// + /// A bool. + public bool UseToDoCommentsOnSummaryError { get; set; } + + /// + /// Gets or Sets the word maps. + /// + /// An array of wordmaps. + public WordMap[] WordMaps { get; set; } = Constants.DEFAULT_WORD_MAPS; + + public static IBaseSettings BuildDefaults() + { + return new Settings2026 + { + ExcludeAsyncSuffix = false, + IncludeValueNodeInProperties = false, + IsEnabledForNonPublicFields = false, + IsEnabledForPublicMembersOnly = false, + PreserveExistingSummaryText = true, + TryToIncludeCrefsForReturnTypes = true, + UseNaturalLanguageForReturnNode = false, + UseToDoCommentsOnSummaryError = true, + WordMaps = Constants.DEFAULT_WORD_MAPS + }; + } + + public IBaseSettings Clone() + { + var newSettings = new Settings2026 + { + ExcludeAsyncSuffix = ExcludeAsyncSuffix, + IncludeValueNodeInProperties = IncludeValueNodeInProperties, + IsEnabledForNonPublicFields = IsEnabledForNonPublicFields, + IsEnabledForPublicMembersOnly = IsEnabledForPublicMembersOnly, + PreserveExistingSummaryText = PreserveExistingSummaryText, + TryToIncludeCrefsForReturnTypes = TryToIncludeCrefsForReturnTypes, + UseNaturalLanguageForReturnNode = UseNaturalLanguageForReturnNode, + UseToDoCommentsOnSummaryError = UseToDoCommentsOnSummaryError + }; + var clonedMaps = new List(); + foreach (var item in WordMaps) + { + clonedMaps.Add(new WordMap + { + Translation = item.Translation, + Word = item.Word, + WordEvaluator = item.WordEvaluator + }); + } + newSettings.WordMaps = clonedMaps.ToArray(); + return newSettings; + } + } +} diff --git a/CodeDocumentor.Common/Services/CommentBuilderService.cs b/CodeDocumentor.Common/Services/CommentBuilderService.cs new file mode 100644 index 0000000..016e698 --- /dev/null +++ b/CodeDocumentor.Common/Services/CommentBuilderService.cs @@ -0,0 +1,590 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CodeDocumentor.Common.Helper; +using CodeDocumentor.Common.Helpers; +using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Locators; +using CodeDocumentor.Common.Models; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace CodeDocumentor.Common.Services +{ + public class CommentBuilderService : ICommentBuilderService + { + private readonly IEventLogger _eventLogger; + private readonly IBaseSettings _settings; + + public CommentBuilderService(IEventLogger eventLogger, IBaseSettings settings) + { + _eventLogger = eventLogger; + _settings = settings; + } + + public string AddDocumentation(string fileContents) + { + var tree = CSharpSyntaxTree.ParseText(fileContents); + var root = tree.GetRoot(); + + // Follow the same pattern as RegisterFileCodeFixesAsync in BaseCodeFixProvider + var _nodesTempToReplace = new Dictionary(); + + // Order Matters - same order as in BaseCodeFixProvider.RegisterFileCodeFixesAsync + var neededCommentCount = 0; + neededCommentCount += BuildPropertyComments(_settings, Constants.DiagnosticIds.PROPERTY_DIAGNOSTIC_ID, root, _nodesTempToReplace); + neededCommentCount += BuildConstructorComments(_settings, Constants.DiagnosticIds.CONSTRUCTOR_DIAGNOSTIC_ID, root, _nodesTempToReplace); + neededCommentCount += BuildEnumComments(_settings, Constants.DiagnosticIds.ENUM_DIAGNOSTIC_ID, root, _nodesTempToReplace); + neededCommentCount += BuildFieldComments(_settings, Constants.DiagnosticIds.FIELD_DIAGNOSTIC_ID, root, _nodesTempToReplace); + neededCommentCount += BuildMethodComments(_settings, Constants.DiagnosticIds.METHOD_DIAGNOSTIC_ID, root, _nodesTempToReplace); + + // Replace nodes from first batch + root = root.ReplaceNodes(_nodesTempToReplace.Keys, (n1, n2) => _nodesTempToReplace[n1]); + _nodesTempToReplace.Clear(); + + // Second batch - same order as in BaseCodeFixProvider.RegisterFileCodeFixesAsync + neededCommentCount += BuildInterfaceComments(_settings, Constants.DiagnosticIds.INTERFACE_DIAGNOSTIC_ID, root, _nodesTempToReplace); + neededCommentCount += BuildComments(_settings, Constants.DiagnosticIds.CLASS_DIAGNOSTIC_ID, root, _nodesTempToReplace); + neededCommentCount += BuildRecordComments(_settings, Constants.DiagnosticIds.RECORD_DIAGNOSTIC_ID, root, _nodesTempToReplace); + + // Final replacement + var newRoot = root.ReplaceNodes(_nodesTempToReplace.Keys, (n1, n2) => _nodesTempToReplace[n1]); + + return newRoot.GetText().ToString(); + } + + #region Class Methods + /// + /// Builds the comments. This is only used in the file level fixProvider. + /// + /// The root. + /// The nodes to replace. + public int BuildComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + return BuildComments(_settings, diagnosticId, root, nodesToReplace); + } + + public int BuildComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.ClassDeclaration)).OfType().ToArray(); + var neededCommentCount = 0; + TryHelper.Try(() => + { + foreach (var declarationSyntax in declarations) + { + if (settings.IsEnabledForPublicMembersOnly + && PrivateMemberVerifier.IsPrivateMember(declarationSyntax)) + { + continue; + } + if (declarationSyntax.HasSummary()) //if the class has comments dont redo it. User should update manually + { + continue; + } + var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + nodesToReplace.TryAdd(declarationSyntax, newDeclaration); + neededCommentCount++; + } + }, diagnosticId, _eventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); + return neededCommentCount; + } + + public ClassDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, ClassDeclarationSyntax declarationSyntax) + { + var commentHelper = ServiceLocator.CommentHelper; + var comment = commentHelper.CreateClassComment(declarationSyntax.Identifier.ValueText, settings.WordMaps); + var builder = ServiceLocator.DocumentationBuilder; + var list = builder.WithSummary(declarationSyntax, comment, settings.PreserveExistingSummaryText) + .WithTypeParamters(declarationSyntax) + .WithParameters(declarationSyntax, settings.WordMaps) + .WithExisting(declarationSyntax, Constants.REMARKS) + .WithExisting(declarationSyntax, Constants.EXAMPLE) + .Build(); + + var commentTrivia = SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, list); + + //append to any existing leading trivia [attributes, decorators, etc) + var leadingTrivia = declarationSyntax.GetLeadingTrivia(); + + var newDeclaration = declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); + return newDeclaration; + } + + public ClassDeclarationSyntax BuildNewDeclaration(ClassDeclarationSyntax declarationSyntax) + { + return BuildNewDeclaration(_settings, declarationSyntax); + } + #endregion + + #region Property Methods + public int BuildPropertyComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + return BuildPropertyComments(_settings, diagnosticId, root, nodesToReplace); + } + + public int BuildPropertyComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.PropertyDeclaration)).OfType().ToArray(); + var neededCommentCount = 0; + TryHelper.Try(() => + { + foreach (var declarationSyntax in declarations) + { + if (settings.IsEnabledForPublicMembersOnly && PrivateMemberVerifier.IsPrivateMember(declarationSyntax)) + { + continue; + } + if (declarationSyntax.HasSummary()) + { + continue; + } + var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + nodesToReplace.TryAdd(declarationSyntax, newDeclaration); + neededCommentCount++; + } + }, diagnosticId, _eventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); + return neededCommentCount; + } + + public PropertyDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, PropertyDeclarationSyntax declarationSyntax) + { + var isBoolean = declarationSyntax.IsPropertyReturnTypeBool(); + var hasSetter = declarationSyntax.PropertyHasSetter(); + + var commentHelper = ServiceLocator.CommentHelper; + var propertyComment = commentHelper.CreatePropertyComment(declarationSyntax.Identifier.ValueText, isBoolean, + hasSetter, settings.ExcludeAsyncSuffix, settings.WordMaps); + var builder = ServiceLocator.DocumentationBuilder; + + var returnOptions = new ReturnTypeBuilderOptions + { + TryToIncludeCrefsForReturnTypes = settings.TryToIncludeCrefsForReturnTypes, + GenerateReturnStatement = settings.IncludeValueNodeInProperties, + ReturnGenericTypeAsFullString = false, + IncludeStartingWordInText = true, + UseProperCasing = true + }; + var list = builder.WithSummary(declarationSyntax, propertyComment, settings.PreserveExistingSummaryText) + .WithPropertyValueTypes(declarationSyntax, returnOptions, settings.WordMaps) + .WithExisting(declarationSyntax, Constants.REMARKS) + .WithExisting(declarationSyntax, Constants.EXAMPLE) + .Build(); + + var commentTrivia = SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, list); + + var leadingTrivia = declarationSyntax.GetLeadingTrivia(); + var newDeclaration = declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); + return newDeclaration; + } + + public PropertyDeclarationSyntax BuildNewDeclaration(PropertyDeclarationSyntax declarationSyntax) + { + return BuildNewDeclaration(_settings, declarationSyntax); + } + #endregion + + #region Constructor Methods + public int BuildConstructorComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + return BuildConstructorComments(_settings, diagnosticId, root, nodesToReplace); + } + + public int BuildConstructorComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.ConstructorDeclaration)).OfType().ToArray(); + var neededCommentCount = 0; + TryHelper.Try(() => + { + foreach (var declarationSyntax in declarations) + { + if (settings.IsEnabledForPublicMembersOnly && PrivateMemberVerifier.IsPrivateMember(declarationSyntax)) + { + continue; + } + if (declarationSyntax.HasSummary()) + { + continue; + } + var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + nodesToReplace.TryAdd(declarationSyntax, newDeclaration); + neededCommentCount++; + } + }, diagnosticId, _eventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); + return neededCommentCount; + } + + public ConstructorDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, ConstructorDeclarationSyntax declarationSyntax) + { + var leadingTrivia = declarationSyntax.GetLeadingTrivia(); + var commentTrivia = CreateConstructorDocumentationCommentTrivia(settings, declarationSyntax); + var newDeclaration = declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); + return newDeclaration; + } + + private DocumentationCommentTriviaSyntax CreateConstructorDocumentationCommentTrivia(IBaseSettings settings, ConstructorDeclarationSyntax declarationSyntax) + { + var commentHelper = ServiceLocator.CommentHelper; + var comment = commentHelper.CreateConstructorComment(declarationSyntax.Identifier.ValueText, declarationSyntax.IsPrivate(), settings.WordMaps); + var builder = ServiceLocator.DocumentationBuilder; + var list = builder.WithSummary(declarationSyntax, comment, settings.PreserveExistingSummaryText) + .WithParameters(declarationSyntax, settings.WordMaps) + .WithExisting(declarationSyntax, Constants.REMARKS) + .WithExisting(declarationSyntax, Constants.EXAMPLE) + .Build(); + + return SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, list); + } + + public ConstructorDeclarationSyntax BuildNewDeclaration(ConstructorDeclarationSyntax declarationSyntax) + { + return BuildNewDeclaration(_settings, declarationSyntax); + } + #endregion + + #region Enum Methods + public int BuildEnumComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + return BuildEnumComments(_settings, diagnosticId, root, nodesToReplace); + } + + public int BuildEnumComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.EnumDeclaration)).OfType().ToArray(); + var neededCommentCount = 0; + TryHelper.Try(() => + { + foreach (var declarationSyntax in declarations) + { + if (settings.IsEnabledForPublicMembersOnly && PrivateMemberVerifier.IsPrivateMember(declarationSyntax)) + { + continue; + } + if (declarationSyntax.HasSummary()) + { + continue; + } + var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + nodesToReplace.TryAdd(declarationSyntax, newDeclaration); + neededCommentCount++; + } + }, diagnosticId, _eventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); + return neededCommentCount; + } + + public EnumDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, EnumDeclarationSyntax declarationSyntax) + { + var leadingTrivia = declarationSyntax.GetLeadingTrivia(); + var commentHelper = ServiceLocator.CommentHelper; + var comment = commentHelper.CreateEnumComment(declarationSyntax.Identifier.ValueText, settings.WordMaps); + + var builder = ServiceLocator.DocumentationBuilder; + + var summaryNodes = builder.WithSummary(comment).Build(); + var commentTrivia = SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, summaryNodes); + var newDeclaration = declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); + return newDeclaration; + } + + public EnumDeclarationSyntax BuildNewDeclaration(EnumDeclarationSyntax declarationSyntax) + { + return BuildNewDeclaration(_settings, declarationSyntax); + } + #endregion + + #region Field Methods + public int BuildFieldComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + return BuildFieldComments(_settings, diagnosticId, root, nodesToReplace); + } + + public int BuildFieldComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.FieldDeclaration)).OfType().ToArray(); + var neededCommentCount = 0; + TryHelper.Try(() => + { + foreach (var declarationSyntax in declarations) + { + if (settings.IsEnabledForPublicMembersOnly && PrivateMemberVerifier.IsPrivateMember(declarationSyntax)) + { + continue; + } + if (declarationSyntax.HasSummary()) + { + continue; + } + var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + nodesToReplace.TryAdd(declarationSyntax, newDeclaration); + neededCommentCount++; + } + }, diagnosticId, _eventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); + return neededCommentCount; + } + + public FieldDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, FieldDeclarationSyntax declarationSyntax) + { + var leadingTrivia = declarationSyntax.GetLeadingTrivia(); + var field = declarationSyntax.DescendantNodes().OfType().FirstOrDefault(); + var commentHelper = ServiceLocator.CommentHelper; + var comment = commentHelper.CreateFieldComment(field?.Identifier.ValueText, settings.ExcludeAsyncSuffix, settings.WordMaps); + + var builder = ServiceLocator.DocumentationBuilder; + + var summaryNodes = builder.WithSummary(comment).Build(); + var commentTrivia = SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, summaryNodes); + var newDeclaration = declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); + return newDeclaration; + } + + public FieldDeclarationSyntax BuildNewDeclaration(FieldDeclarationSyntax declarationSyntax) + { + return BuildNewDeclaration(_settings, declarationSyntax); + } + #endregion + + #region Method Methods + public int BuildMethodComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + return BuildMethodComments(_settings, diagnosticId, root, nodesToReplace); + } + + public int BuildMethodComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.MethodDeclaration)).OfType().ToArray(); + var neededCommentCount = 0; + TryHelper.Try(() => + { + foreach (var declarationSyntax in declarations) + { + if ( + !declarationSyntax.IsOwnedByInterface() && + settings.IsEnabledForPublicMembersOnly && PrivateMemberVerifier.IsPrivateMember(declarationSyntax) + ) + { + continue; + } + //if method is already commented dont redo it, user should update methods individually + if (declarationSyntax.HasSummary()) + { + continue; + } + var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + nodesToReplace.TryAdd(declarationSyntax, newDeclaration); + neededCommentCount++; + } + }, diagnosticId, _eventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); + return neededCommentCount; + } + + public MethodDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, MethodDeclarationSyntax declarationSyntax) + { + var leadingTrivia = declarationSyntax.GetLeadingTrivia(); + var commentTrivia = CreateMethodDocumentationCommentTrivia(settings, declarationSyntax); + return declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); + } + + public MethodDeclarationSyntax BuildNewDeclaration(MethodDeclarationSyntax declarationSyntax) + { + return BuildNewDeclaration(_settings, declarationSyntax); + } + + private DocumentationCommentTriviaSyntax CreateMethodDocumentationCommentTrivia(IBaseSettings settings, MethodDeclarationSyntax declarationSyntax) + { + var commentHelper = ServiceLocator.CommentHelper; + var summaryText = commentHelper.CreateMethodComment(declarationSyntax.Identifier.ValueText, + declarationSyntax.ReturnType, + settings.UseToDoCommentsOnSummaryError, + settings.TryToIncludeCrefsForReturnTypes, + settings.ExcludeAsyncSuffix, + settings.WordMaps); + var builder = ServiceLocator.DocumentationBuilder; + + var list = builder.WithSummary(declarationSyntax, summaryText, settings.PreserveExistingSummaryText) + .WithTypeParamters(declarationSyntax) + .WithParameters(declarationSyntax, settings.WordMaps) + .WithExceptionTypes(declarationSyntax) + .WithExisting(declarationSyntax, Constants.REMARKS) + .WithExisting(declarationSyntax, Constants.EXAMPLE) + .WithReturnType(declarationSyntax, settings.UseNaturalLanguageForReturnNode, settings.TryToIncludeCrefsForReturnTypes, settings.WordMaps) + .Build(); + + return SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, list); + } + #endregion + + #region Interface Methods + public int BuildInterfaceComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + return BuildInterfaceComments(_settings, diagnosticId, root, nodesToReplace); + } + + public int BuildInterfaceComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.InterfaceDeclaration)).OfType().ToArray(); + var neededCommentCount = 0; + TryHelper.Try(() => + { + foreach (var declarationSyntax in declarations) + { + if (declarationSyntax.HasSummary()) + { + continue; + } + var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + nodesToReplace.TryAdd(declarationSyntax, newDeclaration); + neededCommentCount++; + } + }, diagnosticId, _eventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); + return neededCommentCount; + } + + public InterfaceDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, InterfaceDeclarationSyntax declarationSyntax) + { + var commentHelper = ServiceLocator.CommentHelper; + var comment = commentHelper.CreateInterfaceComment(declarationSyntax.Identifier.ValueText, settings.WordMaps); + var builder = ServiceLocator.DocumentationBuilder; + var list = builder.WithSummary(declarationSyntax, comment, settings.PreserveExistingSummaryText) + .WithTypeParamters(declarationSyntax) + .Build(); + + var commentTrivia = SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, list); + + var leadingTrivia = declarationSyntax.GetLeadingTrivia(); + var newDeclaration = declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); + return newDeclaration; + } + + public InterfaceDeclarationSyntax BuildNewDeclaration(InterfaceDeclarationSyntax declarationSyntax) + { + return BuildNewDeclaration(_settings, declarationSyntax); + } + #endregion + + #region Record Methods + public int BuildRecordComments(string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + return BuildRecordComments(_settings, diagnosticId, root, nodesToReplace); + } + + public int BuildRecordComments(IBaseSettings settings, string diagnosticId, SyntaxNode root, Dictionary nodesToReplace) + { + var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.RecordDeclaration)).OfType().ToArray(); + var neededCommentCount = 0; + TryHelper.Try(() => + { + foreach (var declarationSyntax in declarations) + { + if (settings.IsEnabledForPublicMembersOnly + && PrivateMemberVerifier.IsPrivateMember(declarationSyntax)) + { + continue; + } + if (declarationSyntax.HasSummary()) //if record already has comments dont redo it. User should update this manually + { + continue; + } + var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + nodesToReplace.TryAdd(declarationSyntax, newDeclaration); + neededCommentCount++; + } + }, diagnosticId, _eventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); + return neededCommentCount; + } + + public RecordDeclarationSyntax BuildNewDeclaration(IBaseSettings settings, RecordDeclarationSyntax declarationSyntax) + { + var commentHelper = ServiceLocator.CommentHelper; + var comment = commentHelper.CreateRecordComment(declarationSyntax.Identifier.ValueText, settings.WordMaps); + var builder = ServiceLocator.DocumentationBuilder; + + var list = builder.WithSummary(declarationSyntax, comment, settings.PreserveExistingSummaryText) + .WithTypeParamters(declarationSyntax) + .WithExisting(declarationSyntax, Constants.REMARKS) + .WithExisting(declarationSyntax, Constants.EXAMPLE) + .Build(); + + var commentTrivia = SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, list); + + //append to any existing leading trivia [attributes, decorators, etc) + var leadingTrivia = declarationSyntax.GetLeadingTrivia(); + + var newDeclaration = declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); + return newDeclaration; + } + + public RecordDeclarationSyntax BuildNewDeclaration(RecordDeclarationSyntax declarationSyntax) + { + return BuildNewDeclaration(_settings, declarationSyntax); + } + #endregion + + #region Utility Methods + /// + /// Determines if a syntax node is documentable (can have XML documentation comments) + /// + public bool IsDocumentableNode(SyntaxNode node) + { + switch (node) + { + case ClassDeclarationSyntax _: + case InterfaceDeclarationSyntax _: + case RecordDeclarationSyntax _: + case EnumDeclarationSyntax _: + case MethodDeclarationSyntax _: + case PropertyDeclarationSyntax _: + case ConstructorDeclarationSyntax _: + case FieldDeclarationSyntax _: + return true; + default: + return false; + } + } + /// + /// Calculates the number of non-empty lines in the XML documentation comments that precede the specified syntax + /// node. + /// + /// Only lines within single-line or multi-line XML documentation comments are counted. + /// Blank or whitespace-only lines are excluded from the count. + /// The syntax node whose leading XML documentation comment lines are to be counted. + /// The number of non-empty lines found in the single-line or multi-line XML documentation comments immediately + /// preceding the specified node. + public int GetDocumentationLineCount(SyntaxNode node) + { + return node.GetLeadingTrivia() + .Where(t => t.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia) || t.IsKind(SyntaxKind.MultiLineDocumentationCommentTrivia)) + .SelectMany(t => t.ToFullString().Split(new[] { '\n' }, StringSplitOptions.None).Select(line => line.Trim())) + .Where(line => !string.IsNullOrWhiteSpace(line)) + .Count(); + } + + /// + /// Builds documentation for any supported syntax node type + /// + public SyntaxNode BuildNewDocumentationNode(SyntaxNode node) + { + switch (node) + { + case ClassDeclarationSyntax classNode: + return BuildNewDeclaration(classNode); + case InterfaceDeclarationSyntax interfaceNode: + return BuildNewDeclaration(interfaceNode); + case RecordDeclarationSyntax recordNode: + return BuildNewDeclaration(recordNode); + case EnumDeclarationSyntax enumNode: + return BuildNewDeclaration(enumNode); + case MethodDeclarationSyntax methodNode: + return BuildNewDeclaration(methodNode); + case PropertyDeclarationSyntax propertyNode: + return BuildNewDeclaration(propertyNode); + case ConstructorDeclarationSyntax constructorNode: + return BuildNewDeclaration(constructorNode); + case FieldDeclarationSyntax fieldNode: + return BuildNewDeclaration(fieldNode); + default: + return null; + } + } + #endregion + } +} diff --git a/CodeDocumentor.Test/Builders/DocumentationBuilderTests.cs b/CodeDocumentor.Test/Builders/DocumentationBuilderTests.cs index 1254d19..0ea85b2 100644 --- a/CodeDocumentor.Test/Builders/DocumentationBuilderTests.cs +++ b/CodeDocumentor.Test/Builders/DocumentationBuilderTests.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; -using CodeDocumentor.Analyzers.Builders; -using FluentAssertions; +using CodeDocumentor.Common.Builders; +using Shouldly; using Xunit; using Xunit.Abstractions; @@ -28,8 +28,8 @@ public void CreateReturnComment__ReturnsValidNameWithStartingWord_WhenUseNatural _fixture.MockSettings.UseNaturalLanguageForReturnNode = true; _fixture.MockSettings.TryToIncludeCrefsForReturnTypes = false; var comment = _builder.WithReturnType(method, _fixture.MockSettings.UseNaturalLanguageForReturnNode, _fixture.MockSettings.TryToIncludeCrefsForReturnTypes, _fixture.MockSettings.WordMaps).Build(); - comment.Count.Should().Be(3); - comment[1].ToFullString().Should().Be(@"A "); + comment.Count.ShouldBe(3); + comment[1].ToFullString().ShouldBe(@"A "); } } } diff --git a/CodeDocumentor.Test/Classes/ClassUnitTests.cs b/CodeDocumentor.Test/Classes/ClassUnitTests.cs index 27e2f58..17d95e7 100644 --- a/CodeDocumentor.Test/Classes/ClassUnitTests.cs +++ b/CodeDocumentor.Test/Classes/ClassUnitTests.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Classes; +using CodeDocumentor.Analyzers.Analyzers.Classes; using CodeDocumentor.Test.TestHelpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/CodeDocumentor.Test/CodeDocumentor.Test.csproj b/CodeDocumentor.Test/CodeDocumentor.Test.csproj index 81bf4b0..b5627e1 100644 --- a/CodeDocumentor.Test/CodeDocumentor.Test.csproj +++ b/CodeDocumentor.Test/CodeDocumentor.Test.csproj @@ -494,15 +494,16 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + compile; build; native; contentfiles; analyzers; buildtransitive - - - + + + + all diff --git a/CodeDocumentor.Test/Constructors/ConstructorUnitTests.cs b/CodeDocumentor.Test/Constructors/ConstructorUnitTests.cs index 00126b7..cb9eebc 100644 --- a/CodeDocumentor.Test/Constructors/ConstructorUnitTests.cs +++ b/CodeDocumentor.Test/Constructors/ConstructorUnitTests.cs @@ -1,7 +1,6 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Constructors; +using CodeDocumentor.Analyzers.Analyzers.Constructors; using CodeDocumentor.Test.TestHelpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/CodeDocumentor.Test/Enums/EnumUnitTests.cs b/CodeDocumentor.Test/Enums/EnumUnitTests.cs index 4909172..e11cdbb 100644 --- a/CodeDocumentor.Test/Enums/EnumUnitTests.cs +++ b/CodeDocumentor.Test/Enums/EnumUnitTests.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Enums; +using CodeDocumentor.Analyzers.Analyzers.Enums; using CodeDocumentor.Test.TestHelpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/CodeDocumentor.Test/Fields/FieldUnitTests.cs b/CodeDocumentor.Test/Fields/FieldUnitTests.cs index 3f82226..0c27ccd 100644 --- a/CodeDocumentor.Test/Fields/FieldUnitTests.cs +++ b/CodeDocumentor.Test/Fields/FieldUnitTests.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Fields; +using CodeDocumentor.Analyzers.Analyzers.Fields; using CodeDocumentor.Test.TestHelpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/CodeDocumentor.Test/Helper/CommentHelperTests.cs b/CodeDocumentor.Test/Helper/CommentHelperTests.cs index 0a9d72d..a6f823b 100644 --- a/CodeDocumentor.Test/Helper/CommentHelperTests.cs +++ b/CodeDocumentor.Test/Helper/CommentHelperTests.cs @@ -1,12 +1,12 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using CodeDocumentor.Analyzers.Helper; using CodeDocumentor.Common.Extensions; +using CodeDocumentor.Common.Helper; using CodeDocumentor.Test.TestHelpers; -using FluentAssertions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Shouldly; using Xunit; using Xunit.Abstractions; @@ -36,7 +36,7 @@ public void CreateFieldComment_ReturnsValidName(string name, string expected) { var comment = _commentHelper.CreateFieldComment(name, _fixture.MockSettings.ExcludeAsyncSuffix, _fixture.MockSettings.WordMaps); - comment.Should().Be(expected); + comment.ShouldBe(expected); } [Theory] @@ -88,7 +88,7 @@ public void CreateMethodComment_ReturnsValidName(string name, string returnType, _fixture.MockSettings.TryToIncludeCrefsForReturnTypes, _fixture.MockSettings.ExcludeAsyncSuffix, _fixture.MockSettings.WordMaps); - comment.Should().Be(expected); + comment.ShouldBe(expected); } [Fact] @@ -104,7 +104,7 @@ public void CreateMethodComment_ReturnsValidCommentWhenOneWordMethodAndLayeredLi _fixture.MockSettings.TryToIncludeCrefsForReturnTypes, _fixture.MockSettings.ExcludeAsyncSuffix, _fixture.MockSettings.WordMaps); - comment.Should().Be("Work and return a of a list of a list of strings."); + comment.ShouldBe("Work and return a of a list of a list of strings."); } [Fact] @@ -125,7 +125,7 @@ public void CreateMethodComment_ReturnsValidCommentWhenReturnIsTask_ActionResult _fixture.MockSettings.TryToIncludeCrefsForReturnTypes, _fixture.MockSettings.ExcludeAsyncSuffix, _fixture.MockSettings.WordMaps); - comment.Should().Be("Creates and return a task of type actionresult of type clientdto asynchronously."); + comment.ShouldBe("Creates and return a task of type actionresult of type clientdto asynchronously."); } [Theory] @@ -159,7 +159,7 @@ public void CreateMethodComment_ReturnsValidNaturalLanguage(string name, string _fixture.MockSettings.TryToIncludeCrefsForReturnTypes, _fixture.MockSettings.ExcludeAsyncSuffix, _fixture.MockSettings.WordMaps); - comment.Should().Be(expected); + comment.ShouldBe(expected); } [Theory] @@ -169,7 +169,7 @@ public void CreateMethodComment_ReturnsValidNaturalLanguage(string name, string public void CreateInterfaceComment_ReturnsValidName(string name, string expected) { var comment = _commentHelper.CreateInterfaceComment(name, _fixture.MockSettings.WordMaps); - comment.Should().Be(expected); + comment.ShouldBe(expected); } [Theory] @@ -181,7 +181,7 @@ public void CreateInterfaceComment_ReturnsValidName(string name, string expected public void CreateClassComment_ReturnsValidName(string name, string expected) { var comment = _commentHelper.CreateClassComment(name, _fixture.MockSettings.WordMaps); - comment.Should().Be(expected); + comment.ShouldBe(expected); } [Theory] @@ -190,7 +190,7 @@ public void CreateClassComment_ReturnsValidName(string name, string expected) public void CreateRecordComment_ReturnsValidName(string name, string expected) { var comment = _commentHelper.CreateRecordComment(name, _fixture.MockSettings.WordMaps); - comment.Should().Be(expected); + comment.ShouldBe(expected); } [Theory] @@ -200,7 +200,7 @@ public void CreateRecordComment_ReturnsValidName(string name, string expected) public void CreatePropertyComment_ReturnsValidName(string name, string expected, bool isBool, bool hasSetter) { var comment = _commentHelper.CreatePropertyComment(name, isBool, hasSetter, _fixture.MockSettings.ExcludeAsyncSuffix, _fixture.MockSettings.WordMaps); - comment.Should().Be(expected); + comment.ShouldBe(expected); } [Theory] @@ -210,7 +210,7 @@ public void CreatePropertyComment_ReturnsValidName(string name, string expected, public void CreateEnumComment_ReturnsValidName(string name, string expected) { var comment = _commentHelper.CreateEnumComment(name, _fixture.MockSettings.WordMaps); - comment.Should().Be(expected); + comment.ShouldBe(expected); } [Theory] @@ -235,7 +235,7 @@ public void CreateParameterComment_ReturnsValidName(string name, string paramTyp var parameter = SyntaxFactory.Parameter(attributeLists, modifiers, typeSyntax, SyntaxFactory.Identifier(name), null); var comment = _commentHelper.CreateParameterComment(parameter, _fixture.MockSettings.WordMaps); - comment.Should().Be(expected); + comment.ShouldBe(expected); } [Theory] @@ -248,7 +248,7 @@ public void InternalTranslate_ConvertsCorrectly(string word, string converted) { var list = new List { word }; var result = list.TranslateParts(_fixture.MockSettings.WordMaps); - result.Should().Contain(converted); + result.ShouldContain(converted); } } } diff --git a/CodeDocumentor.Test/Helper/DocumentationHeaderHelperTests.cs b/CodeDocumentor.Test/Helper/DocumentationHeaderHelperTests.cs index ecc5ff5..e765950 100644 --- a/CodeDocumentor.Test/Helper/DocumentationHeaderHelperTests.cs +++ b/CodeDocumentor.Test/Helper/DocumentationHeaderHelperTests.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; -using CodeDocumentor.Analyzers.Helper; -using FluentAssertions; +using CodeDocumentor.Common.Helper; +using Shouldly; using Xunit; using Xunit.Abstractions; @@ -21,7 +21,7 @@ public void CreateReturnElementSyntax_ReturnsMultipleCRefAsEmbeddedNodeInReturn( const string str = "A of type "; const string expected = "A of type "; var result = _documentationHeaderHelper.CreateReturnElementSyntax(str); - result.ToFullString().Should().Be(expected); + result.ToFullString().ShouldBe(expected); } [Fact] @@ -30,7 +30,7 @@ public void CreateReturnElementSyntax_ReturnsTypeParamRefAsEmbeddedNodeInReturn( const string str = "A "; const string expected = "A "; var result = _documentationHeaderHelper.CreateReturnElementSyntax(str); - result.ToFullString().Should().Be(expected); + result.ToFullString().ShouldBe(expected); } [Fact] @@ -39,7 +39,7 @@ public void CreateReturnElementSyntax_ReturnsCDATAOfTaskInReturn() const string str = "Task"; const string expected = "]]>"; var result = _documentationHeaderHelper.CreateReturnElementSyntax(str); - result.ToFullString().Should().Be(expected); + result.ToFullString().ShouldBe(expected); } [Fact] @@ -48,7 +48,7 @@ public void CreateReturnElementSyntax_ReturnsCDATAOfCDATATaskInReturn() const string str = "]]>"; const string expected = "]]>"; var result = _documentationHeaderHelper.CreateReturnElementSyntax(str); - result.ToFullString().Should().Be(expected); + result.ToFullString().ShouldBe(expected); } [Fact] @@ -57,7 +57,7 @@ public void CreateReturnElementSyntax_ReturnsCRefOfTypeInReturn() const string str = ""; const string expected = ""; var result = _documentationHeaderHelper.CreateReturnElementSyntax(str); - result.ToFullString().Should().Be(expected); + result.ToFullString().ShouldBe(expected); } [Fact] public void CreateReturnElementSyntax_ReturnsStringAndCRefOfTypeInReturn() @@ -65,7 +65,7 @@ public void CreateReturnElementSyntax_ReturnsStringAndCRefOfTypeInReturn() const string str = "Returns a "; const string expected = "Returns a "; var result = _documentationHeaderHelper.CreateReturnElementSyntax(str); - result.ToFullString().Should().Be(expected); + result.ToFullString().ShouldBe(expected); } [Fact] @@ -74,7 +74,7 @@ public void CreateReturnElementSyntax_ReturnsStringAndCRefOfInterfaceTypeInRetur const string str = "Returns an "; const string expected = "Returns an "; var result = _documentationHeaderHelper.CreateReturnElementSyntax(str); - result.ToFullString().Should().Be(expected); + result.ToFullString().ShouldBe(expected); } [Fact] @@ -83,7 +83,7 @@ public void CreateReturnElementSyntax_ReturnsStringAnd2CRefOfTypesInReturn() const string str = "Returns a of type "; const string expected = "Returns a of type "; var result = _documentationHeaderHelper.CreateReturnElementSyntax(str); - result.ToFullString().Should().Be(expected); + result.ToFullString().ShouldBe(expected); } #region GetExceptions diff --git a/CodeDocumentor.Test/Helper/NameSplitterTests.cs b/CodeDocumentor.Test/Helper/NameSplitterTests.cs index 0373156..9f710f2 100644 --- a/CodeDocumentor.Test/Helper/NameSplitterTests.cs +++ b/CodeDocumentor.Test/Helper/NameSplitterTests.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using CodeDocumentor.Common.Helpers; -using FluentAssertions; +using Shouldly; using Xunit; namespace CodeDocumentor.Test.Helper @@ -13,40 +13,40 @@ public class NameSplitterTests public void Split_ReturnsWordsSplitByUnderscore_WhenAllUppercaseString() { var result = NameSplitter.Split("SERVER_ORG_CODE"); - result.Count.Should().Be(3); - result[0].Should().Be("SERVER"); + result.Count.ShouldBe(3); + result[0].ShouldBe("SERVER"); } [Fact] public void Split_ReturnsWordsSplitByUnderscore_WhenAllUppercaseStringWithNumber() { var result = NameSplitter.Split("SERVER123_ORG_CODE123"); - result.Count.Should().Be(3); - result[0].Should().Be("SERVER123"); + result.Count.ShouldBe(3); + result[0].ShouldBe("SERVER123"); } [Fact] public void Split_ReturnsWordsSplitByUpperCaseLetter() { var result = NameSplitter.Split("ExecuteNewActionAsync"); - result.Count.Should().Be(4); + result.Count.ShouldBe(4); } [Fact] public void Split_ReturnsWordsHandlingGroupsOfUppercaseLetters() { var result = NameSplitter.Split("ExecuteOCRActionAsync"); - result.Count.Should().Be(4); - result.Any(a => a.Contains("OCR")).Should().BeTrue(); + result.Count.ShouldBe(4); + result.Any(a => a.Contains("OCR")).ShouldBeTrue(); } [Fact] public void Split_ReturnsWordsHandlingMultipleGroupsOfUppercaseLetters() { var result = NameSplitter.Split("ExecuteOCRActionFMRAsync"); - result.Count.Should().Be(5); - result.Any(a => a.Contains("OCR")).Should().BeTrue(); - result.Any(a => a.Contains("FMR")).Should().BeTrue(); + result.Count.ShouldBe(5); + result.Any(a => a.Contains("OCR")).ShouldBeTrue(); + result.Any(a => a.Contains("FMR")).ShouldBeTrue(); } //NullIntPROP @@ -55,27 +55,27 @@ public void Split_ReturnsWordsHandlingMultipleGroupsOfUppercaseLetters() public void Split_ReturnsWordsHandlingGroupsOfUppercaseLettersAtEnd() { var result = NameSplitter.Split("ExecuteOCRActionPROP"); - result.Count.Should().Be(4); - result.Any(a => a.Contains("OCR")).Should().BeTrue(); - result.Any(a => a.Contains("PROP")).Should().BeTrue(); + result.Count.ShouldBe(4); + result.Any(a => a.Contains("OCR")).ShouldBeTrue(); + result.Any(a => a.Contains("PROP")).ShouldBeTrue(); } [Fact] public void Split_ReturnsWordsHandlingGroupsOfUppercaseLettersAtBeginning() { var result = NameSplitter.Split("PROPExecuteOCRAction"); - result.Count.Should().Be(4); - result.Any(a => a.Contains("OCR")).Should().BeTrue(); - result.Any(a => a.Contains("PROP")).Should().BeTrue(); + result.Count.ShouldBe(4); + result.Any(a => a.Contains("OCR")).ShouldBeTrue(); + result.Any(a => a.Contains("PROP")).ShouldBeTrue(); } [Fact] public void Split_ReturnsWordsHandlingUnderscoresAsSpaces() { var result = NameSplitter.Split("Execute_Action"); - result.Count.Should().Be(2); - result.Any(a => a.Contains("Execute")).Should().BeTrue(); - result.Any(a => a.Contains("Action")).Should().BeTrue(); + result.Count.ShouldBe(2); + result.Any(a => a.Contains("Execute")).ShouldBeTrue(); + result.Any(a => a.Contains("Action")).ShouldBeTrue(); } } } diff --git a/CodeDocumentor.Test/Helper/ReturnCommentConstructionTests.cs b/CodeDocumentor.Test/Helper/ReturnCommentConstructionTests.cs index 6d9f3ef..469112d 100644 --- a/CodeDocumentor.Test/Helper/ReturnCommentConstructionTests.cs +++ b/CodeDocumentor.Test/Helper/ReturnCommentConstructionTests.cs @@ -1,8 +1,8 @@ using System.Diagnostics.CodeAnalysis; -using CodeDocumentor.Analyzers.Constructors; +using CodeDocumentor.Common.Constructors; using CodeDocumentor.Common.Models; -using FluentAssertions; using Microsoft.CodeAnalysis.CSharp; +using Shouldly; using Xunit; using Xunit.Abstractions; @@ -39,7 +39,7 @@ public void GenerateQualifiedNameComment_CreatesValidStringFromName(bool include var roc = TestFixture.BuildQualifiedNameSyntax("System", "String"); _options.IncludeStartingWordInText = includeStartingWordInText; var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be(startWord + ""); + comment.ShouldBe(startWord + ""); } [Theory] @@ -50,7 +50,7 @@ public void GenerateQualifiedNameComment_CreatesValidStringFromCustomInterface(b var roc = TestFixture.BuildQualifiedNameSyntax("Angler", "IClass"); _options.IncludeStartingWordInText = includeStartingWordInText; var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be(startWord + ""); + comment.ShouldBe(startWord + ""); } #endregion @@ -64,7 +64,7 @@ public void GenerateArrayTypeComment_CreatesValidStringFromNameAndForcesAnPrefix var roc = TestFixture.BuildArrayTypeSyntax(SyntaxKind.StringKeyword); _options.IncludeStartingWordInText = includeStartingWordInText; var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("an array of strings"); + comment.ShouldBe("an array of strings"); } #endregion @@ -77,7 +77,7 @@ public void GeneratePredefinedTypeSyntaxCommentWithCref_CreatesValidStringFromSt var roc = TestFixture.BuildPredefinedTypeSyntax(SyntaxKind.StringKeyword); _options.IncludeStartingWordInText = includeStartingWordInText; var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be(startWord + ""); + comment.ShouldBe(startWord + ""); } [Theory] @@ -88,7 +88,7 @@ public void GeneratePredefinedTypeSyntaxCommentWithCref_CreatesValidStringFromIn var roc = TestFixture.BuildPredefinedTypeSyntax(SyntaxKind.IntKeyword); _options.IncludeStartingWordInText = includeStartingWordInText; var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be(startWord + ""); + comment.ShouldBe(startWord + ""); } #endregion @@ -100,7 +100,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromIReadOnlyCollection var roc = TestFixture.BuildGenericNameSyntax("IReadOnlyCollection", SyntaxKind.StringKeyword); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A read only collection of strings."); + comment.ShouldBe("A read only collection of strings."); } [Fact] @@ -110,7 +110,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromIReadOnlyCollection var roc = TestFixture.BuildGenericNameSyntax("IReadOnlyCollection", list); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A read only collection of list of strings."); + comment.ShouldBe("A read only collection of list of strings."); } [Fact] @@ -121,7 +121,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromIReadOnlyCollection var roc = TestFixture.BuildGenericNameSyntax("IReadOnlyCollection", list); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A read only collection of a read only collection of strings."); + comment.ShouldBe("A read only collection of a read only collection of strings."); } #endregion @@ -133,7 +133,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromList() { var roc = TestFixture.BuildGenericNameSyntax("List", SyntaxKind.StringKeyword); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A list of strings."); + comment.ShouldBe("A list of strings."); } [Fact] @@ -143,7 +143,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromListOfList() var roc = TestFixture.BuildGenericNameSyntax("List", list); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A list of a list of strings."); + comment.ShouldBe("A list of a list of strings."); } [Fact] @@ -155,7 +155,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromListOfListOfList() var roc = TestFixture.BuildGenericNameSyntax("List", list2); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A list of a list of a list of strings."); + comment.ShouldBe("A list of a list of a list of strings."); } [Fact] @@ -163,7 +163,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromIList() { var roc = TestFixture.BuildGenericNameSyntax("IList", SyntaxKind.StringKeyword); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A list of strings."); + comment.ShouldBe("A list of strings."); } [Fact] @@ -172,7 +172,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromIListOfIList() var list = TestFixture.BuildGenericNameSyntax("IList", SyntaxKind.StringKeyword); var roc = TestFixture.BuildGenericNameSyntax("IList", list); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A list of a list of strings."); + comment.ShouldBe("A list of a list of strings."); } [Fact] @@ -180,7 +180,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromListOfInt() { var roc = TestFixture.BuildGenericNameSyntax("List", SyntaxKind.IntKeyword); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A list of integers."); + comment.ShouldBe("A list of integers."); } [Fact] @@ -189,7 +189,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromListOfListOfInt() var list = TestFixture.BuildGenericNameSyntax("List", SyntaxKind.IntKeyword); var roc = TestFixture.BuildGenericNameSyntax("IList", list); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A list of a list of integers."); + comment.ShouldBe("A list of a list of integers."); } #endregion @@ -201,7 +201,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromIEnumerable() { var roc = TestFixture.BuildGenericNameSyntax("IEnumerable", SyntaxKind.StringKeyword); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A list of strings."); + comment.ShouldBe("A list of strings."); } [Fact] @@ -210,7 +210,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromIEnumerableOfIEnume var list = TestFixture.BuildGenericNameSyntax("IEnumerable", SyntaxKind.StringKeyword); var roc = TestFixture.BuildGenericNameSyntax("IEnumerable", list); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A list of a list of strings."); + comment.ShouldBe("A list of a list of strings."); } #endregion @@ -222,7 +222,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromICollection() { var roc = TestFixture.BuildGenericNameSyntax("ICollection", SyntaxKind.StringKeyword); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A collection of strings."); + comment.ShouldBe("A collection of strings."); } [Fact] @@ -230,7 +230,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromCollection() { var roc = TestFixture.BuildGenericNameSyntax("Collection", SyntaxKind.StringKeyword); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A collection of strings."); + comment.ShouldBe("A collection of strings."); } #endregion @@ -242,7 +242,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromIDictionary() { var roc = TestFixture.BuildGenericNameSyntax("IDictionary", SyntaxKind.StringKeyword, SyntaxKind.StringKeyword); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A dictionary with a key of type string and a value of type string."); + comment.ShouldBe("A dictionary with a key of type string and a value of type string."); } [Fact] @@ -250,7 +250,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromIDictionaryOfInt() { var roc = TestFixture.BuildGenericNameSyntax("IDictionary", SyntaxKind.IntKeyword, SyntaxKind.IntKeyword); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A dictionary with a key of type integer and a value of type integer."); + comment.ShouldBe("A dictionary with a key of type integer and a value of type integer."); } [Fact] @@ -259,7 +259,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromDictionary() var roc = TestFixture.BuildGenericNameSyntax("Dictionary", SyntaxKind.StringKeyword, SyntaxKind.StringKeyword); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A dictionary with a key of type string and a value of type string."); + comment.ShouldBe("A dictionary with a key of type string and a value of type string."); } [Fact] @@ -269,7 +269,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromDictionaryWithListV var roc = TestFixture.BuildGenericNameSyntax("Dictionary", SyntaxKind.StringKeyword, list); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A dictionary with a key of type string and a value of type list of strings."); + comment.ShouldBe("A dictionary with a key of type string and a value of type list of strings."); } [Fact] @@ -280,7 +280,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromDictionaryWithListO var roc = TestFixture.BuildGenericNameSyntax("Dictionary", SyntaxKind.StringKeyword, list2); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("A dictionary with a key of type string and a value of type list of a list of strings."); + comment.ShouldBe("A dictionary with a key of type string and a value of type list of a list of strings."); } #endregion @@ -292,7 +292,7 @@ public void GenericTypeDefResult_CreatesValidStringFromString() var method = TestFixture.BuildMethodDeclarationSyntax("TResult", "Tester"); _options.IncludeStartingWordInText = true; var comment = _returnCommentBuilder.BuildComment(method.ReturnType, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("a "); + comment.ShouldBe("a "); } #endregion @@ -309,7 +309,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromTaskOfString(string _options.TryToIncludeCrefsForReturnTypes = true; var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be($"{prefix} of type " + (hasPeriod ? "." : "")); + comment.ShouldBe($"{prefix} of type " + (hasPeriod ? "." : "")); } [Theory] @@ -321,7 +321,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromTaskOfList(string t var list = TestFixture.BuildGenericNameSyntax("IList", SyntaxKind.StringKeyword); var roc = TestFixture.BuildGenericNameSyntax(type, list); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be($"{prefix} of a list of strings" + (hasPeriod ? "." : "")); + comment.ShouldBe($"{prefix} of a list of strings" + (hasPeriod ? "." : "")); } [Theory] @@ -334,7 +334,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromTaskOfDictionary(st var dict = TestFixture.BuildGenericNameSyntax("Dictionary", SyntaxKind.StringKeyword, list); var roc = TestFixture.BuildGenericNameSyntax(type, dict); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be($"{prefix} of a dictionary with a key of type string and a value of type list of strings" + (hasPeriod ? "." : "")); + comment.ShouldBe($"{prefix} of a dictionary with a key of type string and a value of type list of strings" + (hasPeriod ? "." : "")); } [Theory] @@ -346,7 +346,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromTaskOfCustomDoubleG var custom = TestFixture.BuildGenericNameSyntax("CustomDoubleGenericType", SyntaxKind.StringKeyword, SyntaxKind.StringKeyword); var roc = TestFixture.BuildGenericNameSyntax(type, custom); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be($"{prefix} of type CustomDoubleGenericType" + (hasPeriod ? "." : "")); + comment.ShouldBe($"{prefix} of type CustomDoubleGenericType" + (hasPeriod ? "." : "")); } [Theory] @@ -357,7 +357,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromTaskOfCustomClass(s { var roc = SyntaxFactory.ParseTypeName($"{type}"); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be($"{prefix} of type " + (hasPeriod ? "." : "")); + comment.ShouldBe($"{prefix} of type " + (hasPeriod ? "." : "")); } #endregion @@ -377,7 +377,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromUnknown() var roc = TestFixture.BuildGenericNameSyntax("Span", SyntaxKind.StringKeyword); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("Span"); + comment.ShouldBe("Span"); } [Fact] @@ -386,7 +386,7 @@ public void GenerateGenericTypeComment_CreatesValidStringFromUnknownGeneric() var roc = TestFixture.BuildGenericNameSyntax("CustomClass", SyntaxKind.StringKeyword, SyntaxKind.StringKeyword); var comment = _returnCommentBuilder.BuildComment(roc, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("CustomClass"); + comment.ShouldBe("CustomClass"); } #endregion @@ -399,10 +399,10 @@ public void IdentifierNameSyntaxComment_CreatesValidTypeParamRef() var roc = TestFixture.BuildMethodDeclarationSyntax("CustomClass", "TestMethod"); var returnType = TestFixture.GetReturnType(roc); - returnType.Should().NotBeNull(); + returnType.ShouldNotBeNull(); var comment = _returnCommentBuilder.BuildComment(returnType, _options, _testFixture.MockSettings.WordMaps); - comment.Should().Be("a "); + comment.ShouldBe("a "); } #endregion diff --git a/CodeDocumentor.Test/Helper/TranslatorTests.cs b/CodeDocumentor.Test/Helper/TranslatorTests.cs index 4ca8b33..e1764da 100644 --- a/CodeDocumentor.Test/Helper/TranslatorTests.cs +++ b/CodeDocumentor.Test/Helper/TranslatorTests.cs @@ -3,7 +3,7 @@ using CodeDocumentor.Common; using CodeDocumentor.Common.Extensions; using CodeDocumentor.Common.Models; -using FluentAssertions; +using Shouldly; using Xunit; using Xunit.Abstractions; @@ -49,7 +49,7 @@ public void TranslateText_ReturnsTranslatedStrings(string input, string output) temp.AddRange(Constants.INTERNAL_WORD_MAPS); _testFixure.MockSettings.WordMaps = temp.ToArray(); var translated = input.ApplyUserTranslations(_testFixure.MockSettings.WordMaps); - translated.Should().Be(output); + translated.ShouldBe(output); } } } diff --git a/CodeDocumentor.Test/Helper/WordExtensionsTests.cs b/CodeDocumentor.Test/Helper/WordExtensionsTests.cs index 70a853d..01afc77 100644 --- a/CodeDocumentor.Test/Helper/WordExtensionsTests.cs +++ b/CodeDocumentor.Test/Helper/WordExtensionsTests.cs @@ -1,5 +1,5 @@ using System; -using FluentAssertions; +using Shouldly; using Xunit; namespace CodeDocumentor.Test.Helper @@ -13,7 +13,7 @@ public class WordExtensionsTests public void IsVerbCombo_HandlesWordCorrectly(string first, string second, bool result) { var isVerb = first.IsVerbCombo(second); - isVerb.Should().Be(result); + isVerb.ShouldBe(result); } } } diff --git a/CodeDocumentor.Test/Interfaces/InterfaceUnitTests.cs b/CodeDocumentor.Test/Interfaces/InterfaceUnitTests.cs index 0f9dfaa..55f8c8f 100644 --- a/CodeDocumentor.Test/Interfaces/InterfaceUnitTests.cs +++ b/CodeDocumentor.Test/Interfaces/InterfaceUnitTests.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Interfaces; +using CodeDocumentor.Analyzers.Analyzers.Interfaces; using CodeDocumentor.Test.TestHelpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/CodeDocumentor.Test/Methods/MethodUnitTests.cs b/CodeDocumentor.Test/Methods/MethodUnitTests.cs index 80571b3..768f5ff 100644 --- a/CodeDocumentor.Test/Methods/MethodUnitTests.cs +++ b/CodeDocumentor.Test/Methods/MethodUnitTests.cs @@ -1,7 +1,6 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Methods; +using CodeDocumentor.Analyzers.Analyzers.Methods; using CodeDocumentor.Test.TestHelpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/CodeDocumentor.Test/Properties/PropertyUnitTests.cs b/CodeDocumentor.Test/Properties/PropertyUnitTests.cs index 065b99a..aac2dee 100644 --- a/CodeDocumentor.Test/Properties/PropertyUnitTests.cs +++ b/CodeDocumentor.Test/Properties/PropertyUnitTests.cs @@ -1,8 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Properties; +using CodeDocumentor.Analyzers.Analyzers.Properties; using CodeDocumentor.Test.TestHelpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/CodeDocumentor.Test/Records/RecordUnitTests.cs b/CodeDocumentor.Test/Records/RecordUnitTests.cs index be94719..f935b9b 100644 --- a/CodeDocumentor.Test/Records/RecordUnitTests.cs +++ b/CodeDocumentor.Test/Records/RecordUnitTests.cs @@ -1,6 +1,5 @@ using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Records; +using CodeDocumentor.Analyzers.Analyzers.Records; using CodeDocumentor.Test.TestHelpers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/CodeDocumentor.Test/TestFixture.cs b/CodeDocumentor.Test/TestFixture.cs index 6b7ee18..615415f 100644 --- a/CodeDocumentor.Test/TestFixture.cs +++ b/CodeDocumentor.Test/TestFixture.cs @@ -5,15 +5,16 @@ using System.IO; using System.Linq; using System.Reflection; -using CodeDocumentor.Analyzers.Locators; using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Locators; using CodeDocumentor.Common.Models; -using CodeDocumentor.Services; +using CodeDocumentor.Common.Services; using CodeDocumentor.Test.TestHelpers; -using FluentAssertions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Moq; +using Shouldly; using Xunit; using Xunit.Abstractions; @@ -61,9 +62,11 @@ public void Initialize(ITestOutputHelper output) CurrentTestName = output.GetTestName(); MockSettings = new TestSettings(); - ServiceLocator.SettingService = new SettingService(); + var mockLogger = new Mock(); + ServiceLocator.SettingService = new SettingService(mockLogger.Object); ServiceLocator.SettingService.StaticSettings = MockSettings; - ServiceLocator.Logger = new Logger(); + ServiceLocator.Logger = mockLogger.Object; + ServiceLocator.CommentBuilderService = new CommentBuilderService(mockLogger.Object, MockSettings); } public void SetPublicProcessingOption(ISettings o, string diagType) @@ -89,7 +92,7 @@ public void AssertOutputContainsCount(string[] source, string searchTerm, int nu where word.IndexOf(searchTerm, StringComparison.InvariantCultureIgnoreCase) > -1 select word; - matchQuery.Count().Should().Be(numOfTimes); + matchQuery.Count().ShouldBe(numOfTimes); } public static GenericNameSyntax BuildGenericNameSyntax(string listType, SyntaxKind innerKindKey, SyntaxKind innerKindValue) diff --git a/CodeDocumentor.sln b/CodeDocumentor.sln index 9eb723f..860bf32 100644 --- a/CodeDocumentor.sln +++ b/CodeDocumentor.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 18 -VisualStudioVersion = 18.0.11109.219 d18.0-oob +VisualStudioVersion = 18.0.11109.219 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{FC202D1D-DC77-4AD6-BFA7-AFD04EFCAB03}" ProjectSection(SolutionItems) = preProject @@ -9,19 +9,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt .gitignore = .gitignore LICENSE.txt = LICENSE.txt Readme.md = Readme.md + Readme2022.md = Readme2022.md + Reset-Exp.ps1 = Reset-Exp.ps1 EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeDocumentor.Test", "CodeDocumentor.Test\CodeDocumentor.Test.csproj", "{9F806DE0-98F2-4E73-87E5-6F683206E976}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeDocumentor", "CodeDocumentor\CodeDocumentor.csproj", "{0D6E3633-F45F-4883-AC62-DEC9F714AA9B}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Manifests", "Manifests", "{7D0C4464-A2B6-445E-A5B3-65B56D486D5D}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "vs2022", "vs2022", "{F2631D48-BF7F-4DF2-B9BC-6F698DF98F3F}" - ProjectSection(SolutionItems) = preProject - Manifests\vs2022\source.extension.vsixmanifest = Manifests\vs2022\source.extension.vsixmanifest - EndProjectSection -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GifInstruction", "GifInstruction", "{71CE4E03-2814-4AC9-88D4-968D55686C30}" ProjectSection(SolutionItems) = preProject GifInstruction\outOfProcess.png = GifInstruction\outOfProcess.png @@ -45,6 +40,20 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeDocumentor.Common", "Co EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeDocumentor.Analyzers", "CodeDocumentor.Analyzers\CodeDocumentor.Analyzers.csproj", "{738D16AF-93E6-4F9C-9F6A-9283A3E62243}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeDocumentor2026", "CodeDocumentor2026\CodeDocumentor2026.csproj", "{FE2D9454-9027-44A1-83B3-A4B0350EC8A0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2026", "2026", "{916832B5-AA2A-401F-A29B-A85E0AD7FE9D}" + ProjectSection(SolutionItems) = preProject + GifInstruction\2026\CodeDocumentor2026Demo.gif = GifInstruction\2026\CodeDocumentor2026Demo.gif + GifInstruction\2026\DocumentThisKeyboard.png = GifInstruction\2026\DocumentThisKeyboard.png + GifInstruction\2026\FileKeyboard.png = GifInstruction\2026\FileKeyboard.png + GifInstruction\2026\KeyboardSearch.png = GifInstruction\2026\KeyboardSearch.png + GifInstruction\2026\RightContext.png = GifInstruction\2026\RightContext.png + GifInstruction\2026\SolutionFile.png = GifInstruction\2026\SolutionFile.png + GifInstruction\2026\SolutionFolder.png = GifInstruction\2026\SolutionFolder.png + GifInstruction\2026\ToolsMenu.png = GifInstruction\2026\ToolsMenu.png + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -103,12 +112,24 @@ Global {738D16AF-93E6-4F9C-9F6A-9283A3E62243}.Release|x64.Build.0 = Release|Any CPU {738D16AF-93E6-4F9C-9F6A-9283A3E62243}.Release|x86.ActiveCfg = Release|Any CPU {738D16AF-93E6-4F9C-9F6A-9283A3E62243}.Release|x86.Build.0 = Release|Any CPU + {FE2D9454-9027-44A1-83B3-A4B0350EC8A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FE2D9454-9027-44A1-83B3-A4B0350EC8A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE2D9454-9027-44A1-83B3-A4B0350EC8A0}.Debug|x64.ActiveCfg = Debug|Any CPU + {FE2D9454-9027-44A1-83B3-A4B0350EC8A0}.Debug|x64.Build.0 = Debug|Any CPU + {FE2D9454-9027-44A1-83B3-A4B0350EC8A0}.Debug|x86.ActiveCfg = Debug|x86 + {FE2D9454-9027-44A1-83B3-A4B0350EC8A0}.Debug|x86.Build.0 = Debug|x86 + {FE2D9454-9027-44A1-83B3-A4B0350EC8A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FE2D9454-9027-44A1-83B3-A4B0350EC8A0}.Release|Any CPU.Build.0 = Release|Any CPU + {FE2D9454-9027-44A1-83B3-A4B0350EC8A0}.Release|x64.ActiveCfg = Release|Any CPU + {FE2D9454-9027-44A1-83B3-A4B0350EC8A0}.Release|x64.Build.0 = Release|Any CPU + {FE2D9454-9027-44A1-83B3-A4B0350EC8A0}.Release|x86.ActiveCfg = Release|x86 + {FE2D9454-9027-44A1-83B3-A4B0350EC8A0}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {F2631D48-BF7F-4DF2-B9BC-6F698DF98F3F} = {7D0C4464-A2B6-445E-A5B3-65B56D486D5D} + {916832B5-AA2A-401F-A29B-A85E0AD7FE9D} = {71CE4E03-2814-4AC9-88D4-968D55686C30} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B1C24040-E2CB-4A07-9065-4F50A8208F89} diff --git a/CodeDocumentor/CodeDocumentor.Package.cs b/CodeDocumentor/CodeDocumentor.Package.cs index 2d4e960..7e42937 100644 --- a/CodeDocumentor/CodeDocumentor.Package.cs +++ b/CodeDocumentor/CodeDocumentor.Package.cs @@ -2,14 +2,16 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; -using CodeDocumentor.Analyzers.Locators; using CodeDocumentor.Common; +using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Locators; using CodeDocumentor.Common.Models; -using CodeDocumentor.Services; +using CodeDocumentor.Common.Services; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Threading; using Task = System.Threading.Tasks.Task; +using CodeDocumentor.Analyzers.Services; [assembly: InternalsVisibleTo("CodeDocumentor.Test")] @@ -45,6 +47,14 @@ namespace CodeDocumentor.Vsix2022 //[ComVisible(true)] public sealed class CodeDocumentorPackage : AsyncPackage { + + static CodeDocumentorPackage() + { + ServiceLocator.Logger = new PreLoadLogger(); + ServiceLocator.SettingService = new PreLoadSettingService(); + ServiceLocator.CommentBuilderService = new CommentBuilderService(ServiceLocator.Logger, Settings.BuildDefaults()); ; + } + #region Package Members /// @@ -72,7 +82,7 @@ private void Load() { //this needs to be set here due to bootstrapping environment and where EventLog is available ServiceLocator.Logger = new Logger(); - ServiceLocator.SettingService = new SettingService(); + ServiceLocator.SettingService = new SettingService(new Logger()); //var hasCodeDocumentorInEditorConfig = await SlnHasEditorConfigAsync(hasCodeDocumentorInEditorConfig); @@ -82,6 +92,7 @@ private void Load() var settings = new Settings(); settings.SetFromOptionsGrid(options); ServiceLocator.SettingService.StaticSettings = settings; + ServiceLocator.CommentBuilderService = new CommentBuilderService(ServiceLocator.Logger, settings); } //private async System.Threading.Tasks.Task SlnHasEditorConfigAsync(bool hasCodeDocumentorInEditorConfig) diff --git a/CodeDocumentor/CodeDocumentor.csproj b/CodeDocumentor/CodeDocumentor.csproj index 7df8525..124d17d 100644 --- a/CodeDocumentor/CodeDocumentor.csproj +++ b/CodeDocumentor/CodeDocumentor.csproj @@ -23,7 +23,7 @@ true false true - false + true Program $(DevEnvDir)devenv.exe @@ -95,8 +95,6 @@ - - @@ -129,4 +127,4 @@ --> - \ No newline at end of file + diff --git a/CodeDocumentor/Helper/EditorConfigExtensions.cs b/CodeDocumentor/Helper/EditorConfigExtensions.cs index a6fbd3e..cf35079 100644 --- a/CodeDocumentor/Helper/EditorConfigExtensions.cs +++ b/CodeDocumentor/Helper/EditorConfigExtensions.cs @@ -1,8 +1,8 @@ using System; using System.Threading.Tasks; -using CodeDocumentor.Analyzers.Locators; using CodeDocumentor.Common; using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Locators; using CodeDocumentor.Common.Models; using Microsoft.CodeAnalysis.CodeFixes; @@ -17,7 +17,7 @@ public static async Task BuildSettingsAsync(this CodeFixContext conte var tree = await context.Document.GetSyntaxTreeAsync(); var opts = context.Document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(tree); var svc = ServiceLocator.SettingService; - return svc.BuildSettings(opts); + return svc?.BuildSettings(opts) ?? Settings.BuildDefaults(); } catch (Exception ex) { diff --git a/CodeDocumentor/Models/OptionPageGrid.cs b/CodeDocumentor/Models/OptionPageGrid.cs index e37e412..e0c91b8 100644 --- a/CodeDocumentor/Models/OptionPageGrid.cs +++ b/CodeDocumentor/Models/OptionPageGrid.cs @@ -2,9 +2,9 @@ using System.ComponentModel; using System.Runtime.InteropServices; using System.Windows.Forms; -using CodeDocumentor.Analyzers.Locators; using CodeDocumentor.Common; using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Locators; using CodeDocumentor.Common.Models; using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Shell; diff --git a/CodeDocumentor/Providers/BaseCodeFixProvider.cs b/CodeDocumentor/Providers/BaseCodeFixProvider.cs index 0909bd8..b868068 100644 --- a/CodeDocumentor/Providers/BaseCodeFixProvider.cs +++ b/CodeDocumentor/Providers/BaseCodeFixProvider.cs @@ -1,11 +1,19 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Analyzers.Analyzers.Classes; +using CodeDocumentor.Analyzers.Analyzers.Constructors; +using CodeDocumentor.Analyzers.Analyzers.Enums; +using CodeDocumentor.Analyzers.Analyzers.Fields; +using CodeDocumentor.Analyzers.Analyzers.Files; +using CodeDocumentor.Analyzers.Analyzers.Interfaces; +using CodeDocumentor.Analyzers.Analyzers.Methods; +using CodeDocumentor.Analyzers.Analyzers.Properties; +using CodeDocumentor.Analyzers.Analyzers.Records; using CodeDocumentor.Common; using CodeDocumentor.Common.Helpers; using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; @@ -37,11 +45,6 @@ public abstract class BaseCodeFixProvider : CodeFixProvider FileAnalyzerSettings.DiagnosticId, }); - public override FixAllProvider GetFixAllProvider() - { - return null; - } - /// /// Registers code fixes async. /// @@ -49,10 +52,10 @@ public override FixAllProvider GetFixAllProvider() /// A Task. protected async Task RegisterFileCodeFixesAsync(CodeFixContext context, Diagnostic diagnostic) { -#if DEBUG +#if !DEBUG ServiceLocator.Logger.LogDebug(Constants.CATEGORY, "!!!DISABLING FILE CODE FIX. EITHER TESTS ARE RUNNING OR DEBUGGER IS ATTACHED!!!"); return; -#endif +#else //build it up, but check for counts if anything actually needs to be shown var tempDoc = context.Document; var root = await tempDoc.GetSyntaxRootAsync(context.CancellationToken); @@ -61,22 +64,23 @@ protected async Task RegisterFileCodeFixesAsync(CodeFixContext context, Diagnost return; } var settings = await context.BuildSettingsAsync(); + var commentService = ServiceLocator.CommentBuilderService; TryHelper.Try(() => { var _nodesTempToReplace = new Dictionary(); //Order Matters var neededCommentCount = 0; - neededCommentCount += PropertyCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); - neededCommentCount += ConstructorCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); - neededCommentCount += EnumCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); - neededCommentCount += FieldCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); - neededCommentCount += MethodCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); + neededCommentCount += commentService.BuildPropertyComments(settings, PropertyAnalyzerSettings.DiagnosticId, root, _nodesTempToReplace); + neededCommentCount += commentService.BuildConstructorComments(settings, ConstructorAnalyzerSettings.DiagnosticId, root, _nodesTempToReplace); + neededCommentCount += commentService.BuildEnumComments(settings, EnumAnalyzerSettings.DiagnosticId, root, _nodesTempToReplace); + neededCommentCount += commentService.BuildFieldComments(settings, FieldAnalyzerSettings.DiagnosticId, root, _nodesTempToReplace); + neededCommentCount += commentService.BuildMethodComments(settings, MethodAnalyzerSettings.DiagnosticId, root, _nodesTempToReplace); root = root.ReplaceNodes(_nodesTempToReplace.Keys, (n1, n2) => _nodesTempToReplace[n1]); _nodesTempToReplace.Clear(); - neededCommentCount += InterfaceCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); - neededCommentCount += ClassCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); - neededCommentCount += RecordCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); + neededCommentCount += commentService.BuildInterfaceComments(settings, InterfaceAnalyzerSettings.DiagnosticId, root, _nodesTempToReplace); + neededCommentCount += commentService.BuildComments(settings, ClassAnalyzerSettings.DiagnosticId, root, _nodesTempToReplace); + neededCommentCount += commentService.BuildRecordComments(settings, RecordAnalyzerSettings.DiagnosticId, root, _nodesTempToReplace); var newRoot = root.ReplaceNodes(_nodesTempToReplace.Keys, (n1, n2) => _nodesTempToReplace[n1]); if (neededCommentCount == 0) { @@ -90,6 +94,7 @@ protected async Task RegisterFileCodeFixesAsync(CodeFixContext context, Diagnost equivalenceKey: FILE_FIX_TITLE), diagnostic); }, diagnostic.Id, EventLogger, eventId: Constants.EventIds.FILE_FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); +#endif } } } diff --git a/CodeDocumentor/Providers/Classes/ClassCodeFixProvider.cs b/CodeDocumentor/Providers/Classes/ClassCodeFixProvider.cs index 5be60ae..0751207 100644 --- a/CodeDocumentor/Providers/Classes/ClassCodeFixProvider.cs +++ b/CodeDocumentor/Providers/Classes/ClassCodeFixProvider.cs @@ -4,16 +4,15 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Analyzers.Analyzers.Classes; using CodeDocumentor.Common; +using CodeDocumentor.Common.Helper; using CodeDocumentor.Common.Helpers; using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace CodeDocumentor @@ -89,61 +88,10 @@ internal Task AddDocumentationHeaderAsync(ISettings settings, Document { return Task.Run(() => TryHelper.Try(() => { - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + var newDeclaration = ServiceLocator.CommentBuilderService.BuildNewDeclaration(declarationSyntax); var newRoot = root.ReplaceNode(declarationSyntax, newDeclaration); return document.WithSyntaxRoot(newRoot); }, ClassAnalyzerSettings.DiagnosticId, EventLogger, (_) => document, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.ADD_DOCUMENTATION_HEADER), cancellationToken); } - - /// - /// Builds the comments. This is only used in the file level fixProvider. - /// - /// The root. - /// The nodes to replace. - internal static int BuildComments(ISettings settings, SyntaxNode root, Dictionary nodesToReplace) - { - var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.ClassDeclaration)).OfType().ToArray(); - var neededCommentCount = 0; - TryHelper.Try(() => - { - foreach (var declarationSyntax in declarations) - { - if (settings.IsEnabledForPublicMembersOnly - && PrivateMemberVerifier.IsPrivateMember(declarationSyntax)) - { - continue; - } - if (declarationSyntax.HasSummary()) //if the class has comments dont redo it. User should update manually - { - continue; - } - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); - nodesToReplace.TryAdd(declarationSyntax, newDeclaration); - neededCommentCount++; - } - }, ClassAnalyzerSettings.DiagnosticId, EventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); - return neededCommentCount; - } - - private static ClassDeclarationSyntax BuildNewDeclaration(ISettings settings, ClassDeclarationSyntax declarationSyntax) - { - var commentHelper = ServiceLocator.CommentHelper; - var comment = commentHelper.CreateClassComment(declarationSyntax.Identifier.ValueText, settings.WordMaps); - var builder = ServiceLocator.DocumentationBuilder; - var list = builder.WithSummary(declarationSyntax, comment, settings.PreserveExistingSummaryText) - .WithTypeParamters(declarationSyntax) - .WithParameters(declarationSyntax, settings.WordMaps) - .WithExisting(declarationSyntax, Constants.REMARKS) - .WithExisting(declarationSyntax, Constants.EXAMPLE) - .Build(); - - var commentTrivia = SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, list); - - //append to any existing leading trivia [attributes, decorators, etc) - var leadingTrivia = declarationSyntax.GetLeadingTrivia(); - - var newDeclaration = declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); - return newDeclaration; - } } } diff --git a/CodeDocumentor/Providers/Constructors/ConstructorCodeFixProvider.cs b/CodeDocumentor/Providers/Constructors/ConstructorCodeFixProvider.cs index 2958717..ad1bec9 100644 --- a/CodeDocumentor/Providers/Constructors/ConstructorCodeFixProvider.cs +++ b/CodeDocumentor/Providers/Constructors/ConstructorCodeFixProvider.cs @@ -1,20 +1,19 @@ -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Analyzers.Analyzers.Constructors; using CodeDocumentor.Common; +using CodeDocumentor.Common.Helper; using CodeDocumentor.Common.Helpers; using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Extensions; namespace CodeDocumentor { @@ -75,63 +74,6 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) await RegisterFileCodeFixesAsync(context, diagnostic); } - /// - /// Builds the comments. This is only used in the file level fixProvider. - /// - /// The root. - /// The nodes to replace. - /// An int. - internal static int BuildComments(ISettings settings, SyntaxNode root, Dictionary nodesToReplace) - { - var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.ConstructorDeclaration)).OfType().ToArray(); - var neededCommentCount = 0; - TryHelper.Try(() => - { - foreach (var declarationSyntax in declarations) - { - if (settings.IsEnabledForPublicMembersOnly && PrivateMemberVerifier.IsPrivateMember(declarationSyntax)) - { - continue; - } - if (declarationSyntax.HasSummary()) - { - continue; - } - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); - nodesToReplace.TryAdd(declarationSyntax, newDeclaration); - neededCommentCount++; - } - }, ConstructorAnalyzerSettings.DiagnosticId, EventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); - return neededCommentCount; - } - - private static ConstructorDeclarationSyntax BuildNewDeclaration(ISettings settings, ConstructorDeclarationSyntax declarationSyntax) - { - var leadingTrivia = declarationSyntax.GetLeadingTrivia(); - var commentTrivia = CreateDocumentationCommentTriviaSyntax(settings, declarationSyntax); - var newDeclaration = declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); - return newDeclaration; - } - - /// - /// Creates documentation comment trivia syntax. - /// - /// The declaration syntax. - /// A DocumentationCommentTriviaSyntax. - private static DocumentationCommentTriviaSyntax CreateDocumentationCommentTriviaSyntax(ISettings settings, ConstructorDeclarationSyntax declarationSyntax) - { - var commentHelper = ServiceLocator.CommentHelper; - var comment = commentHelper.CreateConstructorComment(declarationSyntax.Identifier.ValueText, declarationSyntax.IsPrivate(), settings.WordMaps); - var builder = ServiceLocator.DocumentationBuilder; - var list = builder.WithSummary(declarationSyntax, comment, settings.PreserveExistingSummaryText) - .WithParameters(declarationSyntax, settings.WordMaps) - .WithExisting(declarationSyntax, Constants.REMARKS) - .WithExisting(declarationSyntax, Constants.EXAMPLE) - .Build(); - - return SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, list); - } - /// /// Adds documentation header async. /// @@ -144,7 +86,7 @@ private Task AddDocumentationHeaderAsync(ISettings settings, Document { return Task.Run(() => TryHelper.Try(() => { - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + var newDeclaration = ServiceLocator.CommentBuilderService.BuildNewDeclaration(settings, declarationSyntax); var newRoot = root.ReplaceNode(declarationSyntax, newDeclaration); return document.WithSyntaxRoot(newRoot); }, ConstructorAnalyzerSettings.DiagnosticId, EventLogger, (_) => document, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.ADD_DOCUMENTATION_HEADER), cancellationToken); diff --git a/CodeDocumentor/Providers/Enums/EnumCodeFixProvider.cs b/CodeDocumentor/Providers/Enums/EnumCodeFixProvider.cs index ec59a67..9368d79 100644 --- a/CodeDocumentor/Providers/Enums/EnumCodeFixProvider.cs +++ b/CodeDocumentor/Providers/Enums/EnumCodeFixProvider.cs @@ -1,19 +1,19 @@ -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Analyzers.Analyzers.Enums; using CodeDocumentor.Common; +using CodeDocumentor.Common.Helper; using CodeDocumentor.Common.Helpers; using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Extensions; namespace CodeDocumentor { @@ -70,45 +70,6 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) await RegisterFileCodeFixesAsync(context, diagnostic); } - /// - /// Builds the comments. This is only used in the file level fixProvider. - /// - /// The root. - /// The nodes to replace. - internal static int BuildComments(ISettings settings, SyntaxNode root, Dictionary nodesToReplace) - { - var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.EnumDeclaration)).OfType().ToArray(); - var neededCommentCount = 0; - TryHelper.Try(() => - { - foreach (var declarationSyntax in declarations) - { - if (declarationSyntax.HasSummary()) - { - continue; - } - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); - nodesToReplace.TryAdd(declarationSyntax, newDeclaration); - neededCommentCount++; - } - }, EnumAnalyzerSettings.DiagnosticId, EventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); - return neededCommentCount; - } - - private static EnumDeclarationSyntax BuildNewDeclaration(ISettings settings, EnumDeclarationSyntax declarationSyntax) - { - var leadingTrivia = declarationSyntax.GetLeadingTrivia(); - var commentHelper = ServiceLocator.CommentHelper; - var comment = commentHelper.CreateEnumComment(declarationSyntax.Identifier.ValueText, settings.WordMaps); - - var builder = ServiceLocator.DocumentationBuilder; - - var summaryNodes = builder.WithSummary(comment).Build(); - var commentTrivia = SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, summaryNodes); - var newDeclaration = declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); - return newDeclaration; - } - /// /// Adds documentation header async. /// @@ -121,7 +82,7 @@ private Task AddDocumentationHeaderAsync(ISettings settings, Document { return Task.Run(() => TryHelper.Try(() => { - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + var newDeclaration = ServiceLocator.CommentBuilderService.BuildNewDeclaration(settings, declarationSyntax); var newRoot = root.ReplaceNode(declarationSyntax, newDeclaration); return document.WithSyntaxRoot(newRoot); }, EnumAnalyzerSettings.DiagnosticId, EventLogger, (_) => document, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.ADD_DOCUMENTATION_HEADER), cancellationToken); diff --git a/CodeDocumentor/Providers/Fields/FieldCodeFixProvider.cs b/CodeDocumentor/Providers/Fields/FieldCodeFixProvider.cs index 61a2a28..64d25d4 100644 --- a/CodeDocumentor/Providers/Fields/FieldCodeFixProvider.cs +++ b/CodeDocumentor/Providers/Fields/FieldCodeFixProvider.cs @@ -1,20 +1,19 @@ -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Analyzers.Analyzers.Fields; using CodeDocumentor.Common; +using CodeDocumentor.Common.Helper; using CodeDocumentor.Common.Helpers; using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using SyntaxNode = Microsoft.CodeAnalysis.SyntaxNode; namespace CodeDocumentor { @@ -75,50 +74,6 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) await RegisterFileCodeFixesAsync(context, diagnostic); } - /// - /// Builds the comments. This is only used in the file level fixProvider. - /// - /// The root. - /// The nodes to replace. - internal static int BuildComments(ISettings settings, SyntaxNode root, Dictionary nodesToReplace) - { - var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.FieldDeclaration)).OfType().ToArray(); - var neededCommentCount = 0; - TryHelper.Try(() => - { - foreach (var declarationSyntax in declarations) - { - if (settings.IsEnabledForPublicMembersOnly && PrivateMemberVerifier.IsPrivateMember(declarationSyntax)) - { - continue; - } - if (declarationSyntax.HasSummary()) - { - continue; - } - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); - nodesToReplace.TryAdd(declarationSyntax, newDeclaration); - neededCommentCount++; - } - }, FieldAnalyzerSettings.DiagnosticId, EventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); - return neededCommentCount; - } - - private static FieldDeclarationSyntax BuildNewDeclaration(ISettings settings, FieldDeclarationSyntax declarationSyntax) - { - var leadingTrivia = declarationSyntax.GetLeadingTrivia(); - var field = declarationSyntax.DescendantNodes().OfType().FirstOrDefault(); - var commentHelper = ServiceLocator.CommentHelper; - var comment = commentHelper.CreateFieldComment(field?.Identifier.ValueText, settings.ExcludeAsyncSuffix, settings.WordMaps); - - var builder = ServiceLocator.DocumentationBuilder; - - var summaryNodes = builder.WithSummary(comment).Build(); - var commentTrivia = SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, summaryNodes); - var newDeclaration = declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); - return newDeclaration; - } - /// /// Adds documentation header async. /// @@ -131,7 +86,7 @@ private Task AddDocumentationHeaderAsync(ISettings settings, Document { return Task.Run(() => TryHelper.Try(() => { - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + var newDeclaration = ServiceLocator.CommentBuilderService.BuildNewDeclaration(settings, declarationSyntax); var newRoot = root.ReplaceNode(declarationSyntax, newDeclaration); return document.WithSyntaxRoot(newRoot); }, FieldAnalyzerSettings.DiagnosticId, EventLogger, (_) => document, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.ADD_DOCUMENTATION_HEADER), cancellationToken); diff --git a/CodeDocumentor/Providers/Files/FileCodeFixProvider.cs b/CodeDocumentor/Providers/Files/FileCodeFixProvider.cs index b4fd110..385c950 100644 --- a/CodeDocumentor/Providers/Files/FileCodeFixProvider.cs +++ b/CodeDocumentor/Providers/Files/FileCodeFixProvider.cs @@ -1,43 +1,14 @@ -using System.Collections.Generic; -using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Locators; -using CodeDocumentor.Common; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; namespace CodeDocumentor { - - /// - /// The class code fix provider. - /// [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(FileCodeFixProvider)), Shared] public class FileCodeFixProvider : BaseCodeFixProvider { - /// - /// The title. - /// - private const string Title = "Code Documentor this whole file"; - - - public override ImmutableArray FixableDiagnosticIds => ImmutableArray.CreateRange(new List { - ClassAnalyzerSettings.DiagnosticId, - PropertyAnalyzerSettings.DiagnosticId, - ConstructorAnalyzerSettings.DiagnosticId, - EnumAnalyzerSettings.DiagnosticId, - InterfaceAnalyzerSettings.DiagnosticId, - MethodAnalyzerSettings.DiagnosticId, - FieldAnalyzerSettings.DiagnosticId, - RecordAnalyzerSettings.DiagnosticId, - FileAnalyzerSettings.DiagnosticId, - }); - public override FixAllProvider GetFixAllProvider() { return null; @@ -51,46 +22,8 @@ public override FixAllProvider GetFixAllProvider() /// A Task. public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { -#if DEBUG - ServiceLocator.Logger.LogDebug(Constants.CATEGORY, "!!!DISABLING FILE CODE FIX. EITHER TESTS ARE RUNNING OR DEBUGGER IS ATTACHED!!!"); - return; -#endif var diagnostic = context.Diagnostics.First(); - var settings = await context.BuildSettingsAsync(); - //build it up, but check for counts if anything actually needs to be shown - var _nodesTempToReplace = new Dictionary(); - var tempDoc = context.Document; - var root = await tempDoc.GetSyntaxRootAsync(context.CancellationToken); - //Order Matters - var neededCommentCount = 0; - neededCommentCount += PropertyCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); - neededCommentCount += ConstructorCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); - neededCommentCount += EnumCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); - neededCommentCount += FieldCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); - neededCommentCount += MethodCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); - root = root.ReplaceNodes(_nodesTempToReplace.Keys, (n1, n2) => - { - return _nodesTempToReplace[n1]; - }); - _nodesTempToReplace.Clear(); - neededCommentCount += InterfaceCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); - neededCommentCount += ClassCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); - neededCommentCount += RecordCodeFixProvider.BuildComments(settings, root, _nodesTempToReplace); - var newRoot = root.ReplaceNodes(_nodesTempToReplace.Keys, (n1, n2) => - { - return _nodesTempToReplace[n1]; - }); - if (neededCommentCount == 0) - { - return; - } - - context.RegisterCodeFix( - CodeAction.Create( - title: Title, - createChangedDocument: (c) => Task.Run(() => context.Document.WithSyntaxRoot(newRoot), c), - equivalenceKey: Title), - diagnostic); + await RegisterFileCodeFixesAsync(context, diagnostic); } } } diff --git a/CodeDocumentor/Providers/Interfaces/InterfaceCodeFixProvider.cs b/CodeDocumentor/Providers/Interfaces/InterfaceCodeFixProvider.cs index 7e3e32a..a320a1e 100644 --- a/CodeDocumentor/Providers/Interfaces/InterfaceCodeFixProvider.cs +++ b/CodeDocumentor/Providers/Interfaces/InterfaceCodeFixProvider.cs @@ -1,19 +1,19 @@ -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Analyzers.Analyzers.Interfaces; using CodeDocumentor.Common; +using CodeDocumentor.Common.Helper; using CodeDocumentor.Common.Helpers; using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; namespace CodeDocumentor { @@ -70,47 +70,6 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) await RegisterFileCodeFixesAsync(context, diagnostic); } - /// - /// Builds the comments. This is only used in the file level fixProvider. - /// - /// The root. - /// The nodes to replace. - internal static int BuildComments(ISettings settings, SyntaxNode root, Dictionary nodesToReplace) - { - var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.InterfaceDeclaration)).OfType().ToArray(); - var neededCommentCount = 0; - TryHelper.Try(() => - { - foreach (var declarationSyntax in declarations) - { - if (declarationSyntax.HasSummary()) - { - continue; - } - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); - nodesToReplace.TryAdd(declarationSyntax, newDeclaration); - neededCommentCount++; - } - }, InterfaceAnalyzerSettings.DiagnosticId, EventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); - return neededCommentCount; - } - - private static InterfaceDeclarationSyntax BuildNewDeclaration(ISettings settings, InterfaceDeclarationSyntax declarationSyntax) - { - var commentHelper = ServiceLocator.CommentHelper; - var comment = commentHelper.CreateInterfaceComment(declarationSyntax.Identifier.ValueText, settings.WordMaps); - var builder = ServiceLocator.DocumentationBuilder; - var list = builder.WithSummary(declarationSyntax, comment, settings.PreserveExistingSummaryText) - .WithTypeParamters(declarationSyntax) - .Build(); - - var commentTrivia = SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, list); - - var leadingTrivia = declarationSyntax.GetLeadingTrivia(); - var newDeclaration = declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); - return newDeclaration; - } - /// /// Adds documentation header async. /// @@ -123,7 +82,7 @@ private Task AddDocumentationHeaderAsync(ISettings settings, Document { return Task.Run(() => TryHelper.Try(() => { - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + var newDeclaration = ServiceLocator.CommentBuilderService.BuildNewDeclaration(settings, declarationSyntax); var newRoot = root.ReplaceNode(declarationSyntax, newDeclaration); return document.WithSyntaxRoot(newRoot); }, InterfaceAnalyzerSettings.DiagnosticId, EventLogger, (_) => document, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.ADD_DOCUMENTATION_HEADER), cancellationToken); diff --git a/CodeDocumentor/Providers/Methods/MethodCodeFixProvider.cs b/CodeDocumentor/Providers/Methods/MethodCodeFixProvider.cs index 63b08e9..47859e6 100644 --- a/CodeDocumentor/Providers/Methods/MethodCodeFixProvider.cs +++ b/CodeDocumentor/Providers/Methods/MethodCodeFixProvider.cs @@ -1,19 +1,17 @@ -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Analyzers.Analyzers.Methods; using CodeDocumentor.Common; +using CodeDocumentor.Common.Helper; using CodeDocumentor.Common.Helpers; using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace CodeDocumentor @@ -80,74 +78,6 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) await RegisterFileCodeFixesAsync(context, diagnostic); } - /// - /// Builds the comments. This is only used in the file level fixProvider. - /// - /// The root. - /// The nodes to replace. - internal static int BuildComments(ISettings settings, SyntaxNode root, Dictionary nodesToReplace) - { - var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.MethodDeclaration)).OfType().ToArray(); - var neededCommentCount = 0; - TryHelper.Try(() => - { - foreach (var declarationSyntax in declarations) - { - if ( - !declarationSyntax.IsOwnedByInterface() && - settings.IsEnabledForPublicMembersOnly && PrivateMemberVerifier.IsPrivateMember(declarationSyntax) - ) - { - continue; - } - //if method is already commented dont redo it, user should update methods individually - if (declarationSyntax.HasSummary()) - { - continue; - } - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); - nodesToReplace.TryAdd(declarationSyntax, newDeclaration); - neededCommentCount++; - } - }, MethodAnalyzerSettings.DiagnosticId, EventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); - return neededCommentCount; - } - - private static MethodDeclarationSyntax BuildNewDeclaration(ISettings settings, MethodDeclarationSyntax declarationSyntax) - { - var leadingTrivia = declarationSyntax.GetLeadingTrivia(); - var commentTrivia = CreateDocumentationCommentTriviaSyntax(settings, declarationSyntax); - return declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); - } - - /// - /// Creates documentation comment trivia syntax. - /// - /// The declaration syntax. - /// A DocumentationCommentTriviaSyntax. - private static DocumentationCommentTriviaSyntax CreateDocumentationCommentTriviaSyntax(ISettings settings, MethodDeclarationSyntax declarationSyntax) - { - var commentHelper = ServiceLocator.CommentHelper; - var summaryText = commentHelper.CreateMethodComment(declarationSyntax.Identifier.ValueText, - declarationSyntax.ReturnType, - settings.UseToDoCommentsOnSummaryError, - settings.TryToIncludeCrefsForReturnTypes, - settings.ExcludeAsyncSuffix, - settings.WordMaps); - var builder = ServiceLocator.DocumentationBuilder; - - var list = builder.WithSummary(declarationSyntax, summaryText, settings.PreserveExistingSummaryText) - .WithTypeParamters(declarationSyntax) - .WithParameters(declarationSyntax, settings.WordMaps) - .WithExceptionTypes(declarationSyntax) - .WithExisting(declarationSyntax, Constants.REMARKS) - .WithExisting(declarationSyntax, Constants.EXAMPLE) - .WithReturnType(declarationSyntax, settings.UseNaturalLanguageForReturnNode, settings.TryToIncludeCrefsForReturnTypes, settings.WordMaps) - .Build(); - - return SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, list); - } - /// /// Adds documentation header async. /// @@ -155,12 +85,12 @@ private static DocumentationCommentTriviaSyntax CreateDocumentationCommentTrivia /// The root. /// The declaration syntax. /// The cancellation token. - /// A Task. + /// A Document. private Task AddDocumentationHeaderAsync(ISettings settings, Document document, SyntaxNode root, MethodDeclarationSyntax declarationSyntax, CancellationToken cancellationToken) { return Task.Run(() => TryHelper.Try(() => { - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + var newDeclaration = ServiceLocator.CommentBuilderService.BuildNewDeclaration(settings, declarationSyntax); var newRoot = root.ReplaceNode(declarationSyntax, newDeclaration); return document.WithSyntaxRoot(newRoot); }, MethodAnalyzerSettings.DiagnosticId, EventLogger, (_) => document, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.ADD_DOCUMENTATION_HEADER), cancellationToken); diff --git a/CodeDocumentor/Providers/Properties/PropertyCodeFixProvider.cs b/CodeDocumentor/Providers/Properties/PropertyCodeFixProvider.cs index 0197971..cfe75ca 100644 --- a/CodeDocumentor/Providers/Properties/PropertyCodeFixProvider.cs +++ b/CodeDocumentor/Providers/Properties/PropertyCodeFixProvider.cs @@ -1,20 +1,17 @@ -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Analyzers.Analyzers.Properties; using CodeDocumentor.Common; +using CodeDocumentor.Common.Helper; using CodeDocumentor.Common.Helpers; using CodeDocumentor.Common.Interfaces; -using CodeDocumentor.Common.Models; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace CodeDocumentor @@ -76,67 +73,6 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) await RegisterFileCodeFixesAsync(context, diagnostic); } - /// - /// Builds the comments. This is only used in the file level fixProvider. - /// - /// The root. - /// The nodes to replace. - internal static int BuildComments(ISettings settings, SyntaxNode root, Dictionary nodesToReplace) - { - var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.PropertyDeclaration)).OfType().ToArray(); - var neededCommentCount = 0; - TryHelper.Try(() => - { - foreach (var declarationSyntax in declarations) - { - if (settings.IsEnabledForPublicMembersOnly && PrivateMemberVerifier.IsPrivateMember(declarationSyntax)) - { - continue; - } - if (declarationSyntax.HasSummary()) - { - continue; - } - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); - nodesToReplace.TryAdd(declarationSyntax, newDeclaration); - neededCommentCount++; - } - }, PropertyAnalyzerSettings.DiagnosticId, EventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); - return neededCommentCount; - } - - private static PropertyDeclarationSyntax BuildNewDeclaration(ISettings settings, PropertyDeclarationSyntax declarationSyntax) - { - var isBoolean = declarationSyntax.IsPropertyReturnTypeBool(); - - var hasSetter = declarationSyntax.PropertyHasSetter(); - - var commentHelper = ServiceLocator.CommentHelper; - var propertyComment = commentHelper.CreatePropertyComment(declarationSyntax.Identifier.ValueText, isBoolean, - hasSetter, settings.ExcludeAsyncSuffix, settings.WordMaps); - var builder = ServiceLocator.DocumentationBuilder; - - var returnOptions = new ReturnTypeBuilderOptions - { - TryToIncludeCrefsForReturnTypes = settings.TryToIncludeCrefsForReturnTypes, - GenerateReturnStatement = settings.IncludeValueNodeInProperties, - ReturnGenericTypeAsFullString = false, - IncludeStartingWordInText = true, - UseProperCasing = true - }; - var list = builder.WithSummary(declarationSyntax, propertyComment, settings.PreserveExistingSummaryText) - .WithPropertyValueTypes(declarationSyntax, returnOptions, settings.WordMaps) - .WithExisting(declarationSyntax, Constants.REMARKS) - .WithExisting(declarationSyntax, Constants.EXAMPLE) - .Build(); - - var commentTrivia = SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, list); - - var leadingTrivia = declarationSyntax.GetLeadingTrivia(); - var newDeclaration = declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); - return newDeclaration; - } - /// /// Adds documentation header async. /// @@ -149,7 +85,7 @@ private Task AddDocumentationHeaderAsync(ISettings settings, Document { return Task.Run(() => TryHelper.Try(() => { - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + var newDeclaration = ServiceLocator.CommentBuilderService.BuildNewDeclaration(settings, declarationSyntax); var newRoot = root.ReplaceNode(declarationSyntax, newDeclaration); return document.WithSyntaxRoot(newRoot); }, PropertyAnalyzerSettings.DiagnosticId, EventLogger, (_) => document, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.ADD_DOCUMENTATION_HEADER), cancellationToken); diff --git a/CodeDocumentor/Providers/Records/RecordCodeFixProvider.cs b/CodeDocumentor/Providers/Records/RecordCodeFixProvider.cs index 1a7f4d5..c498bd6 100644 --- a/CodeDocumentor/Providers/Records/RecordCodeFixProvider.cs +++ b/CodeDocumentor/Providers/Records/RecordCodeFixProvider.cs @@ -1,20 +1,19 @@ -using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CodeDocumentor.Analyzers; -using CodeDocumentor.Analyzers.Helper; -using CodeDocumentor.Analyzers.Locators; +using CodeDocumentor.Analyzers.Analyzers.Records; using CodeDocumentor.Common; +using CodeDocumentor.Common.Helper; using CodeDocumentor.Common.Helpers; using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Locators; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; namespace CodeDocumentor { @@ -87,61 +86,10 @@ internal Task AddDocumentationHeaderAsync(ISettings settings, Document { return Task.Run(() => TryHelper.Try(() => { - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); + var newDeclaration = ServiceLocator.CommentBuilderService.BuildNewDeclaration(settings, declarationSyntax); var newRoot = root.ReplaceNode(declarationSyntax, newDeclaration); return document.WithSyntaxRoot(newRoot); }, RecordAnalyzerSettings.DiagnosticId, EventLogger, (_) => document, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.ADD_DOCUMENTATION_HEADER), cancellationToken); } - - /// - /// Builds the comments. This is only used in the file level fixProvider. - /// - /// The root. - /// The nodes to replace. - internal static int BuildComments(ISettings settings, SyntaxNode root, Dictionary nodesToReplace) - { - var declarations = root.DescendantNodes().Where(w => w.IsKind(SyntaxKind.RecordDeclaration)).OfType().ToArray(); - var neededCommentCount = 0; - TryHelper.Try(() => - { - foreach (var declarationSyntax in declarations) - { - if (settings.IsEnabledForPublicMembersOnly - && PrivateMemberVerifier.IsPrivateMember(declarationSyntax)) - { - continue; - } - if (declarationSyntax.HasSummary()) //if record already has comments dont redo it. User should update this manually - { - continue; - } - var newDeclaration = BuildNewDeclaration(settings, declarationSyntax); - nodesToReplace.TryAdd(declarationSyntax, newDeclaration); - neededCommentCount++; - } - }, RecordAnalyzerSettings.DiagnosticId, EventLogger, eventId: Constants.EventIds.FIXER, category: Constants.EventIds.Categories.BUILD_COMMENTS); - return neededCommentCount; - } - - private static RecordDeclarationSyntax BuildNewDeclaration(ISettings settings, RecordDeclarationSyntax declarationSyntax) - { - var commentHelper = ServiceLocator.CommentHelper; - var comment = commentHelper.CreateRecordComment(declarationSyntax.Identifier.ValueText, settings.WordMaps); - var builder = ServiceLocator.DocumentationBuilder; - - var list = builder.WithSummary(declarationSyntax, comment, settings.PreserveExistingSummaryText) - .WithTypeParamters(declarationSyntax) - .WithExisting(declarationSyntax, Constants.REMARKS) - .WithExisting(declarationSyntax, Constants.EXAMPLE) - .Build(); - - var commentTrivia = SyntaxFactory.DocumentationCommentTrivia(SyntaxKind.SingleLineDocumentationCommentTrivia, list); - - //append to any existing leading trivia [attributes, decorators, etc) - var leadingTrivia = declarationSyntax.GetLeadingTrivia(); - - var newDeclaration = declarationSyntax.WithLeadingTrivia(leadingTrivia.UpsertLeadingTrivia(commentTrivia)); - return newDeclaration; - } } } diff --git a/CodeDocumentor/Services/SettingService.cs b/CodeDocumentor/Services/SettingService.cs index 1c670b2..69e9624 100644 --- a/CodeDocumentor/Services/SettingService.cs +++ b/CodeDocumentor/Services/SettingService.cs @@ -1,19 +1,24 @@ using System; using System.Linq; -using CodeDocumentor.Analyzers.Locators; -using CodeDocumentor.Common; using CodeDocumentor.Common.Interfaces; using CodeDocumentor.Common.Models; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; -namespace CodeDocumentor.Services +namespace CodeDocumentor.Common.Services { public class SettingService : ISettingService { + private readonly IEventLogger _eventLogger; private ISettings _staticSettings = Settings.BuildDefaults(); - public ISettings StaticSettings { + public SettingService(IEventLogger eventLogger) + { + _eventLogger = eventLogger; + } + + public ISettings StaticSettings + { get => _staticSettings.Clone(); set => _staticSettings = value; } @@ -22,12 +27,6 @@ public ISettings BuildSettings(SyntaxNodeAnalysisContext context) var opts = context.Options.AnalyzerConfigOptionsProvider.GetOptions(context.Node.SyntaxTree); return BuildSettings(opts); } - [Obsolete("Use BuildSettings with ISettings parameter")] - public ISettings BuildSettings(SyntaxNodeAnalysisContext context, ISettings Settings) - { - var opts = context.Options.AnalyzerConfigOptionsProvider.GetOptions(context.Node.SyntaxTree); - return BuildSettings(opts); - } public ISettings BuildSettings(AnalyzerConfigOptions options) { @@ -35,11 +34,11 @@ public ISettings BuildSettings(AnalyzerConfigOptions options) var defaultSev = DiagnosticSeverity.Warning; if (!CanReadEditorConfig(options)) { - ServiceLocator.Logger.LogDebug(Constants.CATEGORY, $"{nameof(BuildSettings)}: CanReadEditorConfig == false"); + _eventLogger.LogDebug(Constants.CATEGORY, $"{nameof(BuildSettings)}: CanReadEditorConfig == false"); //no editorconfig, return the settings we have return StaticSettings; } - ServiceLocator.Logger.LogDebug(Constants.CATEGORY, $"{nameof(BuildSettings)}: CanReadEditorConfig == true"); + _eventLogger.LogDebug(Constants.CATEGORY, $"{nameof(BuildSettings)}: CanReadEditorConfig == true"); settings.ClassDiagnosticSeverity = ConvertToDiagnosticSeverity(options, "codedocumentor_class_diagram_severity", defaultSev); settings.ConstructorDiagnosticSeverity = ConvertToDiagnosticSeverity(options, "codedocumentor_constructor_diagram_severity", defaultSev); settings.DefaultDiagnosticSeverity = ConvertToDiagnosticSeverity(options, "codedocumentor_default_diagram_severity", defaultSev); diff --git a/CodeDocumentor/tools/install.ps1 b/CodeDocumentor/tools/install.ps1 deleted file mode 100644 index c1c3d88..0000000 --- a/CodeDocumentor/tools/install.ps1 +++ /dev/null @@ -1,58 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -if($project.Object.SupportsPackageDependencyResolution) -{ - if($project.Object.SupportsPackageDependencyResolution()) - { - # Do not install analyzers via install.ps1, instead let the project system handle it. - return - } -} - -$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve - -foreach($analyzersPath in $analyzersPaths) -{ - if (Test-Path $analyzersPath) - { - # Install the language agnostic analyzers. - foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll) - { - if($project.Object.AnalyzerReferences) - { - $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) - } - } - } -} - -# $project.Type gives the language name like (C# or VB.NET) -$languageFolder = "" -if($project.Type -eq "C#") -{ - $languageFolder = "cs" -} -if($project.Type -eq "VB.NET") -{ - $languageFolder = "vb" -} -if($languageFolder -eq "") -{ - return -} - -foreach($analyzersPath in $analyzersPaths) -{ - # Install language specific analyzers. - $languageAnalyzersPath = join-path $analyzersPath $languageFolder - if (Test-Path $languageAnalyzersPath) - { - foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll) - { - if($project.Object.AnalyzerReferences) - { - $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) - } - } - } -} \ No newline at end of file diff --git a/CodeDocumentor/tools/uninstall.ps1 b/CodeDocumentor/tools/uninstall.ps1 deleted file mode 100644 index 65a8623..0000000 --- a/CodeDocumentor/tools/uninstall.ps1 +++ /dev/null @@ -1,65 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -if($project.Object.SupportsPackageDependencyResolution) -{ - if($project.Object.SupportsPackageDependencyResolution()) - { - # Do not uninstall analyzers via uninstall.ps1, instead let the project system handle it. - return - } -} - -$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve - -foreach($analyzersPath in $analyzersPaths) -{ - # Uninstall the language agnostic analyzers. - if (Test-Path $analyzersPath) - { - foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll) - { - if($project.Object.AnalyzerReferences) - { - $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) - } - } - } -} - -# $project.Type gives the language name like (C# or VB.NET) -$languageFolder = "" -if($project.Type -eq "C#") -{ - $languageFolder = "cs" -} -if($project.Type -eq "VB.NET") -{ - $languageFolder = "vb" -} -if($languageFolder -eq "") -{ - return -} - -foreach($analyzersPath in $analyzersPaths) -{ - # Uninstall language specific analyzers. - $languageAnalyzersPath = join-path $analyzersPath $languageFolder - if (Test-Path $languageAnalyzersPath) - { - foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll) - { - if($project.Object.AnalyzerReferences) - { - try - { - $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) - } - catch - { - - } - } - } - } -} \ No newline at end of file diff --git a/CodeDocumentor2026/CodeDocumentor2026.csproj b/CodeDocumentor2026/CodeDocumentor2026.csproj new file mode 100644 index 0000000..7e8a9a6 --- /dev/null +++ b/CodeDocumentor2026/CodeDocumentor2026.csproj @@ -0,0 +1,139 @@ + + + + 17.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + true + + + + + + + + Debug + AnyCPU + 2.0 + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {FE2D9454-9027-44A1-83B3-A4B0350EC8A0} + Library + Properties + CodeDocumentor2026 + CodeDocumentor2026 + v4.8 + true + true + true + true + false + true + true + Program + $(DevEnvDir)devenv.exe + /rootsuffix Exp + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + True + True + False + + + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + Component + + + + + + + Designer + + + + + + + + + + + + + + compile; build; native; contentfiles; analyzers; buildtransitive + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + 9.0.9 + + + + + + + + Menus.ctmenu + + + Always + true + + + + + + + {7cc64cdf-a7ff-463b-8f05-c37a6c0a820c} + CodeDocumentor.Common + + + + + true + VSPackage + + + + + + \ No newline at end of file diff --git a/CodeDocumentor2026/CodeDocumentor2026Package.cs b/CodeDocumentor2026/CodeDocumentor2026Package.cs new file mode 100644 index 0000000..2ce997f --- /dev/null +++ b/CodeDocumentor2026/CodeDocumentor2026Package.cs @@ -0,0 +1,187 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Threading; +using CodeDocumentor.Common; +using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Models; +using CodeDocumentor.Common.Services; +using CodeDocumentor2026.Commands.Context; +using CodeDocumentor2026.Commands.Menu; +using CodeDocumentor2026.Models; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Task = System.Threading.Tasks.Task; + +namespace CodeDocumentor2026 +{ + /// + /// This is the class that implements the package exposed by this assembly. + /// + /// + /// + /// The minimum requirement for a class to be considered a valid package for Visual Studio + /// is to implement the IVsPackage interface and register itself with the shell. + /// This package uses the helper classes defined inside the Managed Package Framework (MPF) + /// to do it: it derives from the Package class that provides the implementation of the + /// IVsPackage interface and uses the registration attributes defined in the framework to + /// register itself and its components with the shell. These attributes tell the pkgdef creation + /// utility what data to put into .pkgdef file. + /// + /// + /// To get loaded into VS, the package must be referred by <Asset Type="Microsoft.VisualStudio.VsPackage" ...> in .vsixmanifest file. + /// + /// + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")] + [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] + [Guid(VsixOptions.PackageGuidString)] + [ProvideService(typeof(ICommentBuilderService), IsAsyncQueryable = true)] + [ProvideMenuResource("Menus.ctmenu", 1)] + [ProvideOptionPage(typeof(OptionPageGrid), OptionPageGrid.Category, OptionPageGrid.SubCategory, 1000, 1001, true)] + [ProvideAutoLoad(UIContextGuids80.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)] + public sealed class CodeDocumentor2026Package : AsyncPackage + { + /// + /// Initializes a new instance of the class. + /// + public CodeDocumentor2026Package() + { + // Inside this method you can place any initialization code that does not require + // any Visual Studio service because at this point the package object is created but + // not sited yet inside Visual Studio environment. The place to do all the other + // initialization is the Initialize method. + } + + #region Package Members + + /// + /// Initialization of the package; this method is called right after the package is sited, so this is the place + /// where you can put all the initialization code that rely on services provided by VisualStudio. + /// + /// A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down. + /// A provider for progress updates. + /// A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method. + protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) + { + LogDebug("Package InitializeAsync - START"); + + try + { + LogDebug("Package Switching to main thread..."); + // When initialized asynchronously, the current thread may be a background thread at this point. + // Do any initialization that requires the UI thread after switching to the UI thread. + await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + LogDebug("Package Successfully switched to main thread"); + + // Ensure cancellation wasn't requested before continuing + cancellationToken.ThrowIfCancellationRequested(); + LogDebug("Package Cancellation token check passed"); + + var options = (OptionPageGrid)GetDialogPage(typeof(OptionPageGrid)); + LogDebug("Package Got options page"); + + var settings = new Settings2026(); + settings.SetFromOptionsGrid(options); + LogDebug("Package Created settings from options"); + + // TEMPORARILY COMMENTED OUT FOR TESTING - Register services first with proper error handling + LogDebug("Package Starting service registration..."); + await RegisterServicesAsync(cancellationToken, settings); + LogDebug("Package Service registration completed"); + + LogDebug("Package Starting command initialization..."); + await InitializeCommandsAsync(cancellationToken); + LogDebug("Package Command initialization completed"); + + LogDebug("Package InitializeAsync - SUCCESS"); + } + catch (OperationCanceledException ex) + { + LogDebug($"Package InitializeAsync - CANCELED: {ex.Message}"); + throw; + } + catch (Exception ex) + { + LogDebug($"Package InitializeAsync - ERROR: {ex}"); + throw; + } + } + private async Task RegisterServicesAsync(CancellationToken cancellationToken, IBaseSettings settings) + { + try + { + LogDebug("Package RegisterServicesAsync - START"); + + var settingServiceCallback = new AsyncServiceCreatorCallback(async (IAsyncServiceContainer container, CancellationToken ct, Type serviceType) => + { + LogDebug($"Package Service callback for {serviceType?.Name ?? "null"}"); + + if (typeof(ICommentBuilderService) == serviceType) + { + LogDebug("Package Creating ICommentBuilderService..."); + + var logger = new Logger(); + LogDebug("Package Created logger"); + + var svc = new CommentBuilderService(logger, settings); + LogDebug("Package Created CommentBuilderService successfully"); + + return svc; + } + return null; + }); + + LogDebug("Package Adding service to container..."); + AddService(typeof(ICommentBuilderService), settingServiceCallback, true); + LogDebug("Package RegisterServicesAsync - SUCCESS"); + } + catch (Exception ex) + { + LogDebug($"Package RegisterServicesAsync - ERROR: {ex}"); + throw; + } + } + + private async Task InitializeCommandsAsync(CancellationToken cancellationToken) + { + try + { + LogDebug("Package InitializeCommandsAsync - START"); + + // Initialize commands with proper error handling + LogDebug("Package Initializing CodeDocumentorFileMenu..."); + await CodeDocumentorFileMenu.InitializeAsync(this); + LogDebug("Package CodeDocumentorFileMenu initialized"); + + LogDebug("Package Initializing CodeDocumentorContextCommand..."); + await CodeDocumentorContextCommand.InitializeAsync(this); + LogDebug("Package CodeDocumentorContextCommand initialized"); + + LogDebug("Package Initializing CodeDocumentorEditorCommand..."); + await CodeDocumentorEditorCommand.InitializeAsync(this); + LogDebug("Package CodeDocumentorEditorCommand initialized"); + + LogDebug("Package InitializeCommandsAsync - SUCCESS"); + } + catch (Exception ex) + { + LogDebug($"Package InitializeCommandsAsync - ERROR: {ex}"); + throw; + } + } + + private static void LogDebug(string message) + { + try + { + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] {message}"); + } + catch + { + // Don't let logging failures crash the extension + } + } + + #endregion + } +} diff --git a/CodeDocumentor2026/CodeDocumentor2026Package.vsct b/CodeDocumentor2026/CodeDocumentor2026Package.vsct new file mode 100644 index 0000000..8e67427 --- /dev/null +++ b/CodeDocumentor2026/CodeDocumentor2026Package.vsct @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code Documentor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CodeDocumentor2026/Commands/Context/CodeDocumentorContextCommand.cs b/CodeDocumentor2026/Commands/Context/CodeDocumentorContextCommand.cs new file mode 100644 index 0000000..ce9240d --- /dev/null +++ b/CodeDocumentor2026/Commands/Context/CodeDocumentorContextCommand.cs @@ -0,0 +1,252 @@ +using System; +using System.ComponentModel.Design; +using System.Threading; +using CodeDocumentor.Common.Interfaces; +using CodeDocumentor2026.Executors; +using EnvDTE; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Task = System.Threading.Tasks.Task; + +namespace CodeDocumentor2026.Commands.Context +{ + /// + /// Unified command handler for both file and folder context menu items + /// + internal sealed class CodeDocumentorContextCommand + { + /// File context command ID. + public const int FileCommandId = 6013; + + /// Folder context command ID. + public const int FolderCommandId = 6012; + + /// Command menu group (command set GUID). + public static readonly Guid _commandSet = CodeDocumentor.Common.Constants.CommandSetId; + + /// VS Package that provides this command, not null. + private readonly AsyncPackage _package; + + private readonly SDTE _sdteService; + private readonly ICommentBuilderService _commentBuilderService; + private readonly TextSelectionExecutor _textSelectionExecutor; + private readonly IVsThreadedWaitDialogFactory _dialogFactory; + private readonly SelectedItemCountExecutor _selectedItemCountExecutor; + private readonly CommentExecutor _commentExecutor; + + /// + /// Initializes a new instance of the class. + /// Adds command handlers for both file and folder context menus. + /// + /// Owner package, not null. + /// Command service to add command to, not null. + private CodeDocumentorContextCommand(AsyncPackage package, OleMenuCommandService commandService, SDTE SDTEService, + ICommentBuilderService commentBuilderService, TextSelectionExecutor textSelectionExecutor, + IVsThreadedWaitDialogFactory dialogFactory, SelectedItemCountExecutor selectedItemCountExecutor, + CommentExecutor commentExecutor) + { + LogDebug("ContextCommand Constructor - START"); + + _package = package ?? throw new ArgumentNullException(nameof(package)); + commandService = commandService ?? throw new ArgumentNullException(nameof(commandService)); + _sdteService = SDTEService; + _commentBuilderService = commentBuilderService; + _textSelectionExecutor = textSelectionExecutor; + _dialogFactory = dialogFactory; + _selectedItemCountExecutor = selectedItemCountExecutor; + _commentExecutor = commentExecutor; + + // Register both file and folder commands with the same handler + var fileCommandID = new CommandID(_commandSet, FileCommandId); + var folderCommandID = new CommandID(_commandSet, FolderCommandId); + + LogDebug($"ContextCommand Creating File MenuCommand with GUID: {_commandSet}, ID: {FileCommandId}"); + var fileMenuItem = new MenuCommand(Execute, fileCommandID); + commandService.AddCommand(fileMenuItem); + + LogDebug($"ContextCommand Creating Folder MenuCommand with GUID: {_commandSet}, ID: {FolderCommandId}"); + var folderMenuItem = new MenuCommand(Execute, folderCommandID); + commandService.AddCommand(folderMenuItem); + + LogDebug("ContextCommand Constructor - SUCCESS"); + } + + /// Gets the instance of the command. + public static CodeDocumentorContextCommand Instance + { + get; + private set; + } + + /// Gets the service provider from the owner package. + private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider + { + get + { + return _package; + } + } + + /// Initializes the singleton instance of the command. + /// Owner package, not null. + public static async Task InitializeAsync(AsyncPackage package) + { + try + { + LogDebug("ContextCommand InitializeAsync - START"); + + // Switch to the main thread - the call to AddCommand requires the UI thread. + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); + LogDebug("ContextCommand Switched to main thread"); + + LogDebug("ContextCommand Getting IMenuCommandService..."); + var commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; + LogDebug($"ContextCommand IMenuCommandService result: {(commandService != null ? "SUCCESS" : "NULL")}"); + + LogDebug("ContextCommand Getting ICommentBuilderService..."); + var attributeService = await package.GetServiceAsync(typeof(ICommentBuilderService)) as ICommentBuilderService; + LogDebug($"ContextCommand ICommentBuilderService result: {(attributeService != null ? "SUCCESS" : "NULL")}"); + + LogDebug("ContextCommand Getting SVsThreadedWaitDialogFactory..."); + var dialogFactory = await package.GetServiceAsync(typeof(SVsThreadedWaitDialogFactory)) as IVsThreadedWaitDialogFactory; + LogDebug($"ContextCommand SVsThreadedWaitDialogFactory result: {(dialogFactory != null ? "SUCCESS" : "NULL")}"); + + LogDebug("ContextCommand Getting SDTE service..."); + var SDTE = await package.GetServiceAsync(typeof(SDTE)) as SDTE; + LogDebug($"ContextCommand SDTE service result: {(SDTE != null ? "SUCCESS" : "NULL")}"); + + LogDebug("ContextCommand Creating executor objects..."); + var textSelectionExecutor = new TextSelectionExecutor(); + var selectedItemCountExecutor = new SelectedItemCountExecutor(); + var commentExecutor = new CommentExecutor(); + LogDebug("ContextCommand Executor objects created"); + + // Only create instance if required services are available + if (commandService != null && attributeService != null && SDTE != null) + { + LogDebug("ContextCommand All required services available - creating instance"); + Instance = new CodeDocumentorContextCommand(package, commandService, SDTE, attributeService, textSelectionExecutor, + dialogFactory, selectedItemCountExecutor, commentExecutor); + LogDebug("ContextCommand InitializeAsync - SUCCESS"); + } + else + { + var errorMsg = "ContextCommand: Failed to get required services - " + + $"CommandService: {commandService != null}, CommentService: {attributeService != null}, SDTE: {SDTE != null}"; + LogDebug($"ContextCommand InitializeAsync - FAILED: {errorMsg}"); + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] {errorMsg}"); + } + } + catch (Exception ex) + { + var errorMsg = $"ContextCommand initialization error: {ex}"; + LogDebug($"ContextCommand InitializeAsync - ERROR: {errorMsg}"); + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] {errorMsg}"); + throw; + } + } + + /// + /// This function is the callback used to execute the command when either context menu item is clicked. + /// Handles both file and folder context menu items with the same logic. + /// + /// Event sender. + /// Event args. + private void Execute(object sender, EventArgs e) + { + try + { + LogDebug("ContextCommand Execute - START"); + ThreadHelper.ThrowIfNotOnUIThread(); + + if (_sdteService == null || _commentBuilderService == null) + { + var errorMsg = "ContextCommand.Execute: Required services not available - " + + $"SDTE: {_sdteService != null}, CommentService: {_commentBuilderService != null}"; + LogDebug($"ContextCommand Execute - FAILED: {errorMsg}"); + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] {errorMsg}"); + return; + } + + LogDebug("ContextCommand Getting DTE from SDTE service..."); + var dte = _sdteService as DTE; + LogDebug($"ContextCommand DTE result: {(dte != null ? "SUCCESS" : "NULL")}"); + + if (dte?.SelectedItems == null || dte.SelectedItems.Count <= 0) + { + LogDebug("ContextCommand Execute - No selected items"); + return; + } + + LogDebug($"ContextCommand Selected items count: {dte.SelectedItems.Count}"); + var totalCount = _selectedItemCountExecutor.Execute(dte.SelectedItems); + LogDebug($"ContextCommand Total count from executor: {totalCount}"); + + IVsThreadedWaitDialog2 dialog = null; + if (totalCount > 1 && _dialogFactory != null) + { + LogDebug("ContextCommand Creating progress dialog..."); + //https://www.visualstudiogeeks.com/extensions/visualstudio/using-progress-dialog-in-visual-studio-extensions + _dialogFactory.CreateInstance(out dialog); + } + + var cts = new CancellationTokenSource(); + + if (dialog == null || + dialog.StartWaitDialogWithPercentageProgress("CodeDocumentor: Documenting Progress", "", $"0 of {totalCount} Processed", + null, CodeDocumentor2026.Constants.DIALOG_ACTION, true, 0, totalCount, 0) != VSConstants.S_OK) + { + dialog = null; + LogDebug("ContextCommand Progress dialog not available or failed to start"); + } + else + { + LogDebug("ContextCommand Progress dialog started successfully"); + } + + try + { + LogDebug("ContextCommand Starting comment executor..."); + _commentExecutor.Execute(dte.SelectedItems, cts, dialog, totalCount, _textSelectionExecutor, + (content) => + { + LogDebug($"ContextCommand Processing content length: {content?.Length ?? 0}"); + var result = _commentBuilderService.AddDocumentation(content); + LogDebug($"ContextCommand Result content length: {result?.Length ?? 0}"); + return result; + }, CodeDocumentor2026.Constants.DIALOG_ACTION); + LogDebug("ContextCommand Comment executor completed"); + } + finally + { + LogDebug("ContextCommand Ending progress dialog..."); + var usercancel = 0; + dialog?.EndWaitDialog(out usercancel); + LogDebug($"ContextCommand Progress dialog ended, user canceled: {usercancel}"); + } + + LogDebug("ContextCommand Execute - SUCCESS"); + } + catch (Exception ex) + { + var errorMsg = $"ContextCommand.Execute error: {ex}"; + LogDebug($"ContextCommand Execute - ERROR: {errorMsg}"); + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] {errorMsg}"); + // Don't re-throw to prevent VS crashes - just log the error + } + } + + private static void LogDebug(string message) + { + try + { + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] {message}"); + } + catch + { + // Don't let logging failures crash the extension + } + } + } +} diff --git a/CodeDocumentor2026/Commands/Context/CodeDocumentorEditorCommand.cs b/CodeDocumentor2026/Commands/Context/CodeDocumentorEditorCommand.cs new file mode 100644 index 0000000..210f9d1 --- /dev/null +++ b/CodeDocumentor2026/Commands/Context/CodeDocumentorEditorCommand.cs @@ -0,0 +1,345 @@ +using System; +using System.ComponentModel.Design; +using System.Threading.Tasks; +using CodeDocumentor.Common.Interfaces; +using CodeDocumentor2026.Extensions; +using EnvDTE; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Task = System.Threading.Tasks.Task; + +namespace CodeDocumentor2026.Commands.Context +{ + /// + /// Command handler for editor context menu to add documentation at cursor position + /// + internal sealed class CodeDocumentorEditorCommand + { + /// Editor context command ID. + public const int EditorCommandId = 6014; + + public const int EditorWholeFileCommandId = 6015; + + public const int QuickActionCommandId = 6017; + + public const int QuickActionWholeFileCommandId = 6018; + + /// Command menu group (command set GUID). + public static readonly Guid _commandSet = CodeDocumentor.Common.Constants.CommandSetId; + + /// VS Package that provides this command, not null. + private readonly AsyncPackage _package; + + private readonly ICommentBuilderService _commentBuilderService; + + /// + /// Initializes a new instance of the class. + /// + /// Owner package, not null. + /// Command service to add command to, not null. + /// + private CodeDocumentorEditorCommand(AsyncPackage package, OleMenuCommandService commandService, + ICommentBuilderService commentBuilderService) + { + _package = package ?? throw new ArgumentNullException(nameof(package)); + commandService = commandService ?? throw new ArgumentNullException(nameof(commandService)); + _commentBuilderService = commentBuilderService; + var editorCommandID = new CommandID(_commandSet, EditorCommandId); + var editorMenuItem = new OleMenuCommand(Execute, editorCommandID); + editorMenuItem.BeforeQueryStatus += OnBeforeQueryStatus; + commandService.AddCommand(editorMenuItem); + + var editorCommandWholeFileID = new CommandID(_commandSet, EditorWholeFileCommandId); + var editorWholeFileMenuItem = new OleMenuCommand(Execute, editorCommandWholeFileID); + commandService.AddCommand(editorWholeFileMenuItem); + + // New Quick Actions menu commands + var quickActionCommandID = new CommandID(_commandSet, QuickActionCommandId); + var quickActionMenuItem = new OleMenuCommand(Execute, quickActionCommandID); + quickActionMenuItem.BeforeQueryStatus += OnBeforeQueryStatus; + commandService.AddCommand(quickActionMenuItem); + + var quickActionWholeFileCommandID = new CommandID(_commandSet, QuickActionWholeFileCommandId); + var quickActionWholeFileMenuItem = new OleMenuCommand(Execute, quickActionWholeFileCommandID); + commandService.AddCommand(quickActionWholeFileMenuItem); + } + + /// Gets the instance of the command. + public static CodeDocumentorEditorCommand Instance + { + get; + private set; + } + + /// Initializes the singleton instance of the command. + /// Owner package, not null. + public static async Task InitializeAsync(AsyncPackage package) + { + try + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); + var commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; + var attributeService = await package.GetServiceAsync(typeof(ICommentBuilderService)) as ICommentBuilderService; + if (commandService != null && attributeService != null) + { + Instance = new CodeDocumentorEditorCommand(package, commandService, attributeService); + } + } + catch (Exception ex) + { + var errorMsg = $"EditorCommand initialization error: {ex}"; + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] {errorMsg}"); + throw; + } + } + + /// + /// Gets the syntax node at the current cursor/right-click position using DTE + /// + /// The documentable syntax node at cursor position, or null if none found + private async Task GetSyntaxNodeAtCursorAsync() + { + try + { + var documentInfo = await GetCurrentDocumentInfoAsync(); + if (documentInfo == null) + { + return null; + } + + // Find documentable node at cursor position + return FindDocumentableNode(documentInfo.Root, documentInfo.OriginalLine, documentInfo.OriginalColumn); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] GetSyntaxNodeAtCursorAsync error: {ex}"); + return null; + } + } + + /// + /// Called before the command is displayed to determine if it should be visible/enabled + /// + private async void OnBeforeQueryStatus(object sender, EventArgs e) + { + var command = sender as OleMenuCommand; + command.Visible = false; + command.Enabled = false; + if (command == null) + { + + return; + } + + try + { + // Check if we can find a documentable node at the cursor position + var targetNode = await GetSyntaxNodeAtCursorAsync(); + if (targetNode == null) + { + return; + } + + // Only show the menu item if we found a valid documentable node + command.Visible = true; + command.Enabled = true; + } + catch + { + //NO OPT + } + } + + /// + /// Executes the command when the editor context menu item is clicked + /// + private async void Execute(object sender, EventArgs e) + { + var command = sender as OleMenuCommand; + try + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + var documentInfo = await GetCurrentDocumentInfoAsync(); + if (documentInfo == null) + { + return; + } + + if (command.CommandID.ID == EditorWholeFileCommandId || + command.CommandID.ID == QuickActionWholeFileCommandId) + { + var documentedFile = _commentBuilderService.AddDocumentation(documentInfo.DocumentText); + UpdateDocumentAndFormat(documentInfo, documentedFile); + return; + } + + + // Find documentable node at cursor position in THIS syntax tree + var targetNode = FindDocumentableNode(documentInfo.Root, documentInfo.OriginalLine, documentInfo.OriginalColumn); + if (targetNode == null) + { + return; + } + + // We need this incase its re-creating existing documentation + var originalCommentLineCount = _commentBuilderService.GetDocumentationLineCount(targetNode); + var newDeclaration = _commentBuilderService.BuildNewDocumentationNode(targetNode); + if (newDeclaration == null) + { + return; + } + + var commentLineCount = _commentBuilderService.GetDocumentationLineCount(newDeclaration); + + var newRoot = documentInfo.Root.ReplaceNode(targetNode, newDeclaration); + var updatedText = newRoot.ToFullString(); + + // Update document + if (updatedText != documentInfo.DocumentText) + { + UpdateDocumentAndFormat(documentInfo, updatedText); + + documentInfo.TextSelection.SetCursorToLine(documentInfo.OriginalLine + (commentLineCount - originalCommentLineCount), documentInfo.OriginalColumn); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] EditorCommand.Execute error: {ex}"); + } + } + + private static void UpdateDocumentAndFormat(DocumentInfo documentInfo, string updatedText) + { + ThreadHelper.ThrowIfNotOnUIThread(); + var editPoint = documentInfo.TextDocument.StartPoint.CreateEditPoint(); + editPoint.ReplaceText( + documentInfo.TextDocument.EndPoint, + updatedText, + (int)vsEPReplaceTextOptions.vsEPReplaceTextAutoformat + ); + + // Try to format the document after insertion + try + { + editPoint.SmartFormat(documentInfo.TextDocument.StartPoint.CreateEditPoint()); + } + catch + { + // If SmartFormat fails, continue without formatting + } + } + + /// + /// Helper class to hold document information + /// + private class DocumentInfo + { + public SyntaxNode Root { get; set; } + public string DocumentText { get; set; } + public EnvDTE.TextDocument TextDocument { get; set; } + public TextSelection TextSelection { get; set; } + public int OriginalLine { get; set; } + public int OriginalColumn { get; set; } + } + + /// + /// Gets current document information including syntax tree, cursor position, etc. + /// + private async Task GetCurrentDocumentInfoAsync() + { + try + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + // Get DTE service + var dte = await _package.GetServiceAsync(typeof(SDTE)) as DTE; + if (dte?.ActiveDocument == null) + { + return null; + } + + // Check if it's a C# file + var activeDocument = dte.ActiveDocument; + if (!activeDocument.Name.EndsWith(".cs", StringComparison.OrdinalIgnoreCase)) + { + return null; + } + + // Get text selection to find cursor position + var textSelection = activeDocument.Selection as TextSelection; + if (textSelection == null) + { + return null; + } + + // Get the document text + var textDocument = activeDocument.Object("TextDocument") as EnvDTE.TextDocument; + if (textDocument == null) + { + return null; + } + + var startPoint = textDocument.StartPoint.CreateEditPoint(); + var documentText = startPoint.GetText(textDocument.EndPoint); + + // Parse with Roslyn + var syntaxTree = CSharpSyntaxTree.ParseText(documentText); + var root = await syntaxTree.GetRootAsync(); + + return new DocumentInfo + { + Root = root, + DocumentText = documentText, + TextDocument = textDocument, + TextSelection = textSelection, + OriginalLine = textSelection.ActivePoint.Line, + OriginalColumn = textSelection.ActivePoint.LineCharOffset + }; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] GetCurrentDocumentInfoAsync error: {ex}"); + return null; + } + } + + /// + /// Finds a documentable syntax node at the specified position. + /// Only returns a node if the cursor is directly on a documentable node - does not traverse up the tree. + /// + private SyntaxNode FindDocumentableNode(SyntaxNode root, int line, int column) + { + try + { + // Convert line/column to absolute position + var sourceText = root.SyntaxTree.GetText(); + var position = sourceText.Lines[line - 1].Start + (column - 1); // Convert from 1-based to 0-based + + // Ensure position is within bounds + if (position < 0 || position >= sourceText.Length) + { + return null; + } + + // Find the node at the exact cursor position + var nodeAtPosition = root.FindNode(Microsoft.CodeAnalysis.Text.TextSpan.FromBounds(position, position)); + + // Use the service to determine if it's documentable - don't traverse up + if (_commentBuilderService.IsDocumentableNode(nodeAtPosition)) + { + return nodeAtPosition; + } + + return null; + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] FindDocumentableNode error: {ex}"); + return null; + } + } + } +} diff --git a/CodeDocumentor2026/Commands/Menu/CodeDocumentorFileMenu.cs b/CodeDocumentor2026/Commands/Menu/CodeDocumentorFileMenu.cs new file mode 100644 index 0000000..bf0f4de --- /dev/null +++ b/CodeDocumentor2026/Commands/Menu/CodeDocumentorFileMenu.cs @@ -0,0 +1,190 @@ +using System; +using System.ComponentModel.Design; +using CodeDocumentor.Common.Interfaces; +using CodeDocumentor2026.Executors; +using EnvDTE; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Task = System.Threading.Tasks.Task; + +namespace CodeDocumentor2026.Commands.Menu +{ + /// Command handler + internal sealed class CodeDocumentorFileMenu + { + /// Command ID. + public const int CommandId = 6011; + + /// Command menu group (command set GUID). + public static readonly Guid _commandSet = CodeDocumentor.Common.Constants.CommandSetId; + + /// VS Package that provides this command, not null. + private readonly AsyncPackage _package; + + private readonly ICommentBuilderService _commentBuilderService; + private readonly TextSelectionExecutor _textSelectionExecutor; + private readonly SDTE _sdteService; + + /// + /// Initializes a new instance of the class. Adds our command handlers + /// for menu (commands must exist in the command table file) + /// + /// Owner package, not null. + /// Command service to add command to, not null. + private CodeDocumentorFileMenu(AsyncPackage package, OleMenuCommandService commandService, + SDTE SDTEService, ICommentBuilderService commentBuilderService, TextSelectionExecutor textSelectionExecutor) + { + LogDebug("FileMenu Constructor - START"); + + _package = package ?? throw new ArgumentNullException(nameof(package)); + _sdteService = SDTEService; + _commentBuilderService = commentBuilderService; + _textSelectionExecutor = textSelectionExecutor; + commandService = commandService ?? throw new ArgumentNullException(nameof(commandService)); + + var menuCommandID = new CommandID(_commandSet, CommandId); + LogDebug($"FileMenu Creating MenuCommand with GUID: {_commandSet}, ID: {CommandId}"); + + var menuItem = new MenuCommand(Execute, menuCommandID); + commandService.AddCommand(menuItem); + + LogDebug("FileMenu Constructor - SUCCESS"); + } + + /// Gets the instance of the command. + public static CodeDocumentorFileMenu Instance + { + get; + private set; + } + + /// Gets the service provider from the owner package. + private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider + { + get + { + return _package; + } + } + + /// Initializes the singleton instance of the command. + /// Owner package, not null. + public static async Task InitializeAsync(AsyncPackage package) + { + try + { + LogDebug("FileMenu InitializeAsync - START"); + + // Switch to the main thread - the call to AddCommand in ProtoCommand's constructor requires the UI thread. + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); + LogDebug("FileMenu Switched to main thread"); + + LogDebug("FileMenu Getting IMenuCommandService..."); + var commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; + LogDebug($"FileMenu IMenuCommandService result: {(commandService != null ? "SUCCESS" : "NULL")}"); + + LogDebug("FileMenu Getting ICommentBuilderService..."); + var commentService = await package.GetServiceAsync(typeof(ICommentBuilderService)) as ICommentBuilderService; + LogDebug($"FileMenu ICommentBuilderService result: {(commentService != null ? "SUCCESS" : "NULL")}"); + + LogDebug("FileMenu Getting SDTE service..."); + var SDTE = await package.GetServiceAsync(typeof(SDTE)) as SDTE; + LogDebug($"FileMenu SDTE service result: {(SDTE != null ? "SUCCESS" : "NULL")}"); + + LogDebug("FileMenu Creating TextSelectionExecutor..."); + var textSelectionExecutor = new TextSelectionExecutor(); + LogDebug("FileMenu TextSelectionExecutor created"); + + // Only create instance if all services are available + if (commandService != null && commentService != null && SDTE != null) + { + LogDebug("FileMenu All services available - creating instance"); + Instance = new CodeDocumentorFileMenu(package, commandService, SDTE, commentService, textSelectionExecutor); + LogDebug("FileMenu InitializeAsync - SUCCESS"); + } + else + { + var errorMsg = "FileMenu: Failed to get required services - " + + $"CommandService: {commandService != null}, CommentService: {commentService != null}, SDTE: {SDTE != null}"; + LogDebug($"FileMenu InitializeAsync - FAILED: {errorMsg}"); + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] {errorMsg}"); + } + } + catch (Exception ex) + { + var errorMsg = $"FileMenu initialization error: {ex}"; + LogDebug($"FileMenu InitializeAsync - ERROR: {errorMsg}"); + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] {errorMsg}"); + throw; + } + } + + /// + /// This function is the callback used to execute the command when the menu item is clicked. See the + /// constructor to see how the menu item is associated with this function using OleMenuCommandService + /// service and MenuCommand class. + /// + /// Event sender. + /// Event args. + private void Execute(object sender, EventArgs e) + { + try + { + LogDebug("FileMenu Execute - START"); + ThreadHelper.ThrowIfNotOnUIThread(); + + if (_sdteService == null || _commentBuilderService == null || _textSelectionExecutor == null) + { + var errorMsg = "FileMenu.Execute: Required services not available - " + + $"SDTE: {_sdteService != null}, CommentService: {_commentBuilderService != null}, TextExecutor: {_textSelectionExecutor != null}"; + LogDebug($"FileMenu Execute - FAILED: {errorMsg}"); + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] {errorMsg}"); + return; + } + + LogDebug("FileMenu Getting DTE from SDTE service..."); + var dte = _sdteService as DTE; + LogDebug($"FileMenu DTE result: {(dte != null ? "SUCCESS" : "NULL")}"); + + if (dte?.ActiveDocument != null) + { + LogDebug($"FileMenu Active document: {dte.ActiveDocument.Name}"); + LogDebug("FileMenu Executing text selection processor..."); + _textSelectionExecutor.Execute((TextSelection)dte.ActiveDocument.Selection, + (contents) => + { + LogDebug($"FileMenu Processing content length: {contents?.Length ?? 0}"); + var result = _commentBuilderService.AddDocumentation(contents); + LogDebug($"FileMenu Result content length: {result?.Length ?? 0}"); + return result; + }); + + LogDebug("FileMenu Execute - SUCCESS"); + } + else + { + LogDebug("FileMenu Execute - No active document"); + } + } + catch (Exception ex) + { + var errorMsg = $"FileMenu.Execute error: {ex}"; + LogDebug($"FileMenu Execute - ERROR: {errorMsg}"); + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] {errorMsg}"); + // Don't re-throw to prevent VS crashes - just log the error + } + } + + private static void LogDebug(string message) + { + try + { + System.Diagnostics.Debug.WriteLine($"[CodeDocumentor2026] {message}"); + } + catch + { + // Don't let logging failures crash the extension + } + } + } +} diff --git a/CodeDocumentor2026/Constants.cs b/CodeDocumentor2026/Constants.cs new file mode 100644 index 0000000..6d3bd7a --- /dev/null +++ b/CodeDocumentor2026/Constants.cs @@ -0,0 +1,7 @@ +namespace CodeDocumentor2026 +{ + internal class Constants + { + internal const string DIALOG_ACTION = "Adding Documentation"; + } +} diff --git a/CodeDocumentor2026/Executors/CommentExecutor.cs b/CodeDocumentor2026/Executors/CommentExecutor.cs new file mode 100644 index 0000000..fb277e4 --- /dev/null +++ b/CodeDocumentor2026/Executors/CommentExecutor.cs @@ -0,0 +1,126 @@ +using System; +using System.Threading; +using EnvDTE; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace CodeDocumentor2026.Executors +{ + + public class CommentExecutor + { + + public void Execute(SelectedItems selectedItems, CancellationTokenSource cts, + IVsThreadedWaitDialog2 dialog, int totalCount, + TextSelectionExecutor textSelectionExecutor, + Func projectItemApplyAttributing, + string dialogAction = Constants.DIALOG_ACTION) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + if (selectedItems == null) + { + return; + } + + var currentCount = 0; + bool cancelProcessing = false; + + foreach (SelectedItem selectedItem in selectedItems) + { + dialog?.HasCanceled(out cancelProcessing); + if (cancelProcessing) + { + cts.Cancel(); + break; + } + if (selectedItem.ProjectItem == null) + { + continue; + } + Action projectItemAttributingComplete = (fileName) => + { + ThreadHelper.ThrowIfNotOnUIThread(); + currentCount++; + dialog?.UpdateProgress($"{dialogAction}: {fileName}", $"{currentCount} of {totalCount} Processed", dialogAction, currentCount, totalCount, false, out cancelProcessing); + if (cancelProcessing) + { + cts.Cancel(); + } + }; + + Action projectItemAttributingStarted = (fileName) => + { + ThreadHelper.ThrowIfNotOnUIThread(); + dialog?.UpdateProgress($"{dialogAction}: {fileName}", $"{currentCount} of {totalCount} Processed", dialogAction, currentCount, totalCount, false, out cancelProcessing); + if (cancelProcessing) + { + cts.Cancel(); + } + }; + + ProcessProjectItem(selectedItem.ProjectItem, cts.Token, + textSelectionExecutor, + projectItemAttributingComplete, + projectItemAttributingStarted, + projectItemApplyAttributing); + } + } + + private void ProcessProjectItem(ProjectItem projectItem, CancellationToken token, + TextSelectionExecutor textSelectionExecutor, + Action projectItemAttributingComplete, + Action projectItemAttributingStarted, + Func projectItemApplyAttributing) + { + ThreadHelper.ThrowIfNotOnUIThread(); + if (token.IsCancellationRequested) + { + return; + } + if (projectItem.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFolder) + { + if (projectItem.ProjectItems.Count > 0) + { + foreach (ProjectItem item in projectItem.ProjectItems) + { + ProcessProjectItem(item, token, textSelectionExecutor, projectItemAttributingComplete, projectItemAttributingStarted, projectItemApplyAttributing); + } + } + return; + } + if (projectItem.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFile) + { + var fullPath = projectItem.Properties.Item("FullPath")?.Value?.ToString(); + var name = projectItem.Name; + projectItemAttributingStarted?.Invoke(name); + var isOpen = projectItem.IsOpen[EnvDTE.Constants.vsViewKindTextView]; + if (!isOpen) + { + if (fullPath?.EndsWith(".cs") == true) + { + var window = projectItem.Open(EnvDTE.Constants.vsViewKindTextView); + window.Activate(); + //process file + if (projectItem.Document != null) + { + projectItem.Document.Activate(); + textSelectionExecutor.Execute((TextSelection)projectItem.Document.Selection, (contents) => projectItemApplyAttributing.Invoke(contents)); + } + projectItemAttributingComplete?.Invoke(name); + } + } + else if (fullPath?.EndsWith(".cs") == true) + { + //process file + if (projectItem.Document != null) + { + projectItem.Document.Activate(); + textSelectionExecutor.Execute((TextSelection)projectItem.Document.Selection, (contents) => projectItemApplyAttributing.Invoke(contents)); + } + projectItemAttributingComplete?.Invoke(name); + } + } + } + } +} diff --git a/CodeDocumentor2026/Executors/SelectedItemCountExecutor.cs b/CodeDocumentor2026/Executors/SelectedItemCountExecutor.cs new file mode 100644 index 0000000..963d8b9 --- /dev/null +++ b/CodeDocumentor2026/Executors/SelectedItemCountExecutor.cs @@ -0,0 +1,51 @@ +using EnvDTE; +using Microsoft.VisualStudio.Shell; + +namespace CodeDocumentor2026.Executors +{ + + public class SelectedItemCountExecutor + { + public int Execute(SelectedItems selectedItems) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + if (selectedItems == null) + { + return 0; + } + + var totalCount = 0; + foreach (SelectedItem selectedItem in selectedItems) + { + if (selectedItem.ProjectItem == null) + { + continue; + } + GetTotalItemCount(selectedItem.ProjectItem, ref totalCount); + } + return totalCount; + } + + private void GetTotalItemCount(ProjectItem projectItem, ref int count) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + if (projectItem.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFile) + { + var fullPath = projectItem.Properties.Item("FullPath")?.Value?.ToString(); + if (fullPath?.EndsWith(".cs") == true) + { + count++; + } + } + if (projectItem.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFolder && projectItem.ProjectItems.Count > 0) + { + foreach (ProjectItem item in projectItem.ProjectItems) + { + GetTotalItemCount(item, ref count); + } + } + } + } +} diff --git a/CodeDocumentor2026/Executors/TextSelectionExecutor.cs b/CodeDocumentor2026/Executors/TextSelectionExecutor.cs new file mode 100644 index 0000000..927d271 --- /dev/null +++ b/CodeDocumentor2026/Executors/TextSelectionExecutor.cs @@ -0,0 +1,26 @@ +using System; +using EnvDTE; +using Microsoft.VisualStudio.Shell; + +namespace CodeDocumentor2026.Executors +{ + public class TextSelectionExecutor + { + public void Execute(TextSelection textSelection, Func selectionChangeCallback, int gotoLine = 1) + { + ThreadHelper.ThrowIfNotOnUIThread(); + textSelection.GotoLine(1, true); + textSelection.SelectAll(); + var contents = textSelection.Text; + var changedTxt = selectionChangeCallback.Invoke(contents); + if (string.IsNullOrEmpty(changedTxt) || changedTxt == contents) + { + return; + } + textSelection.Insert(changedTxt); + textSelection.SelectAll(); + textSelection.SmartFormat(); + textSelection.GotoLine(gotoLine, false); + } + } +} diff --git a/CodeDocumentor2026/Extensions/TextSelectionExtensions.cs b/CodeDocumentor2026/Extensions/TextSelectionExtensions.cs new file mode 100644 index 0000000..6be7c3c --- /dev/null +++ b/CodeDocumentor2026/Extensions/TextSelectionExtensions.cs @@ -0,0 +1,23 @@ +using EnvDTE; +using Microsoft.VisualStudio.Shell; + +namespace CodeDocumentor2026.Extensions +{ + public static class TestSelectionExtensions + { + public static void SetCursorToLine(this TextSelection textSelection, int line, int column) + { + ThreadHelper.ThrowIfNotOnUIThread(); + // Restore cursor position using the updated syntax tree + try + { + textSelection.MoveToLineAndOffset(line, column); + } + catch + { + // If position restoration fails, just collapse at current position + textSelection.Collapse(); + } + } + } +} diff --git a/CodeDocumentor2026/GlobalSuppressions.cs b/CodeDocumentor2026/GlobalSuppressions.cs new file mode 100644 index 0000000..ac49603 --- /dev/null +++ b/CodeDocumentor2026/GlobalSuppressions.cs @@ -0,0 +1,9 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~M:CodeDocumentor2026.Commands.Context.CodeDocumentorEditorCommand.OnBeforeQueryStatus(System.Object,System.EventArgs)")] +[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~M:CodeDocumentor2026.Commands.Context.CodeDocumentorEditorCommand.Execute(System.Object,System.EventArgs)")] diff --git a/CodeDocumentor2026/Helper/Logger.cs b/CodeDocumentor2026/Helper/Logger.cs new file mode 100644 index 0000000..c4ccd58 --- /dev/null +++ b/CodeDocumentor2026/Helper/Logger.cs @@ -0,0 +1,92 @@ +// For definitions of XML nodes see: +// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/documentation-comments see +// also https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/recommended-tags +using System.Diagnostics; +using CodeDocumentor.Common.Interfaces; + +namespace CodeDocumentor2026 +{ + public class Logger : IEventLogger + { + /// + /// Logs the error. + /// + /// The message. + public void LogError(string message, int eventId, short category, string diagnosticId) + { + try + { +#if DEBUG + // In debug mode, use Debug.WriteLine to avoid permission issues with Event Log + Debug.WriteLine($"[CodeDocumentor2026] ERROR - DiagnosticId: {diagnosticId}, EventId: {eventId}, Category: {category}, Message: {message ?? "null"}"); +#else + // In release mode, try to write to Event Log but don't fail if we can't + try + { + EventLog.WriteEntry("Visual Studio", + $"CodeDocumentor2026: DiagnosticId: {diagnosticId}. Message: {message ?? "null"}", + EventLogEntryType.Error, eventId, category); + } + catch + { + // Fallback to Debug output if Event Log fails + Debug.WriteLine($"[CodeDocumentor2026] ERROR - DiagnosticId: {diagnosticId}, EventId: {eventId}, Category: {category}, Message: {message ?? "null"}"); + } +#endif + } + catch + { + // Don't kill extension for logging errors - silent fallback + } + } + + public void LogDebug(string category, string message) + { + try + { +#if DEBUG + if (!string.IsNullOrEmpty(category)) + { + Debug.WriteLine($"[{category.ToUpper()}]: {message}"); + } + else + { + Debug.WriteLine(message); + } +#endif + } + catch + { + // Don't kill extension for logging errors + } + } + + public void LogInfo(string message, int eventId, short category, string diagnosticId) + { + try + { +#if DEBUG + // In debug mode, use Debug.WriteLine to avoid permission issues + Debug.WriteLine($"[CodeDocumentor2026] INFO - DiagnosticId: {diagnosticId}, EventId: {eventId}, Category: {category}, Message: {message ?? "null"}"); +#else + // In release mode, try to write to Event Log but don't fail if we can't + try + { + EventLog.WriteEntry("Visual Studio", + $"CodeDocumentor2026: DiagnosticId: {diagnosticId}. Message: {message ?? "null"}", + EventLogEntryType.Information, eventId, category); + } + catch + { + // Fallback to Debug output if Event Log fails + Debug.WriteLine($"[CodeDocumentor2026] INFO - DiagnosticId: {diagnosticId}, EventId: {eventId}, Category: {category}, Message: {message ?? "null"}"); + } +#endif + } + catch + { + // Don't kill extension for logging errors - silent fallback + } + } + } +} diff --git a/CodeDocumentor2026/Models/OptionPageGrid.cs b/CodeDocumentor2026/Models/OptionPageGrid.cs new file mode 100644 index 0000000..610174c --- /dev/null +++ b/CodeDocumentor2026/Models/OptionPageGrid.cs @@ -0,0 +1,160 @@ +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using CodeDocumentor.Common; +using CodeDocumentor.Common.Interfaces; +using CodeDocumentor.Common.Models; +using Microsoft.VisualStudio.Shell; + +// For definitions of XML nodes see: +// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/documentation-comments see +// also https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/recommended-tags +namespace CodeDocumentor2026.Models +{ + //This has to live in this project so context thread is valid + /// + /// The option page grid. + /// + [Guid("93DFF1A0-6A9F-42C6-9845-7F1E56105AE3")] + public class OptionPageGrid : DialogPage, IBaseSettings + { + /// + /// The category. + /// + public const string Category = "CodeDocumentor2026"; + + /// + /// The sub category. + /// + public const string SubCategory = "General"; + + /// + /// The returns sub category. + /// + private const string ReturnsSubCategory = "Return Options"; + + /// + /// The summary sub category. + /// + private const string SummarySubCategory = "Summary Options"; + + /// + /// The translation sub category. + /// + private const string TranslationSubCategory = "Translation Options"; + + /// + /// Gets or Sets a value indicating whether exclude asynchronously suffix. + /// + /// A bool. + [Category(SummarySubCategory)] + [DisplayName("Exclude async wording from comments")] + [Description("When documenting members skip adding asynchronously to the comment.")] + public bool ExcludeAsyncSuffix { get; set; } + + /// + /// Gets or Sets a value indicating whether include value node in properties. + /// + /// A bool. + [Category(ReturnsSubCategory)] + [DisplayName("Include node in property comments")] + [Description("When documenting properties add the value node with the return type")] + public bool IncludeValueNodeInProperties { get; set; } + + /// + /// Gets or Sets a value indicating whether enabled for non public is fields. + /// + [Category(SubCategory)] + [DisplayName("Enable comments for non public fields")] + [Description("When documenting fields allow adding documentation headers if the item is not public. This only applies to const and static fields. Visual Studio must be restarted to fully take affect.")] + public bool IsEnabledForNonPublicFields { get; set; } + + /// + /// Gets or Sets a value indicating whether enabled for publish members is only. + /// + /// A bool. + [Category(SubCategory)] + [DisplayName("Enable comments for public members only")] + [Description("When documenting classes, fields, methods, and properties only add documentation headers if the item is public. Visual Studio must be restarted to fully take affect.")] + public bool IsEnabledForPublicMembersOnly { get; set; } + + /// + /// Gets or Sets a value indicating whether preserve existing summary text. + /// + [Category(SummarySubCategory)] + [DisplayName("Preserve Existing Summary Text")] + [Description("When updating a comment or documenting the whole file if this is true; the summary text will not be regenerated. Defaults to true.")] + public bool PreserveExistingSummaryText { get; set; } = true; + + /// + /// Gets or Sets a value indicating whether use try and include crefs in method comments. + /// + /// A bool. + [Category(SummarySubCategory)] + [DisplayName("Try to include return types in documentation")] + [Description("When documenting methods and properties (and Use natural language for return comments is enabled) try to include in the return element. In methods that are named 2 words or less try and generate elements for those types in the method comment")] + public bool TryToIncludeCrefsForReturnTypes { get; set; } + + //Any properties that need defaults should be mnanaged in the Settings Class. This is only a pass through for VS + /// + /// Gets or Sets a value indicating whether use natural language for return node. + /// + /// A bool. + [Category(ReturnsSubCategory)] + [DisplayName("Use natural language for return comments")] + [Description("When documenting members if the return type contains a generic then translate that item into natural language. The default uses CDATA nodes to show the exact return type. Example: A List of Strings")] + public bool UseNaturalLanguageForReturnNode { get; set; } + + /// + /// Gets or Sets a value indicating whether use to do comments on summary error. + /// + /// A bool. + [Category(SummarySubCategory)] + [DisplayName("Use TODO comment when summary can not be determined")] + [Description("When documenting methods that can not create a valid summary insert TODO instead. Async is ignored")] + public bool UseToDoCommentsOnSummaryError { get; set; } + + /// + /// Gets or Sets the word maps. + /// + /// An array of wordmaps. + [Category(TranslationSubCategory)] + [DisplayName("Word mappings for creating comments")] + [Description("When documenting if certain word are matched it will swap out to the translated mapping.")] + public WordMap[] WordMaps { get; set; } + + /// + /// Load settings from storage. + /// + public override void LoadSettingsFromStorage() + { + IBaseSettings settings = new Settings2026(); + settings = settings.Load(); + IsEnabledForPublicMembersOnly = settings.IsEnabledForPublicMembersOnly; + UseNaturalLanguageForReturnNode = settings.UseNaturalLanguageForReturnNode; + ExcludeAsyncSuffix = settings.ExcludeAsyncSuffix; + IncludeValueNodeInProperties = settings.IncludeValueNodeInProperties; + UseToDoCommentsOnSummaryError = settings.UseToDoCommentsOnSummaryError; + TryToIncludeCrefsForReturnTypes = settings.TryToIncludeCrefsForReturnTypes; + WordMaps = settings.WordMaps ?? CodeDocumentor.Common.Constants.DEFAULT_WORD_MAPS; + PreserveExistingSummaryText = settings.PreserveExistingSummaryText; + IsEnabledForNonPublicFields = settings.IsEnabledForNonPublicFields; + } + + /// + /// Save settings to storage. + /// + public override void SaveSettingsToStorage() + { + var settings = new Settings2026(); + var eventLogger = new Logger(); + settings.Update(this, eventLogger); + settings.Save(); + } + + public ISettings Clone() + { + throw new NotImplementedException(); + } + } +} diff --git a/CodeDocumentor2026/Properties/AssemblyInfo.cs b/CodeDocumentor2026/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..50d4ee0 --- /dev/null +++ b/CodeDocumentor2026/Properties/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CodeDocumentor2026")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CodeDocumentor2026")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CodeDocumentor2026/Resources/CodeDocumentor2026Package.ico b/CodeDocumentor2026/Resources/CodeDocumentor2026Package.ico new file mode 100644 index 0000000..d323b07 Binary files /dev/null and b/CodeDocumentor2026/Resources/CodeDocumentor2026Package.ico differ diff --git a/CodeDocumentor2026/Resources/CodeDocumentorCommand.png b/CodeDocumentor2026/Resources/CodeDocumentorCommand.png new file mode 100644 index 0000000..b22d975 Binary files /dev/null and b/CodeDocumentor2026/Resources/CodeDocumentorCommand.png differ diff --git a/CodeDocumentor2026/Settings/VsixOptions.cs b/CodeDocumentor2026/Settings/VsixOptions.cs new file mode 100644 index 0000000..a52e5b0 --- /dev/null +++ b/CodeDocumentor2026/Settings/VsixOptions.cs @@ -0,0 +1,15 @@ +namespace CodeDocumentor2026 +{ + public static class VsixOptions + { + /// + /// CodeDocumentor.Vsix2022Package GUID string. + /// + public const string PackageGuidString = "238cdf3a-397e-4b27-ac5f-d4bb1888cade"; + + /// + /// The version. This needs to be kept in sync with Manifest version + /// + public const string Version = "1.0.0.0"; + } +} diff --git a/CodeDocumentor2026/VSPackage.resx b/CodeDocumentor2026/VSPackage.resx new file mode 100644 index 0000000..236addb --- /dev/null +++ b/CodeDocumentor2026/VSPackage.resx @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + CodeDocumentor2026Package Extension + + + CodeDocumentor2026Package Visual Studio Extension Detailed Info + + + Resources\CodeDocumentor2026Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/CodeDocumentor2026/logo.png b/CodeDocumentor2026/logo.png new file mode 100644 index 0000000..f9fc04e Binary files /dev/null and b/CodeDocumentor2026/logo.png differ diff --git a/CodeDocumentor2026/source.extension.vsixmanifest b/CodeDocumentor2026/source.extension.vsixmanifest new file mode 100644 index 0000000..563fc8f --- /dev/null +++ b/CodeDocumentor2026/source.extension.vsixmanifest @@ -0,0 +1,43 @@ + + + + + CodeDocumentor2026 + An Extension to generate XML documentation automatically using IntelliSense for interface,class,enum, field, constructor, property and method. + logo.png + documentation;xml;comments;intellisense;code generator + + + + amd64 + + + amd64 + + + amd64 + + + arm64 + + + arm64 + + + arm64 + + + + + + + + + + + + + + + + diff --git a/GifInstruction/2026/CodeDocumentor2026Demo.gif b/GifInstruction/2026/CodeDocumentor2026Demo.gif new file mode 100644 index 0000000..46b35ec Binary files /dev/null and b/GifInstruction/2026/CodeDocumentor2026Demo.gif differ diff --git a/GifInstruction/2026/DocumentThisKeyboard.png b/GifInstruction/2026/DocumentThisKeyboard.png new file mode 100644 index 0000000..a9ee6f0 Binary files /dev/null and b/GifInstruction/2026/DocumentThisKeyboard.png differ diff --git a/GifInstruction/2026/FileKeyboard.png b/GifInstruction/2026/FileKeyboard.png new file mode 100644 index 0000000..48364ab Binary files /dev/null and b/GifInstruction/2026/FileKeyboard.png differ diff --git a/GifInstruction/2026/KeyboardSearch.png b/GifInstruction/2026/KeyboardSearch.png new file mode 100644 index 0000000..39e59e2 Binary files /dev/null and b/GifInstruction/2026/KeyboardSearch.png differ diff --git a/GifInstruction/2026/RightContext.png b/GifInstruction/2026/RightContext.png new file mode 100644 index 0000000..59b7e7b Binary files /dev/null and b/GifInstruction/2026/RightContext.png differ diff --git a/GifInstruction/2026/SolutionFile.png b/GifInstruction/2026/SolutionFile.png new file mode 100644 index 0000000..5ab017f Binary files /dev/null and b/GifInstruction/2026/SolutionFile.png differ diff --git a/GifInstruction/2026/SolutionFolder.png b/GifInstruction/2026/SolutionFolder.png new file mode 100644 index 0000000..f5a5665 Binary files /dev/null and b/GifInstruction/2026/SolutionFolder.png differ diff --git a/GifInstruction/2026/ToolsMenu.png b/GifInstruction/2026/ToolsMenu.png new file mode 100644 index 0000000..c1f65c8 Binary files /dev/null and b/GifInstruction/2026/ToolsMenu.png differ diff --git a/Readme.md b/Readme.md index 55a3e95..94b2e76 100644 --- a/Readme.md +++ b/Readme.md @@ -6,68 +6,87 @@ ![Visual Studio Marketplace Installs](https://img.shields.io/visual-studio-marketplace/i/DanTurco.CodeDocumentor) -A Visual Studio Extension to generate XML documentation automatically for c# code using IntelliSense for interface,class,enum, field, constructor, property and method. While VS2022 provides basic documentation capabilities this fills the gap in trying to populate the summary and return nodes. This also gives control over how the summaries are translated. +A Visual Studio Extension to generate XML documentation automatically for c# code using IntelliSense for interface,class,enum, field, constructor, property and method. While VS2022/VS2026 provides basic documentation capabilities this fills the gap in trying to populate the summary and return nodes. This also gives control over how the summaries are translated. In the age of copilots this extension is still valuable when working on projects where sending code to the cloud is not possible. This creates the documentation locally on your machine. Nothing is ever sent to the cloud. No Internet connection is required for this to work. +**Looking for the Original Analyzer Based Version?** +Review the [CodeDocumentor Analyzer Readme.md](https://github.com/d1820/CodeDocumentor/blob/main/Readme2022.md) + ## Installation --- -Download and install the VSIX from the [VS Marketplace](https://marketplace.visualstudio.com/items?itemName=DanTurco.CodeDocumentor) +Download and install the CodeDocumentor2026 VSIX from the [VS Marketplace](https://marketplace.visualstudio.com/items?itemName=DanTurco.CodeDocumentor2026) + +**IMPORTANT!!** This extension no longer uses analyzers to perform the documentation generation. If you have any previous versions of CodeDocumentor installed please uninstall them first. -**IMPORTANT!!** This extension is NOT compatible with Visual Studio 2026! While Microsoft claims backward compatibility, they have removed all support for analyzers being able to run in the foreground. Due to this this can no longer use Visual Studio Options Settings or load anything properly. A new version will be coming soon for Visual Studio 2026, that will run in the foreground, but no longer as an analyzer. ## Table of Contents -- [Instruction](#instruction) -- [Known Issues](#known-issues) -- [Comment Ordering](#comment-ordering) -- [Supported Comment Refactorings](#supported-comment-refactorings) -- [Settings](#settings) - - [Word Translations](#word-translations) - - [Recommended Settings](#recommended-settings) -- [Also Supports](#also-supports) - - [One Word Methods](#one-word-methods) - - [Example](#example) -- [Excluding ErrorList Messages](#excluding-errorlist-messages) - - [Available DiagnosticId Codes](#available-diagnosticid-codes) - - [Supported Members](#supported-members) - - [Attribute](#attribute) - - [Example](#example-1) -- [Usage Examples](#usage-examples) - - [Example Cref Support](#example-cref-support) -- [Errors and Crashes](#errors-and-crashes) -- [Using .editorconfig for settings](#using-editorconfig-for-settings) -- [Changelog](#changelog) -- [Special Thanks](#special-thanks) +- [CodeDocumentor](#codedocumentor) + - [Installation](#installation) + - [Table of Contents](#table-of-contents) + - [Known Issues](#known-issues) + - [Compatibility](#compatibility) + - [4 Ways to Invoke CodeDocumentor](#4-ways-to-invoke-codedocumentor) + - [From the Tools menu](#from-the-tools-menu) + - [From the Solution Explorer context menu on a project or solution](#from-the-solution-explorer-context-menu-on-a-project-or-solution) + - [From the Solution Explorer context menu on a code file](#from-the-solution-explorer-context-menu-on-a-code-file) + - [From the right click context menu in the code editor on a supported type](#from-the-right-click-context-menu-in-the-code-editor-on-a-supported-type) + - [Comment Ordering](#comment-ordering) + - [Supported Comment Refactorings](#supported-comment-refactorings) + - [Settings](#settings) + - [Word Translations](#word-translations) + - [Recommended Settings](#recommended-settings) + - [Also Supports](#also-supports) + - [One Word Methods](#one-word-methods) + - [Example](#example) + - [Supported Members](#supported-members) + - [Keyboard Shortcuts](#keyboard-shortcuts) + - [Usage Demo](#usage-demo) + - [Example Cref Support](#example-cref-support) + - [Errors and Crashes](#errors-and-crashes) + - [Porting over CodeDocumentor 2022 Settings](#porting-over-codedocumentor-2022-settings) + - [Changelog](#changelog) + - [Special Thanks](#special-thanks) -## Instruction +## Known Issues +VS2026 seems to get corrupted when VS2022 extensions are installed and roaming is enabled. +I have had my entire VS2026 crash and be unable to start unless I perform a `Repair` from the VS Installer. Im not shocked by this, as Microsoft always releases buggy things to mean deadlines. +Only install into 1 instance of Visual Studio at a time. Start with VS2022, then do VS2026 + +## Compatibility --- -1. When you installed it successful to your Visual Studio. You can see the warning wave line below the members which don't have documentation on it. -2. Then you can click the bulb to see the fix option. When you click the option, the documentation will be added. -3. You can use shortcut(Alt+Enter or Ctrl+.) to quickly add the documentation. Documentation fixes can be implemented at the member, document, project, and solution levels. +The new CodeDocumentor2026 extension is compatible with Visual Studio 2022 and Visual Studio 2026. This is the new preferred version of CodeDocumentor moving forward. +This does not use Analyzers and Fix Providers anymore so there is no longer a dependency on Roslyn Analyzers. +The XML documentation is now created in the editor foreground directly. No more cluttering the error list with messages. +This gives the developer control on creating XML documentation when they want it. -## Known Issues +## 4 Ways to Invoke CodeDocumentor -Microsoft is not going to make any changes to truly allow analyzers to run out of process. Even with .editorconfig support, it will not work if you want to have any user level settings collection from Visual Studio > Options. +### From the Tools menu -- As of VS2022 version 17.6.x there is some bug that makes extension analyzers not able to work properly if you have *Run code analysis in separate process* +![Solution Explorer Document Folder](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/2026/ToolsMenu.png?raw=true) - ![Out of process](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/outOfProcess.png?raw=true) +### From the Solution Explorer context menu on a project or solution - **Please disable this setting to allow CodeDocumentor to work correctly.** +![Solution Explorer Document Folder](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/2026/SolutionFolder.png?raw=true) -- As of VS2022 Version 17.8.6. Out of process works but ONLY if you deselect *_Run code analysis on latest .NET_*. +### From the Solution Explorer context menu on a code file - ![Out Of Process Latest](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/OutOfProessLatest.png?raw=true) +![Solution Explorer Document File](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/2026/SolutionFile.png?raw=true) -- As of VS2022 Version 17.14.13. Out of process does not work AGAIN. you need to deselect *_Run code analysis in separate process*. - ![Out Of Process 17.14.13](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/OutOfProcess_v17.14.13.png?raw=true) - **Please disable this setting to allow CodeDocumentor to work correctly.** +### From the right click context menu in the code editor on a supported type + +Withing the editor for a given C# file you have 2 options from the right context menu +1. Add documentation to the given type the cursor is on. Note the cursor must be on the line of a supported XML documentation member. If the cursor is not on a supported member line you will not see the `Code Documentor This` menu item. See `Supported Members` below. +1. Add documentation to the whole file. + +![Editor Right Context Menu](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/2026/RightContext.png?raw=true) ## Comment Ordering @@ -85,7 +104,7 @@ Comments are structured in the following order ## Supported Comment Refactorings -Code Documentor supports creating, updating, and recreating on a given type. There is also an avaialble fix at eny level to comment the whole file. +Code Documentor supports creating, updating, and recreating XML documentation on a given member. ## Settings --- @@ -103,17 +122,6 @@ To adjust these defaults go to Tools > Options > CodeDocumentor | Try to include return types in documentation | When documenting methods and properties (and Use natural language for return comments is enabled) try to include in the return element. In methods that are named 2 words or less try and generate `````` elements for those types in the method comment | | Word mappings for creating comments | When documenting if certain word are matched it will swap out to the translated mapping. | | Preserve Existing Summary Text | When updating a comment or documenting the whole file if this is true; the summary text will not be regenerated. Defaults to true. | -| Default Diagnostics | Allows setting a new default diagnostic level for evaluation. Default is Warning. A restart of Visual Studio is required on change. | -| Class Diagnostics | Allows setting a new default diagnostic level for evaluation for classes. A restart of Visual Studio is required on change. | -| Constructor Diagnostics | Allows setting a new default diagnostic level for evaluation for constructors. A restart of Visual Studio is required on change. | -| Enum Diagnostics | Allows setting a new default diagnostic level for evaluation for enums. A restart of Visual Studio is required on change. | -| Field Diagnostics | Allows setting a new default diagnostic level for evaluation for fields. A restart of Visual Studio is required on change. | -| Interface Diagnostics | Allows setting a new default diagnostic level for evaluation for interfaces. A restart of Visual Studio is required on change. | -| Method Diagnostics | Allows setting a new default diagnostic level for evaluation for methods. A restart of Visual Studio is required on change. | -| Property Diagnostics | Allows setting a new default diagnostic level for evaluation for properties. A restart of Visual Studio is required on change. | -| Record Diagnostics | Allows setting a new default diagnostic level for evaluation for records. A restart of Visual Studio is required on change. | -| Use .editorconfig for settings options | This will convert existing extension options to .editorconfig values stored in %USERPROFILE%. This allows CodeDocumentor to run out of process.| - ### Word Translations @@ -134,7 +142,6 @@ These are the recommended settings that create the best output experience | Use TODO comment when summary can not be determined | True | | Try to include return types in documentation | True | | Preserve Existing Summary Text | True | -| Default Diagnostics | Warning | @@ -150,7 +157,7 @@ To adjust these defaults go to Tools > Options > CodeDocumentor ### One Word Methods -In an attempt to create valid summary statements when a method is only 1 word (plus Async suffix) we will read the return type of the method. If the method is a generic type an attempt will be +In an attempt to create valid summary statements when a method is only 1 word (plus Async suffix) we will read the return type of the method. If the method is a generic type an attempt will be made to create text representing that string. In the example below in the summary line CodeDocumentor added ```and return a of type of type ``` This is leveraging the new setting **Try to include return types in documentation** to generate those `````` elements. @@ -181,26 +188,6 @@ internal Task> CreateAsync() ``` - -## Excluding ErrorList Messages ---- - -There are 2 ways to exclude analyzer messaging. Defauly level is set to **Warning** - -1. Add the _SuppressMessage_ attribute to any member. -2. Add DiagnosticId exclusions to the editorconfig - -### Available DiagnosticId Codes - -- CD1600: Class -- CD1601: Constructor -- CD1602: Enum -- CD1603: Field -- CD1604: Interface -- CD1605: Method -- CD1606: Property -- CD1608: Record - ### Supported Members - Class @@ -212,56 +199,22 @@ There are 2 ways to exclude analyzer messaging. Defauly level is set to **Warnin - Enum - Record -### Attribute - -```csharp -[System.Diagnostics.CodeAnalysis.SuppressMessage("XMLDocumentation", "")] -``` - -### Example - -```csharp -//This will remove the analyzer messaging for entire class and all child members -[System.Diagnostics.CodeAnalysis.SuppressMessage("XMLDocumentation", "")] -public class Test -{ - public string GetAsync(string name) - { - return name; - } - public TResult Tester() - { - throw new ArgumentNullException(nameof(Tester)); - return default; - } -} -``` - - -## Usage Examples +## Keyboard Shortcuts +A power developer setup involves setting up Keyboard shortcuts to invoke the CodeDocumentor in the editor. -Inline code notification +- Open `Tools > Options` +- Search for Keyboard +- Open the Keyboard dialog if in VS2026) +- Search for CodeDocumentor + ![Documentor keyboard Search](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/2026/KeyboardSearch.png?raw=true) +- For the File Action, set a keyboard shortcut assignment. I use Cntrl+D Cntrl+F + ![Documentor Keyboard Action File](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/2026/FileKeyboard.png?raw=true) +- For the This Action, set a keyboard shortcut assignment. I use Cntrl+D Cntrl+D + ![Documentor Keyboard Action File](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/2026/DocumentThisKeyboard.png?raw=true) -![Wavy line](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/warning%20wave%20line.gif?raw=true) +## Usage Demo +![CodeDocumentor 2026 Demo](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/2026/CodeDocumentor2026Demo.gif?raw=true) -Add comments to a single type -![Single type add](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/SingleTypeAddComments.gif?raw=true) - -Update comments to a single type -![Single type update](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/SingleTypeUpdateComments.gif?raw=true) - -Update comments to a single type when preserving the summary setting is true -![Single type preserve comment](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/SingleTypeUpdatePreserveComments.gif?raw=true) - -Update comments to a single type when preserving the summary setting is false -![Single type preserve comment disabled](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/SingleTypeUpdatePreserveCommentsDisabled.gif?raw=true) - -Update the whole file at once -![Update whole file](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/UpdateWholeFile.gif?raw=true) - -How fast comments can be added - -![Quick add](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/short%20cut%20to%20quick%20add.gif?raw=true) ### Example Cref Support @@ -285,30 +238,23 @@ All errors are written to the EventLog in windows. Check there for causes, and u **Source**: "Visual Studio" -**Message Prefix**: "CodeDocumentor: " +**Message Prefix**: "CodeDocumentor2026: " ![Event Log](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/EventLog.png?raw=true) -## Using .editorconfig for settings - -To convert existing settings to .editorconfig go to Tools > Options > CodeDocumentor and select **Use .editorconfig for settings options**. -This will convert the existing Visual Studio Option Settings to editor config format and copy them to your clipboard. -Paste this into a new or existing .editorconfig file in your solution. - -**NOTE**: Even with using .editorconfig as your settings, Out of Process still can not be used, because the extension needs to support backward compatibility of using the Visual Studio Options. +## Porting over CodeDocumentor 2022 Settings +1. Uninstall the existing CodeDocumentor 2022 extension +2. In Explorer navigate to `C:\Users\\AppData\Roaming\CodeDocumentor` +3. Copy `codedocumentor.json` and rename to `codedocumentor2026.json` +4. That is it! Start VS2026 and the settings that apply still will be loaded. ## Changelog | Date | Change | Version | | ---------- | ----------------------------------------------------------------------- | ------- | -| 02/13/2024 | Rewrote document generator to builder pattern | 2.1.0.X | -| | Increased code coverage for tests | | -| | Added support for `````` tags in summary and return nodes | | -| | Bug fixes | | -| 02/1/2024 | Added support for ArgumentNullException.ThrowIf statements | 2.0.1.1 | -| 09/01/2025 | Added support for storing settings in a solution level .editorconfig | 3.0.0.0 | +| 11/17/2025 | New version created. Non-Analyzer version. | 1.0.0.0 | ## Special Thanks diff --git a/Readme2022.md b/Readme2022.md new file mode 100644 index 0000000..55a3e95 --- /dev/null +++ b/Readme2022.md @@ -0,0 +1,315 @@ +# CodeDocumentor +--- +![GitHub CI](https://img.shields.io/github/actions/workflow/status/d1820/CodeDocumentor/dotnet.yml) +![GitHub License](https://img.shields.io/github/license/d1820/CodeDocumentor?logo=github&logoColor=green) +![Visual Studio Marketplace Version (including pre-releases)](https://img.shields.io/visual-studio-marketplace/v/DanTurco.CodeDocumentor) +![Visual Studio Marketplace Installs](https://img.shields.io/visual-studio-marketplace/i/DanTurco.CodeDocumentor) + + +A Visual Studio Extension to generate XML documentation automatically for c# code using IntelliSense for interface,class,enum, field, constructor, property and method. While VS2022 provides basic documentation capabilities this fills the gap in trying to populate the summary and return nodes. This also gives control over how the summaries are translated. + +In the age of copilots this extension is still valuable when working on projects where sending code to the cloud is not possible. This creates the documentation locally on your machine. Nothing is ever sent to the cloud. No Internet connection is required for this to work. + +## Installation +--- + +Download and install the VSIX from the [VS Marketplace](https://marketplace.visualstudio.com/items?itemName=DanTurco.CodeDocumentor) + +**IMPORTANT!!** This extension is NOT compatible with Visual Studio 2026! While Microsoft claims backward compatibility, they have removed all support for analyzers being able to run in the foreground. Due to this this can no longer use Visual Studio Options Settings or load anything properly. A new version will be coming soon for Visual Studio 2026, that will run in the foreground, but no longer as an analyzer. +## Table of Contents + + + +- [Instruction](#instruction) +- [Known Issues](#known-issues) +- [Comment Ordering](#comment-ordering) +- [Supported Comment Refactorings](#supported-comment-refactorings) +- [Settings](#settings) + - [Word Translations](#word-translations) + - [Recommended Settings](#recommended-settings) +- [Also Supports](#also-supports) + - [One Word Methods](#one-word-methods) + - [Example](#example) +- [Excluding ErrorList Messages](#excluding-errorlist-messages) + - [Available DiagnosticId Codes](#available-diagnosticid-codes) + - [Supported Members](#supported-members) + - [Attribute](#attribute) + - [Example](#example-1) +- [Usage Examples](#usage-examples) + - [Example Cref Support](#example-cref-support) +- [Errors and Crashes](#errors-and-crashes) +- [Using .editorconfig for settings](#using-editorconfig-for-settings) +- [Changelog](#changelog) +- [Special Thanks](#special-thanks) + + + +## Instruction +--- + +1. When you installed it successful to your Visual Studio. You can see the warning wave line below the members which don't have documentation on it. +2. Then you can click the bulb to see the fix option. When you click the option, the documentation will be added. +3. You can use shortcut(Alt+Enter or Ctrl+.) to quickly add the documentation. Documentation fixes can be implemented at the member, document, project, and solution levels. + +## Known Issues + +Microsoft is not going to make any changes to truly allow analyzers to run out of process. Even with .editorconfig support, it will not work if you want to have any user level settings collection from Visual Studio > Options. + +- As of VS2022 version 17.6.x there is some bug that makes extension analyzers not able to work properly if you have *Run code analysis in separate process* + + ![Out of process](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/outOfProcess.png?raw=true) + + **Please disable this setting to allow CodeDocumentor to work correctly.** + +- As of VS2022 Version 17.8.6. Out of process works but ONLY if you deselect *_Run code analysis on latest .NET_*. + + ![Out Of Process Latest](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/OutOfProessLatest.png?raw=true) + +- As of VS2022 Version 17.14.13. Out of process does not work AGAIN. you need to deselect *_Run code analysis in separate process*. + ![Out Of Process 17.14.13](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/OutOfProcess_v17.14.13.png?raw=true) + **Please disable this setting to allow CodeDocumentor to work correctly.** + + +## Comment Ordering + +Comments are structured in the following order + +- Summary +- Generic Types *if applies +- Parameter Types *if applies +- Exception Types *if applies +- Property Value Types *if applies +- Remarks +- Examples +- Return Types *if applies + +## Supported Comment Refactorings + +Code Documentor supports creating, updating, and recreating on a given type. There is also an avaialble fix at eny level to comment the whole file. + +## Settings +--- + +To adjust these defaults go to Tools > Options > CodeDocumentor + +| Setting | Description | +| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Exclude async wording from comments | When documenting members skip adding asynchronously to the comment. | +| Include `````` node in property comments | When documenting properties add the value node with the return type | +| Enable comments for public members only | When documenting classes, fields, methods, and properties only add documentation headers if the item is public | +| Enable comments for non public fields | When documenting fields allow adding documentation headers if the item is not public. This only applies to const and static fields. | +| Use natural language for return comments | When documenting members if the return type contains a generic then translate that item into natural language. The default uses CDATA nodes to show the exact return type. Example Enabled: ```A List of Strings``` Example Disabled: ``` ]]>``` | +| Use TODO comment when summary can not be determined | When documenting methods that can not create a valid summary insert TODO instead. Async is ignored in evaluation. Using this in conjunction with the vs2022 Task Window you can quickly find all summaries that could not be generated. | +| Try to include return types in documentation | When documenting methods and properties (and Use natural language for return comments is enabled) try to include in the return element. In methods that are named 2 words or less try and generate `````` elements for those types in the method comment | +| Word mappings for creating comments | When documenting if certain word are matched it will swap out to the translated mapping. | +| Preserve Existing Summary Text | When updating a comment or documenting the whole file if this is true; the summary text will not be regenerated. Defaults to true. | +| Default Diagnostics | Allows setting a new default diagnostic level for evaluation. Default is Warning. A restart of Visual Studio is required on change. | +| Class Diagnostics | Allows setting a new default diagnostic level for evaluation for classes. A restart of Visual Studio is required on change. | +| Constructor Diagnostics | Allows setting a new default diagnostic level for evaluation for constructors. A restart of Visual Studio is required on change. | +| Enum Diagnostics | Allows setting a new default diagnostic level for evaluation for enums. A restart of Visual Studio is required on change. | +| Field Diagnostics | Allows setting a new default diagnostic level for evaluation for fields. A restart of Visual Studio is required on change. | +| Interface Diagnostics | Allows setting a new default diagnostic level for evaluation for interfaces. A restart of Visual Studio is required on change. | +| Method Diagnostics | Allows setting a new default diagnostic level for evaluation for methods. A restart of Visual Studio is required on change. | +| Property Diagnostics | Allows setting a new default diagnostic level for evaluation for properties. A restart of Visual Studio is required on change. | +| Record Diagnostics | Allows setting a new default diagnostic level for evaluation for records. A restart of Visual Studio is required on change. | +| Use .editorconfig for settings options | This will convert existing extension options to .editorconfig values stored in %USERPROFILE%. This allows CodeDocumentor to run out of process.| + + + +### Word Translations + +As part of the settings WordMaps can be defined to help control how you want text displayed. There are already a set of default WordMaps defined. + +### Recommended Settings + +These are the recommended settings that create the best output experience + +| Setting | Description | +| --------------------------------------------------- | ----------- | +| Exclude async wording from comments | False | +| Include `````` node in property comments | False | +| Enable comments for public members only | False | +| Enable comments for non public fields | False | +| Use natural language for return comments | False | +| Use TODO comment when summary can not be determined | True | +| Try to include return types in documentation | True | +| Preserve Existing Summary Text | True | +| Default Diagnostics | Warning | + + + +## Also Supports +--- + +- For method documenting it will scan the method code for any exceptions and automatically add them as exception nodes +- For method generic return types it uses XML CDATA so the actual generic type is displayed +- For method documenting where generics are used typeparam nodes are added. +- Whole file, project and solution comment adding + +To adjust these defaults go to Tools > Options > CodeDocumentor + +### One Word Methods + +In an attempt to create valid summary statements when a method is only 1 word (plus Async suffix) we will read the return type of the method. If the method is a generic type an attempt will be +made to create text representing that string. In the example below in the summary line CodeDocumentor added ```and return a of type of type ``` +This is leveraging the new setting **Try to include return types in documentation** to generate those `````` elements. + +#### Example + +With **Try to include return types in documentation** enabled + +```csharp +/// +/// Creates and return a of type of type asynchronously. +/// +/// A of type of type +internal Task> CreateAsync() +{ +} +``` + +With **Try to include return types in documentation** disabled + +```csharp +/// +/// Creates and return a task of type actionresult of type clientdto asynchronously. +/// +/// A of type of type +internal Task> CreateAsync() +{ +} +``` + + + +## Excluding ErrorList Messages +--- + +There are 2 ways to exclude analyzer messaging. Defauly level is set to **Warning** + +1. Add the _SuppressMessage_ attribute to any member. +2. Add DiagnosticId exclusions to the editorconfig + +### Available DiagnosticId Codes + +- CD1600: Class +- CD1601: Constructor +- CD1602: Enum +- CD1603: Field +- CD1604: Interface +- CD1605: Method +- CD1606: Property +- CD1608: Record + +### Supported Members + +- Class +- Method +- Interface +- Property +- Field +- Constructor +- Enum +- Record + +### Attribute + +```csharp +[System.Diagnostics.CodeAnalysis.SuppressMessage("XMLDocumentation", "")] +``` + +### Example + +```csharp +//This will remove the analyzer messaging for entire class and all child members +[System.Diagnostics.CodeAnalysis.SuppressMessage("XMLDocumentation", "")] +public class Test +{ + public string GetAsync(string name) + { + return name; + } + public TResult Tester() + { + throw new ArgumentNullException(nameof(Tester)); + return default; + } +} +``` + + +## Usage Examples + +Inline code notification + +![Wavy line](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/warning%20wave%20line.gif?raw=true) + +Add comments to a single type +![Single type add](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/SingleTypeAddComments.gif?raw=true) + +Update comments to a single type +![Single type update](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/SingleTypeUpdateComments.gif?raw=true) + +Update comments to a single type when preserving the summary setting is true +![Single type preserve comment](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/SingleTypeUpdatePreserveComments.gif?raw=true) + +Update comments to a single type when preserving the summary setting is false +![Single type preserve comment disabled](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/SingleTypeUpdatePreserveCommentsDisabled.gif?raw=true) + +Update the whole file at once +![Update whole file](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/UpdateWholeFile.gif?raw=true) + +How fast comments can be added + +![Quick add](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/short%20cut%20to%20quick%20add.gif?raw=true) + +### Example Cref Support + +```csharp +/// +/// Creates and return a of type of type asynchronously. +/// +/// The client data transfer object. +/// +/// A of type of type +internal Task> CreateAsync(CreateClientDto clientDto) +{ +throw new ArgumentException("test"); +} +``` + +## Errors and Crashes + +If you are finding the code documentor is crashing or causing errors. +All errors are written to the EventLog in windows. Check there for causes, and use this information to file a bug. + +**Source**: "Visual Studio" + +**Message Prefix**: "CodeDocumentor: " + +![Event Log](https://github.com/d1820/CodeDocumentor/blob/main/GifInstruction/EventLog.png?raw=true) + + +## Using .editorconfig for settings + +To convert existing settings to .editorconfig go to Tools > Options > CodeDocumentor and select **Use .editorconfig for settings options**. +This will convert the existing Visual Studio Option Settings to editor config format and copy them to your clipboard. +Paste this into a new or existing .editorconfig file in your solution. + +**NOTE**: Even with using .editorconfig as your settings, Out of Process still can not be used, because the extension needs to support backward compatibility of using the Visual Studio Options. + + +## Changelog + +| Date | Change | Version | +| ---------- | ----------------------------------------------------------------------- | ------- | +| 02/13/2024 | Rewrote document generator to builder pattern | 2.1.0.X | +| | Increased code coverage for tests | | +| | Added support for `````` tags in summary and return nodes | | +| | Bug fixes | | +| 02/1/2024 | Added support for ArgumentNullException.ThrowIf statements | 2.0.1.1 | +| 09/01/2025 | Added support for storing settings in a solution level .editorconfig | 3.0.0.0 | + + +## Special Thanks +This was forked and modified from [jinyafeng](https://github.com/jinyafeng/DocumentationAssistant) diff --git a/Reset-Exp.ps1 b/Reset-Exp.ps1 new file mode 100644 index 0000000..15d4944 --- /dev/null +++ b/Reset-Exp.ps1 @@ -0,0 +1,2 @@ +cd "C:\Program Files\Microsoft Visual Studio\18\Community\VSSDK\VisualStudioIntegration\Tools\Bin" +./CreateExpInstance.exe /Reset /VSInstance=18.0 /RootSuffix=_0ac13c8dExp diff --git a/TestProject/Sample/Sample/ProtoAttributor/ProtoTesterBracketNamespace.cs b/TestProject/Sample/Sample/ProtoAttributor/ProtoTesterBracketNamespace.cs index 4815335..bb45df2 100644 --- a/TestProject/Sample/Sample/ProtoAttributor/ProtoTesterBracketNamespace.cs +++ b/TestProject/Sample/Sample/ProtoAttributor/ProtoTesterBracketNamespace.cs @@ -1,5 +1,4 @@ using ProtoBuf; - namespace Sample.Other { [ProtoContract]