From 0cbde6f3344a4d98b2b5ca239693ab140653cffb Mon Sep 17 00:00:00 2001 From: Nikolay Pianikov Date: Sat, 20 Jul 2024 20:42:44 +0300 Subject: [PATCH] Fixing comments for generic types --- src/Pure.DI.Core/Core/Code/ClassCommenter.cs | 198 +++++++++--------- .../Core/Code/DisposeMethodBuilder.cs | 2 +- src/Pure.DI.Core/Core/Code/Formatter.cs | 48 +++-- .../Core/Code/RootMethodsCommenter.cs | 54 +++-- .../Pure.DI.UsageTests.csproj | 2 + 5 files changed, 174 insertions(+), 130 deletions(-) diff --git a/src/Pure.DI.Core/Core/Code/ClassCommenter.cs b/src/Pure.DI.Core/Core/Code/ClassCommenter.cs index 350134517..dc42b4dc5 100644 --- a/src/Pure.DI.Core/Core/Code/ClassCommenter.cs +++ b/src/Pure.DI.Core/Core/Code/ClassCommenter.cs @@ -24,120 +24,126 @@ public void AddComments(CompositionCode composition, Unit unit) } code.AppendLine("/// "); - if (classComments.Count > 0) + try { - foreach (var comment in comments.Format(classComments, true)) + if (classComments.Count > 0) { - code.AppendLine(comment); + foreach (var comment in comments.Format(classComments, true)) + { + code.AppendLine(comment); + } } - } - - var orderedRoots = composition.Roots - .OrderByDescending(root => root.IsPublic) - .ThenBy(root => root.DisplayName) - .ThenBy(root => root.Node.Binding) - .ToArray(); - if (orderedRoots.Length > 0) - { - var rootComments = comments.FormatList( - "Composition roots:", - orderedRoots.Select(root => (CreateRootTerms(root), CreateRootDescriptions(root))), - false); + var orderedRoots = composition.Roots + .OrderByDescending(root => root.IsPublic) + .ThenBy(root => root.DisplayName) + .ThenBy(root => root.Node.Binding) + .ToArray(); - foreach (var rootComment in rootComments) + if (orderedRoots.Length > 0) { - code.AppendLine(rootComment); - } + var rootComments = comments.FormatList( + "Composition roots:", + orderedRoots.Select(root => (CreateRootTerms(root), CreateRootDescriptions(root))), + false); - IReadOnlyCollection CreateRootTerms(Root root) - { - var term = new StringBuilder(); - if (root.IsPublic) - { - term.Append(formatter.FormatRef(composition.Source.Source, root.Injection.Type)); - term.Append(' '); - term.Append(formatter.FormatRef(root)); - } - else - { - term.Append("Private composition root of type "); - term.Append(formatter.FormatRef(composition.Source.Source, root.Injection.Type)); - term.Append('.'); - } - - if (!hints.IsResolveEnabled) + foreach (var rootComment in rootComments) { - return [term.ToString()]; + code.AppendLine(rootComment); } - - var resolvers = resolversBuilder.Build(new RootContext(composition.Source.Source, ImmutableArray.Create(root))); - if (!resolvers.Any()) + + IReadOnlyCollection CreateRootTerms(Root root) { + var term = new StringBuilder(); + if (root.IsPublic) + { + term.Append(formatter.FormatRef(composition.Source.Source, root.Injection.Type)); + term.Append(' '); + term.Append(formatter.FormatRef(root)); + } + else + { + term.Append("Private composition root of type "); + term.Append(formatter.FormatRef(composition.Source.Source, root.Injection.Type)); + term.Append('.'); + } + + if (!hints.IsResolveEnabled) + { + return [term.ToString()]; + } + + var resolvers = resolversBuilder.Build(new RootContext(composition.Source.Source, ImmutableArray.Create(root))); + if (!resolvers.Any()) + { + return [term.ToString()]; + } + + if (root.IsPublic) + { + term.Append("
or using "); + } + else + { + term.Append(" It can be resolved by "); + } + + if (root.Injection.Tag is null) + { + term.Append(formatter.FormatRef($"{hints.ResolveMethodName}{{T}}()")); + term.Append(" method: "); + term.Append(hints.ResolveMethodName); + term.Append("<"); + } + else + { + term.Append(formatter.FormatRef($"{hints.ResolveByTagMethodName}{{T}}(object)")); + term.Append(" method: "); + term.Append(hints.ResolveByTagMethodName); + term.Append("<"); + } + + term.Append(comments.Escape(root.TypeDescription.Name)); + term.Append(">("); + term.Append(root.Injection.Tag != null ? root.Injection.Tag.ValueToString() : ""); + term.Append(")"); return [term.ToString()]; } - - if (root.IsPublic) - { - term.Append("
or using "); - } - else - { - term.Append(" It can be resolved by "); - } - if (root.Injection.Tag is null) - { - term.Append(formatter.FormatRef($"{hints.ResolveMethodName}{{T}}()")); - term.Append(" method: "); - term.Append(hints.ResolveMethodName); - term.Append("<"); - } - else - { - term.Append(formatter.FormatRef($"{hints.ResolveByTagMethodName}{{T}}(object)")); - term.Append(" method: "); - term.Append(hints.ResolveByTagMethodName); - term.Append("<"); - } + IReadOnlyCollection CreateRootDescriptions(Root root) => + root.Source.Comments.Count > 0 + ? root.Source.Comments.Select(comments.Escape).ToList() + : [$"Provides a composition root of type {formatter.FormatRef(composition.Source.Source, root.Node.Type)}."]; + } - term.Append(comments.Escape(root.TypeDescription.Name)); - term.Append(">("); - term.Append(root.Injection.Tag != null ? root.Injection.Tag.ValueToString() : ""); - term.Append(")"); - return [term.ToString()]; + var root = orderedRoots.FirstOrDefault(i => i.IsPublic); + if (root is not null) + { + code.AppendLine("/// "); + code.AppendLine($"/// This example shows how to get an instance of type {formatter.FormatRef(composition.Source.Source, root.Node.Type)} using the composition root {formatter.FormatRef(root)}:"); + code.AppendLine("/// "); + code.AppendLine($"/// {(composition.TotalDisposablesCount == 0 ? "" : "using ")}var composition = new {composition.Source.Source.Name.ClassName}({string.Join(", ", composition.Args.Where(i => i.Node.Arg?.Source.Kind == ArgKind.Class).Select(arg => arg.VariableDeclarationName))});"); + code.AppendLine($"/// var instance = composition.{formatter.Format(root)};"); + code.AppendLine("/// "); + code.AppendLine("/// See also:"); + code.AppendLine("///
"); + code.AppendLine("///
"); + code.AppendLine("///
"); + code.AppendLine("///
"); } - IReadOnlyCollection CreateRootDescriptions(Root root) => - root.Source.Comments.Count > 0 - ? root.Source.Comments.Select(comments.Escape).ToList() - : [ $"Provides a composition root of type {formatter.FormatRef(composition.Source.Source, root.Node.Type)}." ]; - } - - var root = orderedRoots.FirstOrDefault(i => i.IsPublic); - if (root is not null) - { - code.AppendLine("/// "); - code.AppendLine($"/// This example shows how to get an instance of type {formatter.FormatRef(composition.Source.Source, root.Node.Type)} using the composition root {formatter.FormatRef(root)}:"); - code.AppendLine("/// "); - code.AppendLine($"/// {(composition.TotalDisposablesCount == 0 ? "" : "using ")}var composition = new {composition.Source.Source.Name.ClassName}({string.Join(", ", composition.Args.Where(i => i.Node.Arg?.Source.Kind == ArgKind.Class).Select(arg => arg.VariableDeclarationName))});"); - code.AppendLine($"/// var instance = composition.{formatter.Format(root)};"); - code.AppendLine("/// "); - code.AppendLine("/// See also:"); - code.AppendLine("///
"); - code.AppendLine("///
"); - code.AppendLine("///
"); - code.AppendLine("///
"); - } + code.AppendLine("///
"); + if (!composition.Diagram.IsEmpty) + { + var diagramUrl = mermaidUrlBuilder.Build(composition.Diagram.Select(i => i.Text)); + code.AppendLine($"///
Class diagram
"); + } - code.AppendLine("///
"); - if (!composition.Diagram.IsEmpty) + code.AppendLine("///
This class was created by Pure.DI source code generator."); + } + finally { - var diagramUrl = mermaidUrlBuilder.Build(composition.Diagram.Select(i => i.Text)); - code.AppendLine($"///
Class diagram
"); + code.AppendLine("///
"); } - - code.AppendLine("///
This class was created by Pure.DI source code generator."); - code.AppendLine("/// "); } } \ No newline at end of file diff --git a/src/Pure.DI.Core/Core/Code/DisposeMethodBuilder.cs b/src/Pure.DI.Core/Core/Code/DisposeMethodBuilder.cs index ffa599c4c..175377ef9 100644 --- a/src/Pure.DI.Core/Core/Code/DisposeMethodBuilder.cs +++ b/src/Pure.DI.Core/Core/Code/DisposeMethodBuilder.cs @@ -125,7 +125,7 @@ public CompositionCode Build(CompositionCode composition) code.AppendLine(); code.AppendLine("/// "); code.AppendLine("/// Implement this partial method to handle the exception on async disposing."); - code.AppendLine("/// "); + code.AppendLine("/// "); code.AppendLine("/// The disposable instance."); code.AppendLine("/// Exception occurring during disposal."); code.AppendLine("/// The actual type of instance being disposed of."); diff --git a/src/Pure.DI.Core/Core/Code/Formatter.cs b/src/Pure.DI.Core/Core/Code/Formatter.cs index 95e5a27ae..8621a0ca1 100644 --- a/src/Pure.DI.Core/Core/Code/Formatter.cs +++ b/src/Pure.DI.Core/Core/Code/Formatter.cs @@ -1,11 +1,26 @@ // ReSharper disable ClassNeverInstantiated.Global +#pragma warning disable CS9113 // Parameter is unread. namespace Pure.DI.Core.Code; -internal class Formatter( - ITypeResolver typeResolver, - IComments comments) +internal class Formatter(IComments comments) : IFormatter { +#if ROSLYN4_8_OR_GREATER + private static readonly SymbolDisplayFormat RefFormat = new( + genericsOptions: + SymbolDisplayGenericsOptions.IncludeTypeParameters, + typeQualificationStyle: + SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, + memberOptions: + SymbolDisplayMemberOptions.IncludeType | + SymbolDisplayMemberOptions.IncludeContainingType, + miscellaneousOptions: + SymbolDisplayMiscellaneousOptions.ExpandValueTuple | + SymbolDisplayMiscellaneousOptions.UseSpecialTypes | + SymbolDisplayMiscellaneousOptions.ExpandNullable + ); +#endif + public string Format(Root root) { var sb = new StringBuilder(); @@ -43,7 +58,7 @@ public string FormatRef(Root root) { sb.Append('{'); sb.Append(string.Join(", ", typeArgs.Select(i => i.Name))); - sb.Append('}'); + sb.Append('}'); } sb.Append('('); @@ -52,13 +67,22 @@ public string FormatRef(Root root) return FormatRef(sb.ToString()); } - public string FormatRef(string text) => - $""; + public string FormatRef(string text) + { +#if ROSLYN4_8_OR_GREATER + return $""; +#else + return text; +#endif + } - public string FormatRef(MdSetup setup, ITypeSymbol type) => - FormatRef( - comments.Escape( - typeResolver.Resolve(setup, type).Name - .Replace('<', '{') - .Replace('>', '}'))); + public string FormatRef(MdSetup setup, ITypeSymbol type) + { +#if ROSLYN4_8_OR_GREATER + var originalDefinitionTypeName = type.OriginalDefinition.ToDisplayString(NullableFlowState.None, RefFormat); + return FormatRef(comments.Escape(originalDefinitionTypeName.Replace('<', '{').Replace('>', '}'))); +#else + return type.OriginalDefinition.ToDisplayString(NullableFlowState.None, SymbolDisplayFormat.MinimallyQualifiedFormat); +#endif + } } \ No newline at end of file diff --git a/src/Pure.DI.Core/Core/Code/RootMethodsCommenter.cs b/src/Pure.DI.Core/Core/Code/RootMethodsCommenter.cs index 2e2690e7f..1b2f08385 100644 --- a/src/Pure.DI.Core/Core/Code/RootMethodsCommenter.cs +++ b/src/Pure.DI.Core/Core/Code/RootMethodsCommenter.cs @@ -15,32 +15,44 @@ public void AddComments(CompositionCode composition, Root root) var rootComments = root.Source.Comments; var code = composition.Code; code.AppendLine("/// "); - code.AppendLine("/// "); - if (rootComments.Count > 0) + try { - foreach (var comment in comments.Format(rootComments, true)) + code.AppendLine("/// "); + try { - code.AppendLine(comment); + if (rootComments.Count > 0) + { + foreach (var comment in comments.Format(rootComments, true)) + { + code.AppendLine(comment); + } + } + else + { + code.AppendLine($"/// Provides a composition root of type {formatter.FormatRef(composition.Source.Source, root.Node.Type)}."); + } } + finally + { + code.AppendLine("/// "); + } + + if (!root.IsPublic) + { + return; + } + + code.AppendLine("/// "); + code.AppendLine($"/// This example shows how to get an instance of type {formatter.FormatRef(composition.Source.Source, root.Node.Type)}:"); + code.AppendLine("/// "); + code.AppendLine($"/// {(composition.TotalDisposablesCount == 0 ? "" : "using ")}var composition = new {composition.Source.Source.Name.ClassName}({string.Join(", ", composition.Args.Where(i => i.Node.Arg?.Source.Kind == ArgKind.Class).Select(arg => arg.VariableDeclarationName))});"); + code.AppendLine($"/// var instance = composition.{formatter.Format(root)};"); + code.AppendLine("/// "); + code.AppendLine("/// "); } - else - { - code.AppendLine($"/// Provides a composition root of type {formatter.FormatRef(composition.Source.Source, root.Node.Type)}."); - } - - code.AppendLine("/// "); - if (!root.IsPublic) + finally { - return; + code.AppendLine("/// "); } - - code.AppendLine("/// "); - code.AppendLine($"/// This example shows how to get an instance of type {formatter.FormatRef(composition.Source.Source, root.Node.Type)}:"); - code.AppendLine("/// "); - code.AppendLine($"/// {(composition.TotalDisposablesCount == 0 ? "" : "using ")}var composition = new {composition.Source.Source.Name.ClassName}({string.Join(", ", composition.Args.Where(i => i.Node.Arg?.Source.Kind == ArgKind.Class).Select(arg => arg.VariableDeclarationName))});"); - code.AppendLine($"/// var instance = composition.{formatter.Format(root)};"); - code.AppendLine("/// "); - code.AppendLine("/// "); - code.AppendLine("/// "); } } \ No newline at end of file diff --git a/tests/Pure.DI.UsageTests/Pure.DI.UsageTests.csproj b/tests/Pure.DI.UsageTests/Pure.DI.UsageTests.csproj index 4e609e5d3..fc4f4bb89 100644 --- a/tests/Pure.DI.UsageTests/Pure.DI.UsageTests.csproj +++ b/tests/Pure.DI.UsageTests/Pure.DI.UsageTests.csproj @@ -1,8 +1,10 @@ + true true ../../.logs + CS1591