From ff6f436c15a671d84ba19f753bd3968d1cf69013 Mon Sep 17 00:00:00 2001 From: Laszlo Paillat Date: Sun, 28 Jan 2024 15:11:27 +0100 Subject: [PATCH] migrated to net8 updated signing key updated libraries added support for record --- .github/workflows/CodeCoverage.yml | 2 +- .github/workflows/ContinuousIntegration.yml | 2 +- .github/workflows/PullRequest.yml | 2 +- .github/workflows/Release.yml | 6 +- README.md | 3 +- Release.bat | 4 +- documentation/NEXT_RELEASENOTES.txt | 1 + ds.snk.gpg | Bin 677 -> 677 bytes source/.editorconfig | 331 +++++++++++++++--- .../Api/IUrlFactory.cs | 4 +- .../DefaultDocumentation.Api/Api/IWriter.cs | 17 +- .../DefaultDocumentation.Api.csproj | 26 +- .../Documentations.cs | 4 +- ...cItemExtension.cs => DocItemExtensions.cs} | 46 ++- ...ension.cs => IGeneralContextExtensions.cs} | 35 +- .../Extensions/IPageContextExtensions.cs | 39 +++ .../Extensions/ISymbolExtensions.cs | 30 ++ ...riterExtension.cs => IWriterExtensions.cs} | 17 +- source/DefaultDocumentation.Api/IContext.cs | 7 +- .../IGeneralContext.cs | 14 +- .../DefaultDocumentation.Api/IPageContext.cs | 22 ++ source/DefaultDocumentation.Api/ISettings.cs | 2 +- ...mentExtension.cs => XElementExtensions.cs} | 2 +- .../MissingAPIs/ArgumentNullException.cs | 20 ++ .../CallerArgumentExpressionAttribute.cs | 17 + .../MissingAPIs/NullableAttributes.cs | 90 +++++ .../Internal/NullableAttributes.cs | 29 -- .../Models/DocItem.cs | 13 +- .../Models/EntityDocItem.cs | 4 +- .../Models/ExternDocItem.cs | 17 +- .../Models/Members/ConstructorDocItem.cs | 7 +- .../Models/Members/EnumFieldDocItem.cs | 7 +- .../Models/Members/EventDocItem.cs | 7 +- .../ExplicitInterfaceImplementationDocItem.cs | 12 +- .../Models/Members/FieldDocItem.cs | 4 +- .../Models/Members/MethodDocItem.cs | 4 +- .../Models/Members/OperatorDocItem.cs | 7 +- .../Models/Members/PropertyDocItem.cs | 7 +- .../Models/NamespaceDocItem.cs | 5 +- .../Models/Types/TypeDocItem.cs | 2 +- .../DefaultDocumentation.Common.csproj | 25 +- .../DefaultDocumentation.Common/Generator.cs | 109 +++--- .../Internal/Context.cs | 11 +- .../Internal/DocItemReader.cs | 27 +- ...ntityExtension.cs => IEntityExtensions.cs} | 8 +- .../Internal/Extensions/XElementExtension.cs | 15 - .../Internal/Extensions/XElementExtensions.cs | 18 + .../Internal/GeneralContext.cs | 23 +- .../Internal/PageContext.cs | 62 ++++ .../Internal/PageWriter.cs | 27 +- .../Internal/Settings.cs | 28 +- .../DefaultDocumentation.Console.csproj | 41 +-- .../DefaultDocumentation.Markdown.csproj | 26 +- .../Elements/CElement.cs | 3 + .../Elements/CodeElement.cs | 3 + .../Elements/ListElement.cs | 3 + .../Elements/NoteElement.cs | 3 + .../Elements/ParamRefElement.cs | 3 + .../Elements/SeeElement.cs | 3 + .../Elements/TypeParamRefElement.cs | 3 + ...cItemExtension.cs => DocItemExtensions.cs} | 9 +- ...ension.cs => IGeneralContextExtensions.cs} | 35 +- ...riterExtension.cs => IWriterExtensions.cs} | 124 +++++-- ...tory.cs => BaseMarkdownFileNameFactory.cs} | 7 +- .../FileNameFactories/FullNameFactory.cs | 2 +- .../FileNameFactories/Md5Factory.cs | 2 +- .../FileNameFactories/NameAndMd5MixFactory.cs | 2 +- .../FileNameFactories/NameFactory.cs | 2 +- .../Internal/CodeRegion.cs | 1 + ...StringExtension.cs => StringExtensions.cs} | 2 +- ...mentExtension.cs => XElementExtensions.cs} | 2 +- .../Sections/ChildrenSection.cs | 3 + .../Sections/DefinitionSection.cs | 29 +- .../Sections/DerivedSection.cs | 2 + .../Sections/ExceptionSection.cs | 3 + .../Sections/FooterSection.cs | 4 +- .../Sections/HeaderSection.cs | 6 +- .../Sections/SeeAlsoSection.cs | 3 + .../Sections/TableOfContentsSection.cs | 2 + .../Sections/TitleSection.cs | 20 +- .../UrlFactories/DocItemFactory.cs | 12 +- .../UrlFactories/DotnetApiFactory.cs | 5 +- .../Writers/MarkdownWriter.cs | 13 +- .../Writers/OverrideWriter.cs | 59 +++- .../Writers/PrefixedWriter.cs | 16 +- .../DefaultDocumentation.Test/AssemblyInfo.cs | 29 +- .../DefaultDocumentation.Test.csproj | 40 ++- ...ensionTest.cs => DocItemExtensionsTest.cs} | 2 +- ...st.cs => IGeneralContextExtensionsTest.cs} | 2 +- .../Markdown/AWriterTest.cs | 46 +-- .../Markdown/Elements/AElementTest.cs | 13 +- .../Markdown/Elements/CodeElementTest.cs | 16 +- .../Markdown/Sections/ASectionTest.cs | 5 +- .../Sections/DefinitionSectionTest.cs | 31 +- .../Markdown/Sections/EventTypeSectionTest.cs | 5 +- .../Sections/FieldValueSectionTest.cs | 5 +- .../Sections/ParametersSectionTest.cs | 12 +- .../Markdown/Sections/ReturnsSectionTest.cs | 3 +- .../Markdown/Sections/TitleSectionTest.cs | 5 +- .../Markdown/Sections/ValueSectionTest.cs | 3 +- .../Models/DocItemTest.cs | 2 +- .../Models/ExternDocItemTest.cs | 2 +- .../Models/Members/ConstructorDocItemTest.cs | 4 +- .../Models/Members/EnumFieldDocItemTest.cs | 4 +- .../Models/Members/EventDocItemTest.cs | 4 +- ...licitInterfaceImplementationDocItemTest.cs | 12 +- .../Models/Members/FieldDocItemTest.cs | 4 +- .../Models/Members/MethodDocItemTest.cs | 4 +- .../Models/Members/OperatorDocItemTest.cs | 4 +- .../Models/Members/PropertyDocItemTest.cs | 4 +- .../Models/NamespaceDocItemTest.cs | 2 +- .../Models/Types/TypeDocItemTest.cs | 4 +- .../DefaultDocumentation.Test/PageWriter.cs | 77 ---- .../DefaultDocumentation.csproj | 58 +-- source/Directory.Build.props | 11 +- .../DefaultDocumentation.PluginExample.csproj | 32 +- .../FolderFileNameFactory.cs | 17 +- .../NewElement.cs | 3 + .../NewSection.cs | 2 + 119 files changed, 1395 insertions(+), 746 deletions(-) rename source/DefaultDocumentation.Api/Extensions/{DocItemExtension.cs => DocItemExtensions.cs} (74%) rename source/DefaultDocumentation.Api/Extensions/{IGeneralContextExtension.cs => IGeneralContextExtensions.cs} (77%) create mode 100644 source/DefaultDocumentation.Api/Extensions/IPageContextExtensions.cs create mode 100644 source/DefaultDocumentation.Api/Extensions/ISymbolExtensions.cs rename source/DefaultDocumentation.Api/Extensions/{IWriterExtension.cs => IWriterExtensions.cs} (91%) create mode 100644 source/DefaultDocumentation.Api/IPageContext.cs rename source/DefaultDocumentation.Api/Internal/Extensions/{XElementExtension.cs => XElementExtensions.cs} (91%) create mode 100644 source/DefaultDocumentation.Api/Internal/MissingAPIs/ArgumentNullException.cs create mode 100644 source/DefaultDocumentation.Api/Internal/MissingAPIs/CallerArgumentExpressionAttribute.cs create mode 100644 source/DefaultDocumentation.Api/Internal/MissingAPIs/NullableAttributes.cs delete mode 100644 source/DefaultDocumentation.Api/Internal/NullableAttributes.cs rename source/DefaultDocumentation.Common/Internal/Extensions/{IEntityExtension.cs => IEntityExtensions.cs} (56%) delete mode 100644 source/DefaultDocumentation.Common/Internal/Extensions/XElementExtension.cs create mode 100644 source/DefaultDocumentation.Common/Internal/Extensions/XElementExtensions.cs create mode 100644 source/DefaultDocumentation.Common/Internal/PageContext.cs rename source/DefaultDocumentation.Markdown/Extensions/{DocItemExtension.cs => DocItemExtensions.cs} (67%) rename source/DefaultDocumentation.Markdown/Extensions/{IGeneralContextExtension.cs => IGeneralContextExtensions.cs} (76%) rename source/DefaultDocumentation.Markdown/Extensions/{IWriterExtension.cs => IWriterExtensions.cs} (79%) rename source/DefaultDocumentation.Markdown/FileNameFactories/{AMarkdownFactory.cs => BaseMarkdownFileNameFactory.cs} (89%) rename source/DefaultDocumentation.Markdown/Internal/Extensions/{StringExtension.cs => StringExtensions.cs} (96%) rename source/DefaultDocumentation.Markdown/Internal/Extensions/{XElementExtension.cs => XElementExtensions.cs} (97%) rename source/DefaultDocumentation.Test/Extensions/{DocItemExtensionTest.cs => DocItemExtensionsTest.cs} (99%) rename source/DefaultDocumentation.Test/Extensions/{IGeneralContextExtensionTest.cs => IGeneralContextExtensionsTest.cs} (96%) delete mode 100644 source/DefaultDocumentation.Test/PageWriter.cs diff --git a/.github/workflows/CodeCoverage.yml b/.github/workflows/CodeCoverage.yml index 1a71a7db..ced23c53 100644 --- a/.github/workflows/CodeCoverage.yml +++ b/.github/workflows/CodeCoverage.yml @@ -18,7 +18,7 @@ jobs: - name: Set up dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: '7.0.x' + dotnet-version: '8.0.x' - name: Collect coverage run: dotnet test source\DefaultDocumentation.sln -c Release -p:CollectCoverage=true -p:CoverletOutputFormat=lcov -p:CoverletOutput=..\..\build\lcov.info -p:Exclude="[*]System.Diagnostics.CodeAnalysis.*" /p:TEST=true diff --git a/.github/workflows/ContinuousIntegration.yml b/.github/workflows/ContinuousIntegration.yml index ebe29d4c..8ac31019 100644 --- a/.github/workflows/ContinuousIntegration.yml +++ b/.github/workflows/ContinuousIntegration.yml @@ -22,7 +22,7 @@ jobs: - name: Set up dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: '7.0.x' + dotnet-version: '8.0.x' source-url: https://nuget.pkg.github.com/Doraku/index.json env: NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/PullRequest.yml b/.github/workflows/PullRequest.yml index a6278636..04e261a5 100644 --- a/.github/workflows/PullRequest.yml +++ b/.github/workflows/PullRequest.yml @@ -15,7 +15,7 @@ jobs: - name: Set up dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: '7.0.x' + dotnet-version: '8.0.x' - name: Build run: dotnet build source\DefaultDocumentation.sln -c Release -p:TreatWarningsAsErrors=true /p:TEST=true diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml index d37e6162..638ecacc 100644 --- a/.github/workflows/Release.yml +++ b/.github/workflows/Release.yml @@ -16,7 +16,7 @@ jobs: - name: Set up dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: '7.0.x' + dotnet-version: '8.0.x' source-url: https://api.nuget.org/v3/index.json env: NUGET_AUTH_TOKEN: ${{ secrets.NUGET_API_KEY }} @@ -52,8 +52,8 @@ jobs: - name: Generate documentation run: | - dotnet run --project source\DefaultDocumentation.Console\DefaultDocumentation.Console.csproj --framework net6.0 -c Release --ConfigurationFilePath source\DefaultDocumentation.Api\DefaultDocumentation.json - dotnet run --project source\DefaultDocumentation.Console\DefaultDocumentation.Console.csproj --framework net6.0 -c Release --ConfigurationFilePath source\DefaultDocumentation.Markdown\DefaultDocumentation.json + dotnet run --project source\DefaultDocumentation.Console\DefaultDocumentation.Console.csproj --framework net8.0 -c Release --ConfigurationFilePath source\DefaultDocumentation.Api\DefaultDocumentation.json + dotnet run --project source\DefaultDocumentation.Console\DefaultDocumentation.Console.csproj --framework net8.0 -c Release --ConfigurationFilePath source\DefaultDocumentation.Markdown\DefaultDocumentation.json - name: Publish packages run: dotnet nuget push build\*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json diff --git a/README.md b/README.md index d106cb65..f24e0f75 100644 --- a/README.md +++ b/README.md @@ -243,7 +243,8 @@ State elements with which access modifier should be generated. All by default, a - `Protected`: generates documentation for 'protected' access modifier - `Internal`: generates documentation for 'internal' access modifier - `ProtectedInternal`: generates documentation for 'protected internal' access modifier -- `PrivateProtected`generates documentation for 'private protected' access modifier +- `PrivateProtected`: generates documentation for 'private protected' access modifier +- `Api`: generates documentation for 'public', 'protected' and 'protected internal' access modifier. ## IncludeUndocumentedItems diff --git a/Release.bat b/Release.bat index 482fa469..5224dcf6 100644 --- a/Release.bat +++ b/Release.bat @@ -5,5 +5,5 @@ dotnet clean source\DefaultDocumentation.sln -c Release dotnet pack source\DefaultDocumentation.sln -c Release -o build /p:LOCAL_VERSION=true -dotnet run --project source\DefaultDocumentation.Console\DefaultDocumentation.Console.csproj --framework net6.0 -c Release --ConfigurationFilePath source\DefaultDocumentation.Api\DefaultDocumentation.json -dotnet run --project source\DefaultDocumentation.Console\DefaultDocumentation.Console.csproj --framework net6.0 -c Release --ConfigurationFilePath source\DefaultDocumentation.Markdown\DefaultDocumentation.json \ No newline at end of file +dotnet run --project source\DefaultDocumentation.Console\DefaultDocumentation.Console.csproj --framework net8.0 -c Release --ConfigurationFilePath source\DefaultDocumentation.Api\DefaultDocumentation.json +dotnet run --project source\DefaultDocumentation.Console\DefaultDocumentation.Console.csproj --framework net8.0 -c Release --ConfigurationFilePath source\DefaultDocumentation.Markdown\DefaultDocumentation.json \ No newline at end of file diff --git a/documentation/NEXT_RELEASENOTES.txt b/documentation/NEXT_RELEASENOTES.txt index d9edce9e..00c828d4 100644 --- a/documentation/NEXT_RELEASENOTES.txt +++ b/documentation/NEXT_RELEASENOTES.txt @@ -1,6 +1,7 @@ ## New features - added DefaultDocumentation.GeneratedAccessModifiers.Api which regroup Public, Protected and InternalProtected access modifiers (closes #116) +- added support for record ## Bug fixes diff --git a/ds.snk.gpg b/ds.snk.gpg index b9d4115f6878ce95596be4c86467e7ef8706f63f..7324c285e50bd55bfe46ef5bf435f7b4f56f6504 100644 GIT binary patch literal 677 zcmV;W0$Tly4Fm}T0u4Hbx1qYF-o?`C0b95F{`L@0+X5FyAYitIj(EIiiACNZ@(|=V z(jhFf9C`r`JBHqz%<#X$5S4($wkL$lhp(U4?_(HDJPgZBnA&C?H6orXdL~QJ(Yly`tFx1yw9?i(kYhW2%f0^^Fm5k(!L89nfK|0wObvKZ&q{Z_)uu zcaRj3q!)NGW!PNc#;Oiq$2z$8fBEoc^Ww+%y+w2=Ny~ri7H;+cLL3H!bcw*To7fFx z1xdIgE2ktT%HZQh(YIsCPbgyEdkL0Zip*?>97A4PD?)Mj1+KD_^V>)T$v(Yp6jWQT zrB~Iy@{ld%rEq!=y^ONy9U4Vqps3M(b9HHj zsy@^q{JVkf4>NF&nN1sR5hbsWw*R6*&ea7F8q)0XRN>SOUgdAiTucsiLt50A%oWtIAQ Lf_3B=az<{#RcuX9 literal 677 zcmV;W0$Tly4Fm}T0v<7PZ!D8RyT#J!0eqdt*+C<)fSN!IWyY5^d@w8KMbzF_=m>cL z$$AfLM3mrDgfuV}W+oFS-L}5~;k9(WT9WfiBEJpTif<(2TJ(?&fx-9VCHhl-nWw$I z`xhn?*}LzymfmF)wG85h6(gTlGHght;iUU*L)wT@*hXu^Jq05x1|aH)I*4nn=Vuq+ zDmmKNH#KEfu>5ZxnO+4P4BzFjDmn86e4le1(j8@yC?=(4eOgvAa=f_cY42t@l3V|N zy&`Acq72Xin42AhoIS#SYI9OScA0aTK~o-}AvAdGHDd8!`jljvya=)n^JAO$mJMs1 zv_i33c$S+DD6nc1cK}b5`lT8Z{>H$m0@L``aIh^L$x=1Y?S@JosLvt9A8jJYyXLFr zbdWlthqRo_Aav2gf>i|Gb^U){(l!$*SwczV!r8_ZYpb~S*z@GLQ_ok=DiFpfp8VC0 zI5gK7sIGHyx^I`VzBbVrH7kAFqffjHeCQq>Mvmw)Xk?tY9xXuJp?gk@&qn1kZw-|( zv8`Ye)v6iwY}+c5G7D(I$LTSh@SlgjOucl;FQ{u-5)`#hIqtURGf%J%Xd&0v-1o^P zBDcT{zh(W>UIPDHYAFxA-zHW_tyry0z~eBd-{hZ*uL*)x53Ct}{SGF*;EgPvD+NCp z<{$lmB9Ob^Q{qg3fxJf@lca3WalG<}OcLqzXcu)1gyVC@C*NO&2|7!0`zC8oO<@Y6 zc#(j|;EPV;Nr=!&T{7up*uA_)SV0P+e;inq;<_uc@2Cv2@vqaf=PDdD7WdMcVht{u zUlp<0U*g /// Gets the url of the given id. Returns null of the instance does not know how to handle the provided id. /// - /// The of the current documentation generation process. + /// The of the current documentation generation process. /// The id to get the url for. /// The url of the given id. - string? GetUrl(IGeneralContext context, string id); + string? GetUrl(IPageContext context, string id); } } diff --git a/source/DefaultDocumentation.Api/Api/IWriter.cs b/source/DefaultDocumentation.Api/Api/IWriter.cs index e08af287..b8918576 100644 --- a/source/DefaultDocumentation.Api/Api/IWriter.cs +++ b/source/DefaultDocumentation.Api/Api/IWriter.cs @@ -1,5 +1,4 @@ using System; -using DefaultDocumentation.Models; namespace DefaultDocumentation.Api { @@ -9,27 +8,15 @@ namespace DefaultDocumentation.Api public interface IWriter { /// - /// Gets the of the current documentation generation process. + /// Gets the of the current documentation generation process. /// - IGeneralContext Context { get; } - - /// - /// Gets the for which the documentation is being generated. - /// - DocItem DocItem { get; } + IPageContext Context { get; } /// /// Gets or sets the length of the documentation text currently produced. /// int Length { get; set; } - /// - /// Gets or sets extra data for the current documentation generation. - /// - /// The key of the data. - /// The value of the data. - object? this[string key] { get; set; } - /// /// Appends a string at the end of the documentation text. /// diff --git a/source/DefaultDocumentation.Api/DefaultDocumentation.Api.csproj b/source/DefaultDocumentation.Api/DefaultDocumentation.Api.csproj index 779ee5ad..ae7b3731 100644 --- a/source/DefaultDocumentation.Api/DefaultDocumentation.Api.csproj +++ b/source/DefaultDocumentation.Api/DefaultDocumentation.Api.csproj @@ -1,15 +1,17 @@ - - Api to extend the creation of documentation of DefaultDocumentation. - DefaultDocumentation - netstandard2.0 - true - enable - - - - - - + + Api to extend the creation of documentation of DefaultDocumentation. + DefaultDocumentation + netstandard2.0 + true + enable + + + + + + + + diff --git a/source/DefaultDocumentation.Api/Documentations.cs b/source/DefaultDocumentation.Api/Documentations.cs index e716c24a..cf7328f9 100644 --- a/source/DefaultDocumentation.Api/Documentations.cs +++ b/source/DefaultDocumentation.Api/Documentations.cs @@ -5,11 +5,11 @@ namespace Api /// /// This is the full API documentation of DefaultDocumentation. /// - internal static class AssemblyDoc { } + internal static class AssemblyDoc; /// /// The namespace contains interfaces that can be used to add custom features to the documentation generation. /// - internal static class NamespaceDoc { } + internal static class NamespaceDoc; } } diff --git a/source/DefaultDocumentation.Api/Extensions/DocItemExtension.cs b/source/DefaultDocumentation.Api/Extensions/DocItemExtensions.cs similarity index 74% rename from source/DefaultDocumentation.Api/Extensions/DocItemExtension.cs rename to source/DefaultDocumentation.Api/Extensions/DocItemExtensions.cs index fc7935c5..80ac36a2 100644 --- a/source/DefaultDocumentation.Api/Extensions/DocItemExtension.cs +++ b/source/DefaultDocumentation.Api/Extensions/DocItemExtensions.cs @@ -10,7 +10,7 @@ namespace DefaultDocumentation.Models /// /// Provides extension methods on the type. /// - public static class DocItemExtension + public static class DocItemExtensions { private static GeneratedPages GetPage(DocItem item) => item switch { @@ -37,11 +37,17 @@ public static class DocItemExtension /// The for which to get if it has its own page. /// The used to generation the documentation. /// if the has its own page, otherwise . - public static bool HasOwnPage(this DocItem item, IGeneralContext context) => item switch + public static bool HasOwnPage(this DocItem item, IGeneralContext context) { - AssemblyDocItem when !string.IsNullOrEmpty(context.Settings.AssemblyPageName) || item.Documentation != null || context.Items.Values.OfType().Skip(1).Any() => true, - _ => (context.Settings.GeneratedPages & GetPage(item)) != 0 - }; + ArgumentNullException.ThrowIfNull(item); + ArgumentNullException.ThrowIfNull(context); + + return item switch + { + AssemblyDocItem when !string.IsNullOrEmpty(context.Settings.AssemblyPageName) || item.Documentation != null || context.Items.Values.OfType().Skip(1).Any() => true, + _ => (context.Settings.GeneratedPages & GetPage(item)) != 0 + }; + } /// /// Searchs recursively on the given parent a with the provided name. @@ -50,17 +56,22 @@ public static class DocItemExtension /// The name of the . /// The if found, else . /// if the was found, else . - public static bool TryGetTypeParameterDocItem(this DocItem? item, string name, [NotNullWhen(true)] out TypeParameterDocItem? typeParameterDocItem) + public static bool TryGetTypeParameterDocItem(this DocItem item, string name, [NotNullWhen(true)] out TypeParameterDocItem? typeParameterDocItem) { + ArgumentNullException.ThrowIfNull(item); + ArgumentNullException.ThrowIfNull(name); + + DocItem? currentItem = item; typeParameterDocItem = null; - while (item != null && typeParameterDocItem == null) + + while (currentItem != null && typeParameterDocItem == null) { - if (item is ITypeParameterizedDocItem typeParameters) + if (currentItem is ITypeParameterizedDocItem typeParameters) { typeParameterDocItem = typeParameters.TypeParameters.FirstOrDefault(i => i.TypeParameter.Name == name); } - item = item.Parent; + currentItem = currentItem.Parent; } return typeParameterDocItem != null; @@ -73,17 +84,22 @@ public static bool TryGetTypeParameterDocItem(this DocItem? item, string name, [ /// The name of the . /// The if found, else . /// if the was found, else . - public static bool TryGetParameterDocItem(this DocItem? item, string name, [NotNullWhen(true)] out ParameterDocItem? parameterDocItem) + public static bool TryGetParameterDocItem(this DocItem item, string name, [NotNullWhen(true)] out ParameterDocItem? parameterDocItem) { + ArgumentNullException.ThrowIfNull(item); + ArgumentNullException.ThrowIfNull(name); + + DocItem? currentItem = item; parameterDocItem = null; - while (item != null && parameterDocItem == null) + + while (currentItem != null && parameterDocItem == null) { - if (item is IParameterizedDocItem typeParameters) + if (currentItem is IParameterizedDocItem typeParameters) { parameterDocItem = typeParameters.Parameters.FirstOrDefault(i => i.Parameter.Name == name); } - item = item.Parent; + currentItem = currentItem.Parent; } return parameterDocItem != null; @@ -96,8 +112,10 @@ public static bool TryGetParameterDocItem(this DocItem? item, string name, [NotN /// The parents of the given from the top parent. public static IEnumerable GetParents(this DocItem item) { + ArgumentNullException.ThrowIfNull(item); + Stack parents = new(); - for (DocItem? parent = item.Parent; parent != null; parent = parent.Parent) + for (DocItem? parent = item?.Parent; parent != null; parent = parent.Parent) { parents.Push(parent); } diff --git a/source/DefaultDocumentation.Api/Extensions/IGeneralContextExtension.cs b/source/DefaultDocumentation.Api/Extensions/IGeneralContextExtensions.cs similarity index 77% rename from source/DefaultDocumentation.Api/Extensions/IGeneralContextExtension.cs rename to source/DefaultDocumentation.Api/Extensions/IGeneralContextExtensions.cs index 8b3a0b6d..6eaf1842 100644 --- a/source/DefaultDocumentation.Api/Extensions/IGeneralContextExtension.cs +++ b/source/DefaultDocumentation.Api/Extensions/IGeneralContextExtensions.cs @@ -6,23 +6,21 @@ namespace DefaultDocumentation /// /// Provides extension methods on the type. /// - public static class IGeneralContextExtension + public static class IGeneralContextExtensions { - /// - /// Gets the url of the given . - /// - /// The of the current documentation file. - /// The for which to get the url. - /// The url of the given . - public static string GetUrl(this IGeneralContext context, DocItem item) => context.GetUrl(item.Id); - /// /// Gets the specific for the given kind. /// /// The of the current documentation file. /// The for which to get a specific . /// The specific to the provided . - public static IContext GetContext(this IGeneralContext context, DocItem item) => context.GetContext(item.GetType()); + public static IContext GetContext(this IGeneralContext context, DocItem item) + { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(item); + + return context.GetContext(item.GetType()); + } /// /// Gets a data from the specific of the provided if it exists, else from the . @@ -33,7 +31,13 @@ public static class IGeneralContextExtension /// The used to get the setting from a . /// The settings from the specific if it exists, otherwise from the . /// The should be for struct settings. - public static T? GetSetting(this IGeneralContext context, Type type, Func getter) => getter(context.GetContext(type)) ?? getter(context); + public static T? GetSetting(this IGeneralContext context, Type? type, Func getter) + { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(getter); + + return getter(context.GetContext(type)) ?? getter(context); + } /// /// Gets a data from the specific of the provided if it exists, else from the . @@ -44,6 +48,13 @@ public static class IGeneralContextExtension /// The used to get the setting from a . /// The settings from the specific if it exists, otherwise from the . /// The should be for struct settings. - public static T? GetSetting(this IGeneralContext context, DocItem item, Func getter) => context.GetSetting(item.GetType(), getter); + public static T? GetSetting(this IGeneralContext context, DocItem item, Func getter) + { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(item); + ArgumentNullException.ThrowIfNull(getter); + + return context.GetSetting(item.GetType(), getter); + } } } diff --git a/source/DefaultDocumentation.Api/Extensions/IPageContextExtensions.cs b/source/DefaultDocumentation.Api/Extensions/IPageContextExtensions.cs new file mode 100644 index 00000000..4142102a --- /dev/null +++ b/source/DefaultDocumentation.Api/Extensions/IPageContextExtensions.cs @@ -0,0 +1,39 @@ +using System.Linq; +using DefaultDocumentation.Models; + +namespace DefaultDocumentation +{ + /// + /// Provides extension methods on the type. + /// + public static class IPageContextExtensions + { + /// + /// Gets the url of the given in a specific page. + /// + /// The of the current documentation file. + /// The for which to get the url. + /// The url of the given . + public static string? GetUrl(this IPageContext context, DocItem item) + { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(item); + + return context.GetUrl(item.Id); + } + + /// + /// Gets the url of the given id in a specific page. + /// + /// The of the current documentation file. + /// The id for which to get the url. + /// The url of the given . + public static string? GetUrl(this IPageContext context, string id) + { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(id); + + return context.UrlFactories.Select(f => f.GetUrl(context, id)).FirstOrDefault(url => url is not null); + } + } +} diff --git a/source/DefaultDocumentation.Api/Extensions/ISymbolExtensions.cs b/source/DefaultDocumentation.Api/Extensions/ISymbolExtensions.cs new file mode 100644 index 00000000..fe35cc8d --- /dev/null +++ b/source/DefaultDocumentation.Api/Extensions/ISymbolExtensions.cs @@ -0,0 +1,30 @@ +using System.IO; +using DefaultDocumentation; +using ICSharpCode.Decompiler.CSharp.OutputVisitor; + +namespace ICSharpCode.Decompiler.TypeSystem +{ + /// + /// Provides extension methods on the type. + /// + public static class ISymbolExtensions + { + /// + /// Converts a into its string representation using the provided . + /// + /// The symbol to convert into its string representation. + /// The to use. + /// + public static string ToString(this ISymbol symbol, CSharpAmbience ambience) + { + ArgumentNullException.ThrowIfNull(symbol); + ArgumentNullException.ThrowIfNull(ambience); + + using StringWriter writer = new(); + + ambience.ConvertSymbol(symbol, TokenWriter.InsertRequiredSpaces(new TextWriterTokenWriter(writer)), FormattingOptionsFactory.CreateEmpty()); + + return writer.ToString(); + } + } +} diff --git a/source/DefaultDocumentation.Api/Extensions/IWriterExtension.cs b/source/DefaultDocumentation.Api/Extensions/IWriterExtensions.cs similarity index 91% rename from source/DefaultDocumentation.Api/Extensions/IWriterExtension.cs rename to source/DefaultDocumentation.Api/Extensions/IWriterExtensions.cs index c0373400..d7f7df77 100644 --- a/source/DefaultDocumentation.Api/Extensions/IWriterExtension.cs +++ b/source/DefaultDocumentation.Api/Extensions/IWriterExtensions.cs @@ -6,7 +6,7 @@ namespace DefaultDocumentation.Api /// /// Provides extension methods on the type. /// - public static class IWriterExtension + public static class IWriterExtensions { /// /// Appends an to a by using the of . @@ -17,6 +17,8 @@ public static class IWriterExtension /// The given . public static IWriter Append(this IWriter writer, XElement? value) { + ArgumentNullException.ThrowIfNull(writer); + static void AppendMultiline(IWriter writer, string text, ref int? textStartIndex, ref bool startingNewLine) { string[] lines = text.Split('\n'); @@ -91,7 +93,13 @@ static void AppendMultiline(IWriter writer, string text, ref int? textStartIndex /// The to append to. /// The to append before the line. /// The given . - public static IWriter AppendLine(this IWriter writer, string value) => writer.Append(value).AppendLine(); + public static IWriter AppendLine(this IWriter writer, string value) + { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(value); + + return writer.Append(value).AppendLine(); + } /// /// Trims from the end of a all the provided values. @@ -101,7 +109,10 @@ static void AppendMultiline(IWriter writer, string text, ref int? textStartIndex /// The given . public static IWriter TrimEnd(this IWriter writer, params string[] values) { - Start: + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(values); + +Start: foreach (string value in values) { if (!string.IsNullOrEmpty(value) && writer.EndsWith(value)) diff --git a/source/DefaultDocumentation.Api/IContext.cs b/source/DefaultDocumentation.Api/IContext.cs index 5320d08e..043e70bc 100644 --- a/source/DefaultDocumentation.Api/IContext.cs +++ b/source/DefaultDocumentation.Api/IContext.cs @@ -1,22 +1,23 @@ using System.Collections.Generic; using DefaultDocumentation.Api; +using DefaultDocumentation.Models; namespace DefaultDocumentation { /// - /// Exposes settings used to generate documentation. + /// Exposes settings used to generate documentation for a given type. /// public interface IContext { /// /// Gets the to use to generate a file for a documentation page. /// - IFileNameFactory FileNameFactory { get; } + IFileNameFactory? FileNameFactory { get; } /// /// Gets the to use to generate a documentation page. /// - IEnumerable Sections { get; } + IEnumerable? Sections { get; } /// /// Gets a setting with the given name. diff --git a/source/DefaultDocumentation.Api/IGeneralContext.cs b/source/DefaultDocumentation.Api/IGeneralContext.cs index 85450079..dd7337df 100644 --- a/source/DefaultDocumentation.Api/IGeneralContext.cs +++ b/source/DefaultDocumentation.Api/IGeneralContext.cs @@ -25,12 +25,17 @@ public interface IGeneralContext : IContext /// IReadOnlyDictionary Elements { get; } + /// + /// Gets the used to create the documentation urls. + /// + IEnumerable UrlFactories { get; } + /// /// Gets the specific for the given . /// /// The for which to get the specific . /// The specific to the provided . - IContext GetContext(Type type); + IContext GetContext(Type? type); /// /// Gets the file name for the given . @@ -38,12 +43,5 @@ public interface IGeneralContext : IContext /// The for which to get the page name. /// The file name of the documentation page of the given . string GetFileName(DocItem item); - - /// - /// Gets the url of the given id. - /// - /// The id to get the url for. - /// The url of the given id. - string GetUrl(string id); } } diff --git a/source/DefaultDocumentation.Api/IPageContext.cs b/source/DefaultDocumentation.Api/IPageContext.cs new file mode 100644 index 00000000..08d5ac38 --- /dev/null +++ b/source/DefaultDocumentation.Api/IPageContext.cs @@ -0,0 +1,22 @@ +using DefaultDocumentation.Models; + +namespace DefaultDocumentation +{ + /// + /// Exposes settings used to generate documentation for a specific . + /// + public interface IPageContext : IGeneralContext + { + /// + /// Gets the for which the documentation is being generated. + /// + DocItem DocItem { get; } + + /// + /// Gets or sets extra data for the current documentation generation. + /// + /// The key of the data. + /// The value of the data. + object? this[string key] { get; set; } + } +} diff --git a/source/DefaultDocumentation.Api/ISettings.cs b/source/DefaultDocumentation.Api/ISettings.cs index 52cfc7f3..4522c5cb 100644 --- a/source/DefaultDocumentation.Api/ISettings.cs +++ b/source/DefaultDocumentation.Api/ISettings.cs @@ -62,7 +62,7 @@ public interface ISettings /// /// Gets the base url to prefix item url with when generating the links output file. /// - public string? LinksBaseUrl { get; } + public string LinksBaseUrl { get; } /// /// Gets the links files of external items which are not part of the dotnet api. diff --git a/source/DefaultDocumentation.Api/Internal/Extensions/XElementExtension.cs b/source/DefaultDocumentation.Api/Internal/Extensions/XElementExtensions.cs similarity index 91% rename from source/DefaultDocumentation.Api/Internal/Extensions/XElementExtension.cs rename to source/DefaultDocumentation.Api/Internal/Extensions/XElementExtensions.cs index 4ea2f533..01596486 100644 --- a/source/DefaultDocumentation.Api/Internal/Extensions/XElementExtension.cs +++ b/source/DefaultDocumentation.Api/Internal/Extensions/XElementExtensions.cs @@ -3,7 +3,7 @@ namespace System.Xml.Linq { - internal static class XElementExtension + internal static class XElementExtensions { public static IEnumerable GetTypeParameters(this XElement? element) => element?.Elements("typeparam") ?? Enumerable.Empty(); diff --git a/source/DefaultDocumentation.Api/Internal/MissingAPIs/ArgumentNullException.cs b/source/DefaultDocumentation.Api/Internal/MissingAPIs/ArgumentNullException.cs new file mode 100644 index 00000000..fbbca050 --- /dev/null +++ b/source/DefaultDocumentation.Api/Internal/MissingAPIs/ArgumentNullException.cs @@ -0,0 +1,20 @@ +#if !NET5_0_OR_GREATER + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace DefaultDocumentation +{ + internal static class ArgumentNullException + { + public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null) + { + if (argument is null) + { + throw new System.ArgumentNullException(paramName); + } + } + } +} + +#endif diff --git a/source/DefaultDocumentation.Api/Internal/MissingAPIs/CallerArgumentExpressionAttribute.cs b/source/DefaultDocumentation.Api/Internal/MissingAPIs/CallerArgumentExpressionAttribute.cs new file mode 100644 index 00000000..6f0aceff --- /dev/null +++ b/source/DefaultDocumentation.Api/Internal/MissingAPIs/CallerArgumentExpressionAttribute.cs @@ -0,0 +1,17 @@ +#if !NET5_0_OR_GREATER + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] + internal sealed class CallerArgumentExpressionAttribute : Attribute + { + public string ParameterName { get; } + + public CallerArgumentExpressionAttribute(string parameterName) + { + ParameterName = parameterName; + } + } +} + +#endif diff --git a/source/DefaultDocumentation.Api/Internal/MissingAPIs/NullableAttributes.cs b/source/DefaultDocumentation.Api/Internal/MissingAPIs/NullableAttributes.cs new file mode 100644 index 00000000..e274b733 --- /dev/null +++ b/source/DefaultDocumentation.Api/Internal/MissingAPIs/NullableAttributes.cs @@ -0,0 +1,90 @@ +#if !NETSTANDARD2_1_OR_GREATER && !NET5_0_OR_GREATER + +namespace System.Diagnostics.CodeAnalysis +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class AllowNullAttribute : Attribute; + + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class DisallowNullAttribute : Attribute; + + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class MaybeNullAttribute : Attribute; + + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class NotNullAttribute : Attribute; + + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class MaybeNullWhenAttribute : Attribute + { + public bool ReturnValue { get; } + + public MaybeNullWhenAttribute(bool returnValue) + { + ReturnValue = returnValue; + } + } + + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class NotNullWhenAttribute : Attribute + { + public bool ReturnValue { get; } + + public NotNullWhenAttribute(bool returnValue) + { + ReturnValue = returnValue; + } + } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] + internal sealed class NotNullIfNotNullAttribute : Attribute + { + public string ParameterName { get; } + + public NotNullIfNotNullAttribute(string parameterName) + { + ParameterName = parameterName; + } + } + + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class DoesNotReturnAttribute : Attribute; + + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class DoesNotReturnIfAttribute : Attribute + { + public bool ParameterValue { get; } + + public DoesNotReturnIfAttribute(bool parameterValue) + { + ParameterValue = parameterValue; + } + } + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullAttribute : Attribute + { + public string[] Members { get; } + + public MemberNotNullAttribute(params string[] members) + { + Members = members; + } + } + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullWhenAttribute : Attribute + { + public bool ReturnValue { get; } + + public string[] Members { get; } + + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) + { + ReturnValue = returnValue; + Members = members; + } + } +} + +#endif diff --git a/source/DefaultDocumentation.Api/Internal/NullableAttributes.cs b/source/DefaultDocumentation.Api/Internal/NullableAttributes.cs deleted file mode 100644 index 63135907..00000000 --- a/source/DefaultDocumentation.Api/Internal/NullableAttributes.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace System.Diagnostics.CodeAnalysis -{ - /// Specifies that null is allowed as an input even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - internal sealed class AllowNullAttribute : Attribute - { } - - /// Specifies that an output may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - internal sealed class MaybeNullAttribute : Attribute - { } - - /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class NotNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - public NotNullWhenAttribute(bool returnValue) - { - ReturnValue = returnValue; - } - - /// Gets the return value condition. - public bool ReturnValue { get; } - } -} \ No newline at end of file diff --git a/source/DefaultDocumentation.Api/Models/DocItem.cs b/source/DefaultDocumentation.Api/Models/DocItem.cs index 38a07ad5..6a0ff0be 100644 --- a/source/DefaultDocumentation.Api/Models/DocItem.cs +++ b/source/DefaultDocumentation.Api/Models/DocItem.cs @@ -1,5 +1,4 @@ -using System; -using System.Xml.Linq; +using System.Xml.Linq; namespace DefaultDocumentation.Models { @@ -35,10 +34,14 @@ public abstract class DocItem private protected DocItem(DocItem? parent, string id, string fullName, string name, XElement? documentation) { + ArgumentNullException.ThrowIfNull(id); + ArgumentNullException.ThrowIfNull(fullName); + ArgumentNullException.ThrowIfNull(name); + Parent = parent; - Id = id ?? throw new ArgumentNullException(nameof(id)); - FullName = fullName ?? throw new ArgumentNullException(nameof(fullName)); - Name = name ?? throw new ArgumentNullException(nameof(name)); + Id = id; + FullName = fullName; + Name = name; Documentation = documentation; } } diff --git a/source/DefaultDocumentation.Api/Models/EntityDocItem.cs b/source/DefaultDocumentation.Api/Models/EntityDocItem.cs index 84861027..9f53176d 100644 --- a/source/DefaultDocumentation.Api/Models/EntityDocItem.cs +++ b/source/DefaultDocumentation.Api/Models/EntityDocItem.cs @@ -35,14 +35,14 @@ public abstract class EntityDocItem : DocItem public IEntity Entity { get; } private protected EntityDocItem(DocItem parent, IEntity entity, XElement? documentation) - : base(parent, entity.GetIdString(), GetFullName(entity).Replace("?", string.Empty), _nameAmbience.ConvertSymbol(entity).Replace("?", string.Empty), documentation) + : base(parent, entity.GetIdString(), GetFullName(entity).Replace("?", string.Empty), entity.ToString(_nameAmbience).Replace("?", string.Empty), documentation) { Entity = entity; } private static string GetFullName(IEntity entity) { - string fullName = _fullNameAmbience.ConvertSymbol(entity); + string fullName = entity.ToString(_fullNameAmbience); if (entity.SymbolKind == SymbolKind.Operator) { diff --git a/source/DefaultDocumentation.Api/Models/ExternDocItem.cs b/source/DefaultDocumentation.Api/Models/ExternDocItem.cs index cce432e0..2b4fbe7f 100644 --- a/source/DefaultDocumentation.Api/Models/ExternDocItem.cs +++ b/source/DefaultDocumentation.Api/Models/ExternDocItem.cs @@ -1,6 +1,4 @@ -using System; - -namespace DefaultDocumentation.Models +namespace DefaultDocumentation.Models { /// /// Represent an external documentation. @@ -18,10 +16,17 @@ public sealed class ExternDocItem : DocItem /// The id of the external item. /// The url of the documentation. /// The name of the external item. - public ExternDocItem(string id, string url, string name) - : base(null, id, id.Substring(2), name ?? id.Substring(2), null) + public ExternDocItem(string id, string url, string? name) + : base( + null, + id, + (id ?? throw new System.ArgumentNullException(nameof(id))).Substring(2), + name ?? id.Substring(2), + null) { - Url = url ?? throw new ArgumentNullException(nameof(url)); + ArgumentNullException.ThrowIfNull(url); + + Url = url; } } } diff --git a/source/DefaultDocumentation.Api/Models/Members/ConstructorDocItem.cs b/source/DefaultDocumentation.Api/Models/Members/ConstructorDocItem.cs index 464b0995..4c21c16e 100644 --- a/source/DefaultDocumentation.Api/Models/Members/ConstructorDocItem.cs +++ b/source/DefaultDocumentation.Api/Models/Members/ConstructorDocItem.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using DefaultDocumentation.Models.Parameters; @@ -30,8 +29,8 @@ public sealed class ConstructorDocItem : EntityDocItem, IParameterizedDocItem /// or is null. public ConstructorDocItem(TypeDocItem parent, IMethod method, XElement? documentation) : base( - parent ?? throw new ArgumentNullException(nameof(parent)), - method ?? throw new ArgumentNullException(nameof(method)), + parent ?? throw new System.ArgumentNullException(nameof(parent)), + method ?? throw new System.ArgumentNullException(nameof(method)), documentation) { Method = method; diff --git a/source/DefaultDocumentation.Api/Models/Members/EnumFieldDocItem.cs b/source/DefaultDocumentation.Api/Models/Members/EnumFieldDocItem.cs index 48f3ec65..f1f69509 100644 --- a/source/DefaultDocumentation.Api/Models/Members/EnumFieldDocItem.cs +++ b/source/DefaultDocumentation.Api/Models/Members/EnumFieldDocItem.cs @@ -1,5 +1,4 @@ -using System; -using System.Xml.Linq; +using System.Xml.Linq; using DefaultDocumentation.Models.Types; using ICSharpCode.Decompiler.TypeSystem; @@ -23,8 +22,8 @@ public sealed class EnumFieldDocItem : EntityDocItem /// The documentation element of the enum field. public EnumFieldDocItem(EnumDocItem parent, IField field, XElement? documentation) : base( - parent ?? throw new ArgumentNullException(nameof(parent)), - field ?? throw new ArgumentNullException(nameof(field)), + parent ?? throw new System.ArgumentNullException(nameof(parent)), + field ?? throw new System.ArgumentNullException(nameof(field)), documentation) { Field = field; diff --git a/source/DefaultDocumentation.Api/Models/Members/EventDocItem.cs b/source/DefaultDocumentation.Api/Models/Members/EventDocItem.cs index 383a722c..77c47858 100644 --- a/source/DefaultDocumentation.Api/Models/Members/EventDocItem.cs +++ b/source/DefaultDocumentation.Api/Models/Members/EventDocItem.cs @@ -1,5 +1,4 @@ -using System; -using System.Xml.Linq; +using System.Xml.Linq; using DefaultDocumentation.Models.Types; using ICSharpCode.Decompiler.TypeSystem; @@ -23,8 +22,8 @@ public sealed class EventDocItem : EntityDocItem /// The documentation element of the event. public EventDocItem(TypeDocItem parent, IEvent @event, XElement? documentation) : base( - parent ?? throw new ArgumentNullException(nameof(parent)), - @event ?? throw new ArgumentNullException(nameof(@event)), + parent ?? throw new System.ArgumentNullException(nameof(parent)), + @event ?? throw new System.ArgumentNullException(nameof(@event)), documentation) { Event = @event; diff --git a/source/DefaultDocumentation.Api/Models/Members/ExplicitInterfaceImplementationDocItem.cs b/source/DefaultDocumentation.Api/Models/Members/ExplicitInterfaceImplementationDocItem.cs index 80e40b2e..b67aa9fe 100644 --- a/source/DefaultDocumentation.Api/Models/Members/ExplicitInterfaceImplementationDocItem.cs +++ b/source/DefaultDocumentation.Api/Models/Members/ExplicitInterfaceImplementationDocItem.cs @@ -33,8 +33,8 @@ public sealed class ExplicitInterfaceImplementationDocItem : EntityDocItem, ITyp /// The documentation element of the interface event explicitly implemented. public ExplicitInterfaceImplementationDocItem(TypeDocItem parent, IEvent @event, XElement? documentation) : base( - parent ?? throw new ArgumentNullException(nameof(parent)), - @event ?? throw new ArgumentNullException(nameof(@event)), + parent ?? throw new System.ArgumentNullException(nameof(parent)), + @event ?? throw new System.ArgumentNullException(nameof(@event)), documentation) { Member = @event; @@ -50,8 +50,8 @@ public ExplicitInterfaceImplementationDocItem(TypeDocItem parent, IEvent @event, /// The documentation element of the interface property explicitly implemented. public ExplicitInterfaceImplementationDocItem(TypeDocItem parent, IProperty property, XElement? documentation) : base( - parent ?? throw new ArgumentNullException(nameof(parent)), - property ?? throw new ArgumentNullException(nameof(property)), + parent ?? throw new System.ArgumentNullException(nameof(parent)), + property ?? throw new System.ArgumentNullException(nameof(property)), documentation) { Member = property; @@ -67,8 +67,8 @@ public ExplicitInterfaceImplementationDocItem(TypeDocItem parent, IProperty prop /// The documentation element of the interface method explicitly implemented. public ExplicitInterfaceImplementationDocItem(TypeDocItem parent, IMethod method, XElement? documentation) : base( - parent ?? throw new ArgumentNullException(nameof(parent)), - method ?? throw new ArgumentNullException(nameof(method)), + parent ?? throw new System.ArgumentNullException(nameof(parent)), + method ?? throw new System.ArgumentNullException(nameof(method)), documentation) { Member = method; diff --git a/source/DefaultDocumentation.Api/Models/Members/FieldDocItem.cs b/source/DefaultDocumentation.Api/Models/Members/FieldDocItem.cs index c8fa25d3..b44186a1 100644 --- a/source/DefaultDocumentation.Api/Models/Members/FieldDocItem.cs +++ b/source/DefaultDocumentation.Api/Models/Members/FieldDocItem.cs @@ -23,8 +23,8 @@ public sealed class FieldDocItem : EntityDocItem /// The documentation element of the field. public FieldDocItem(TypeDocItem parent, IField field, XElement? documentation) : base( - parent ?? throw new ArgumentNullException(nameof(parent)), - field ?? throw new ArgumentNullException(nameof(field)), + parent ?? throw new System.ArgumentNullException(nameof(parent)), + field ?? throw new System.ArgumentNullException(nameof(field)), documentation) { Field = field; diff --git a/source/DefaultDocumentation.Api/Models/Members/MethodDocItem.cs b/source/DefaultDocumentation.Api/Models/Members/MethodDocItem.cs index a2643f4a..db81d66b 100644 --- a/source/DefaultDocumentation.Api/Models/Members/MethodDocItem.cs +++ b/source/DefaultDocumentation.Api/Models/Members/MethodDocItem.cs @@ -32,8 +32,8 @@ public sealed class MethodDocItem : EntityDocItem, ITypeParameterizedDocItem, IP /// The documentation element of the method. public MethodDocItem(TypeDocItem parent, IMethod method, XElement? documentation) : base( - parent ?? throw new ArgumentNullException(nameof(parent)), - method ?? throw new ArgumentNullException(nameof(method)), + parent ?? throw new System.ArgumentNullException(nameof(parent)), + method ?? throw new System.ArgumentNullException(nameof(method)), documentation) { Method = method; diff --git a/source/DefaultDocumentation.Api/Models/Members/OperatorDocItem.cs b/source/DefaultDocumentation.Api/Models/Members/OperatorDocItem.cs index 27bc52b4..57733ace 100644 --- a/source/DefaultDocumentation.Api/Models/Members/OperatorDocItem.cs +++ b/source/DefaultDocumentation.Api/Models/Members/OperatorDocItem.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using DefaultDocumentation.Models.Parameters; @@ -29,8 +28,8 @@ public sealed class OperatorDocItem : EntityDocItem, IParameterizedDocItem /// The documentation element of the operator. public OperatorDocItem(TypeDocItem parent, IMethod method, XElement? documentation) : base( - parent ?? throw new ArgumentNullException(nameof(parent)), - method ?? throw new ArgumentNullException(nameof(method)), + parent ?? throw new System.ArgumentNullException(nameof(parent)), + method ?? throw new System.ArgumentNullException(nameof(method)), documentation) { Method = method; diff --git a/source/DefaultDocumentation.Api/Models/Members/PropertyDocItem.cs b/source/DefaultDocumentation.Api/Models/Members/PropertyDocItem.cs index 78f567c6..908b3381 100644 --- a/source/DefaultDocumentation.Api/Models/Members/PropertyDocItem.cs +++ b/source/DefaultDocumentation.Api/Models/Members/PropertyDocItem.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using DefaultDocumentation.Models.Parameters; @@ -29,8 +28,8 @@ public sealed class PropertyDocItem : EntityDocItem, IParameterizedDocItem /// The documentation element of the property. public PropertyDocItem(TypeDocItem parent, IProperty property, XElement? documentation) : base( - parent ?? throw new ArgumentNullException(nameof(parent)), - property ?? throw new ArgumentNullException(nameof(property)), + parent ?? throw new System.ArgumentNullException(nameof(parent)), + property ?? throw new System.ArgumentNullException(nameof(property)), documentation) { Property = property; diff --git a/source/DefaultDocumentation.Api/Models/NamespaceDocItem.cs b/source/DefaultDocumentation.Api/Models/NamespaceDocItem.cs index d3cb880c..d6f55408 100644 --- a/source/DefaultDocumentation.Api/Models/NamespaceDocItem.cs +++ b/source/DefaultDocumentation.Api/Models/NamespaceDocItem.cs @@ -1,5 +1,4 @@ -using System; -using System.Xml.Linq; +using System.Xml.Linq; namespace DefaultDocumentation.Models { @@ -15,7 +14,7 @@ public sealed class NamespaceDocItem : DocItem /// The name of the namespace. /// The documentation element of the namespace. public NamespaceDocItem(AssemblyDocItem parent, string name, XElement? documentation) - : base(parent ?? throw new ArgumentNullException(nameof(parent)), $"N:{name}", name, name, documentation) + : base(parent ?? throw new System.ArgumentNullException(nameof(parent)), $"N:{name}", name, name, documentation) { } } } diff --git a/source/DefaultDocumentation.Api/Models/Types/TypeDocItem.cs b/source/DefaultDocumentation.Api/Models/Types/TypeDocItem.cs index 88f34326..b881c1f8 100644 --- a/source/DefaultDocumentation.Api/Models/Types/TypeDocItem.cs +++ b/source/DefaultDocumentation.Api/Models/Types/TypeDocItem.cs @@ -23,7 +23,7 @@ public abstract class TypeDocItem : EntityDocItem, ITypeParameterizedDocItem private protected TypeDocItem(DocItem parent, ITypeDefinition type, XElement? documentation) : base( parent is NamespaceDocItem or TypeDocItem ? parent : throw new ArgumentException($"must be either {nameof(NamespaceDocItem)} or {nameof(TypeDocItem)}", nameof(parent)), - type ?? throw new ArgumentNullException(nameof(type)), + type ?? throw new System.ArgumentNullException(nameof(type)), documentation) { Type = type; diff --git a/source/DefaultDocumentation.Common/DefaultDocumentation.Common.csproj b/source/DefaultDocumentation.Common/DefaultDocumentation.Common.csproj index a20236bd..59e448af 100644 --- a/source/DefaultDocumentation.Common/DefaultDocumentation.Common.csproj +++ b/source/DefaultDocumentation.Common/DefaultDocumentation.Common.csproj @@ -1,11 +1,18 @@  - - DefaultDocumentation - netstandard2.0 - false - - - - - + + + DefaultDocumentation + netstandard2.0 + false + enable + + + + + + + + + + \ No newline at end of file diff --git a/source/DefaultDocumentation.Common/Generator.cs b/source/DefaultDocumentation.Common/Generator.cs index b0543ff9..f22c6245 100644 --- a/source/DefaultDocumentation.Common/Generator.cs +++ b/source/DefaultDocumentation.Common/Generator.cs @@ -22,8 +22,9 @@ public sealed class Generator { private static readonly JsonSerializer _serializer; + private readonly IRawSettings _settings; private readonly JObject _configuration; - private readonly ILogger _logger; + private readonly Logger _logger; private readonly GeneralContext _context; static Generator() @@ -34,20 +35,9 @@ static Generator() private Generator(Target loggerTarget, IRawSettings settings) { - T GetSetting(string name) => _configuration.TryGetValue(name, StringComparison.OrdinalIgnoreCase, out JToken value) ? value.ToObject() : default; - - void AddSetting(Expression> property, Predicate noValue, Func convert) - { - string name = ((MemberExpression)property.Body).Member.Name; - TSetting value = property.Compile().Invoke(settings); - - if (!noValue(value)) - { - _configuration.Property(name, StringComparison.OrdinalIgnoreCase)?.Remove(); - _configuration.Add(name, JToken.FromObject(convert(value), _serializer)); - } - } + T? GetSetting(string name) => _configuration.TryGetValue(name, StringComparison.OrdinalIgnoreCase, out JToken? value) ? value.ToObject() : default; + _settings = settings; _configuration = new JObject(); if (File.Exists(settings.ConfigurationFilePath)) @@ -56,45 +46,30 @@ void AddSetting(Expression> prop Environment.CurrentDirectory = Path.GetDirectoryName(settings.ConfigurationFilePath); } - AddSetting(s => s.LogLevel, string.IsNullOrEmpty, v => v); - AddSetting(s => s.AssemblyFilePath, string.IsNullOrEmpty, v => v); - AddSetting(s => s.DocumentationFilePath, string.IsNullOrEmpty, v => v); - AddSetting(s => s.ProjectDirectoryPath, string.IsNullOrEmpty, v => v); - AddSetting(s => s.OutputDirectoryPath, string.IsNullOrEmpty, v => v); - AddSetting(s => s.AssemblyPageName, string.IsNullOrEmpty, v => v); - AddSetting(s => s.GeneratedAccessModifiers, v => v == GeneratedAccessModifiers.Default, v => v); - AddSetting(s => s.IncludeUndocumentedItems, v => !v, v => v); - AddSetting(s => s.GeneratedPages, v => v == GeneratedPages.Default, v => v); - AddSetting(s => s.LinksOutputFilePath, string.IsNullOrEmpty, v => v); - AddSetting(s => s.LinksBaseUrl, string.IsNullOrEmpty, v => v); + AddSetting(s => s.LogLevel, string.IsNullOrEmpty); + AddSetting(s => s.AssemblyFilePath, string.IsNullOrEmpty); + AddSetting(s => s.DocumentationFilePath, string.IsNullOrEmpty); + AddSetting(s => s.ProjectDirectoryPath, string.IsNullOrEmpty); + AddSetting(s => s.OutputDirectoryPath, string.IsNullOrEmpty); + AddSetting(s => s.AssemblyPageName, string.IsNullOrEmpty); + AddSetting(s => s.GeneratedAccessModifiers, v => v == GeneratedAccessModifiers.Default); + AddSetting(s => s.IncludeUndocumentedItems, v => !v); + AddSetting(s => s.GeneratedPages, v => v == GeneratedPages.Default); + AddSetting(s => s.LinksOutputFilePath, string.IsNullOrEmpty); + AddSetting(s => s.LinksBaseUrl, string.IsNullOrEmpty); AddSetting(s => s.ExternLinksFilePaths, v => !(v ?? Enumerable.Empty()).Any(), v => v.ToArray()); // context settings AddSetting(s => s.Plugins, v => !(v ?? Enumerable.Empty()).Any(), v => v.ToArray()); - AddSetting(s => s.FileNameFactory, string.IsNullOrEmpty, v => v); - if (string.IsNullOrEmpty(GetSetting(nameof(settings.FileNameFactory)))) - { - AddSetting(s => s.FileNameFactory, _ => false, _ => "FullName"); - } - AddSetting(s => s.UrlFactories, v => !(v ?? Enumerable.Empty()).Any(), v => v.ToArray()); - if (GetSetting(nameof(settings.UrlFactories)) is null) - { - AddSetting(s => s.UrlFactories, _ => false, _ => new[] { "DocItem", "DotnetApi" }); - } - AddSetting(s => s.Sections, v => !(v ?? Enumerable.Empty()).Any(), v => v.ToArray()); - if (GetSetting(nameof(settings.Sections)) is null) - { - AddSetting(s => s.Sections, _ => false, _ => new[] { "Header", "Default" }); - } + AddSetting(s => s.FileNameFactory, string.IsNullOrEmpty, "FullName"); + AddSetting(s => s.UrlFactories, v => !(v ?? Enumerable.Empty()).Any(), v => v.ToArray(), ["DocItem", "DotnetApi"]); + AddSetting(s => s.Sections, v => !(v ?? Enumerable.Empty()).Any(), v => v.ToArray(), ["Header", "Default"]); AddSetting(s => s.Elements, v => !(v ?? Enumerable.Empty()).Any(), v => v.ToArray()); - string logLevel = GetSetting(nameof(logLevel)); - if (loggerTarget != null) - { - LoggingConfiguration logConfiguration = new(); - logConfiguration.AddTarget(loggerTarget); - logConfiguration.AddRule(LogLevel.FromString(string.IsNullOrEmpty(logLevel) ? nameof(LogLevel.Info) : logLevel), LogLevel.Fatal, loggerTarget); - LogManager.Configuration = logConfiguration; - } + string? logLevel = GetSetting(nameof(logLevel)); + LoggingConfiguration logConfiguration = new(); + logConfiguration.AddTarget(loggerTarget); + logConfiguration.AddRule(LogLevel.FromString(string.IsNullOrEmpty(logLevel) ? nameof(LogLevel.Info) : logLevel), LogLevel.Fatal, loggerTarget); + LogManager.Configuration = logConfiguration; _logger = LogManager.GetLogger("DefaultDocumentation"); _logger.Info($"Starting DefaultDocumentation with this configuration:{Environment.NewLine}{_configuration.ToString(Formatting.Indented)}"); @@ -123,14 +98,41 @@ void AddSetting(Expression> prop DocItemReader.GetItems(resolvedSettings)); } + private void AddSetting( + Expression> property, + Predicate noValuePredicate, + Func convert, + TConfig? defaultValue = default) + { + string name = ((MemberExpression)property.Body).Member.Name; + TSetting value = property.Compile().Invoke(_settings); + + if (!noValuePredicate(value)) + { + _configuration.Property(name, StringComparison.OrdinalIgnoreCase)?.Remove(); + _configuration.Add(name, JToken.FromObject(convert(value)!, _serializer)); + } + else if (!Equals(defaultValue, default(TConfig))) + { + _configuration.Property(name, StringComparison.OrdinalIgnoreCase)?.Remove(); + _configuration.Add(name, JToken.FromObject(defaultValue!, _serializer)); + } + } + + private void AddSetting( + Expression> property, + Predicate noValuePredicate, + TSetting? defaultValue = default) + => AddSetting(property, noValuePredicate, v => v, defaultValue); + private void WritePage(DocItem item, StringBuilder builder) { _context.Settings.Logger.Debug($"Writing DocItem \"{item}\" with id \"{item.Id}\""); builder.Clear(); - PageWriter writer = new(builder, _context, item); + PageWriter writer = new(builder, new PageContext(_context, item)); - foreach (ISection sectionWriter in writer.Context.GetSetting(item, c => c.Sections)) + foreach (ISection sectionWriter in writer.Context.GetSetting(item, c => c.Sections) ?? Array.Empty()) { sectionWriter.Write(writer); } @@ -152,12 +154,14 @@ private void WriteLinks() using StreamWriter writer = _context.Settings.LinksOutputFile.CreateText(); + PageContext context = new(_context, new ExternDocItem("", "", "")); + writer.WriteLine(_context.Settings.LinksBaseUrl); foreach (DocItem item in _context.Items.Values.Where(i => i is not ExternDocItem and not AssemblyDocItem and not TypeParameterDocItem and not ParameterDocItem)) { writer.Write(item.Id); writer.Write('|'); - writer.Write(_context.GetUrl(item)); + writer.Write(context.GetUrl(item)); writer.Write('|'); writer.WriteLine(item.Name); } @@ -192,6 +196,9 @@ private void Execute() public static void Execute(Target loggerTarget, IRawSettings settings) { + ArgumentNullException.ThrowIfNull(loggerTarget); + ArgumentNullException.ThrowIfNull(settings); + Generator generator = new(loggerTarget, settings); using Mutex mutex = new(false, "DefaultDocumenation:" + generator._context.Settings.OutputDirectory.FullName.Replace('\\', '|').Replace('/', '|').TrimEnd('|')); diff --git a/source/DefaultDocumentation.Common/Internal/Context.cs b/source/DefaultDocumentation.Common/Internal/Context.cs index 0b601b93..8d31475f 100644 --- a/source/DefaultDocumentation.Common/Internal/Context.cs +++ b/source/DefaultDocumentation.Common/Internal/Context.cs @@ -16,14 +16,15 @@ public Context( { _configuration = configuration; - string fileNameFactory = GetSetting(nameof(IRawSettings.FileNameFactory)); + string? fileNameFactory = GetSetting(nameof(IRawSettings.FileNameFactory)); FileNameFactory = string.IsNullOrEmpty(fileNameFactory) ? null : availableTypes .Where(t => typeof(IFileNameFactory).IsAssignableFrom(t) && !t.IsAbstract) .Select(t => (IFileNameFactory)Activator.CreateInstance(t)) .LastOrDefault(f => f.Name == fileNameFactory || $"{f.GetType().FullName} {f.GetType().Assembly.GetName().Name}" == fileNameFactory) ?? throw new Exception($"FileNameFactory '{fileNameFactory}' not found"); - string[] sections = GetSetting(nameof(IRawSettings.Sections)); + string[]? sections = GetSetting(nameof(IRawSettings.Sections)); + if (sections != null) { Dictionary availableSections = availableTypes @@ -47,11 +48,11 @@ public Context( #region IContext - public IFileNameFactory FileNameFactory { get; } + public IFileNameFactory? FileNameFactory { get; } - public IEnumerable Sections { get; } + public IEnumerable? Sections { get; } - public T GetSetting(string name) => _configuration.TryGetValue(name, StringComparison.OrdinalIgnoreCase, out JToken value) ? value.ToObject() : default; + public T? GetSetting(string name) => _configuration.TryGetValue(name, StringComparison.OrdinalIgnoreCase, out JToken? value) ? value.ToObject() : default; #endregion } diff --git a/source/DefaultDocumentation.Common/Internal/DocItemReader.cs b/source/DefaultDocumentation.Common/Internal/DocItemReader.cs index f8f8a79a..e1773cc7 100644 --- a/source/DefaultDocumentation.Common/Internal/DocItemReader.cs +++ b/source/DefaultDocumentation.Common/Internal/DocItemReader.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Xml.Linq; @@ -26,7 +27,7 @@ internal sealed class DocItemReader private DocItemReader(Settings settings) { - bool IsGenerated(IEntity entity) => entity.EffectiveAccessibility() switch + bool IsGenerated(IEntity? entity) => entity.EffectiveAccessibility() switch { Accessibility.Public => (settings.GeneratedAccessModifiers & GeneratedAccessModifiers.Public) != 0, Accessibility.Private => entity switch @@ -83,7 +84,7 @@ private DocItemReader(Settings settings) List declaringTypes = new(type.GetDeclaringTypeDefinitions().Skip(1).Reverse()); List docItemsToAdd = new(); - if (!_items.TryGetValue($"N:{(declaringTypes.FirstOrDefault() ?? type).Namespace}", out DocItem parentDocItem)) + if (!_items.TryGetValue($"N:{(declaringTypes.FirstOrDefault() ?? type).Namespace}", out DocItem? parentDocItem)) { parentDocItem = new NamespaceDocItem( assemblyDocItem, @@ -160,7 +161,7 @@ private DocItemReader(Settings settings) continue; } - if (!TryGetDocumentation(entity, out XElement documentation) && !settings.IncludeUndocumentedItems) + if (!TryGetDocumentation(entity, out XElement? documentation) && !settings.IncludeUndocumentedItems) { _logger.Debug($"Skipping documentation for member \"{entity.FullName}\": no documentation"); continue; @@ -217,7 +218,7 @@ private DocItemReader(Settings settings) string baseLink = string.Empty; while (!reader.EndOfStream) { - string[] items = reader.ReadLine().Split(new[] { '|' }, 3); + string[] items = reader.ReadLine().Split(['|'], 3); switch (items.Length) { @@ -239,7 +240,7 @@ private DocItemReader(Settings settings) private TypeDocItem GetDocItem(ITypeDefinition type, DocItem parentDocItem) { - TryGetDocumentation(type, out XElement documentation); + TryGetDocumentation(type, out XElement? documentation); return type.Kind switch { @@ -252,14 +253,14 @@ private TypeDocItem GetDocItem(ITypeDefinition type, DocItem parentDocItem) }; } - private bool TryGetDocumentation(IEntity entity, out XElement documentation, HashSet referencedIds = null) + private bool TryGetDocumentation(IEntity? entity, [NotNullWhen(true)] out XElement? documentation, HashSet? referencedIds = null) { - static XElement ConvertToDocumentation(string documentationString) => documentationString is null ? null : XElement.Parse($"{documentationString}"); + static XElement? ConvertToDocumentation(string? documentationString) => documentationString is null ? null : XElement.Parse($"{documentationString}"); referencedIds ??= new HashSet(); _logger.Trace($"looking for documentation of \"{entity?.FullName}\""); - if (entity is null) + if (entity?.ParentModule?.PEFile is null) { documentation = null; return false; @@ -274,14 +275,14 @@ private bool TryGetDocumentation(IEntity entity, out XElement documentation, Has documentation = ConvertToDocumentation(documentationProvider?.GetDocumentation(entity)); - if (documentation.HasInheritDoc(out XElement inheritDoc)) + if (documentation.HasInheritDoc(out XElement? inheritDoc)) { - string referenceName = inheritDoc.GetCRefAttribute(); + string? referenceName = inheritDoc.GetCRefAttribute(); if (referenceName is null) { _logger.Trace($"looking for inherited documentation of \"{entity.FullName}\""); - XElement baseDocumentation = null; + XElement? baseDocumentation = null; if (entity is ITypeDefinition type) { _ = type @@ -326,10 +327,10 @@ private bool TryGetDocumentation(IEntity entity, out XElement documentation, Has return documentation != null; } - private XElement GetDocumentation(string id) + private XElement? GetDocumentation(string id) { _logger.Trace($"looking for documentation of \"{id}\""); - return TryGetDocumentation(IdStringProvider.FindEntity(id, _resolver), out XElement documentation) ? documentation : null; + return TryGetDocumentation(IdStringProvider.FindEntity(id, _resolver), out XElement? documentation) ? documentation : null; } private void Add(DocItem item) diff --git a/source/DefaultDocumentation.Common/Internal/Extensions/IEntityExtension.cs b/source/DefaultDocumentation.Common/Internal/Extensions/IEntityExtensions.cs similarity index 56% rename from source/DefaultDocumentation.Common/Internal/Extensions/IEntityExtension.cs rename to source/DefaultDocumentation.Common/Internal/Extensions/IEntityExtensions.cs index 00529c10..12a1882b 100644 --- a/source/DefaultDocumentation.Common/Internal/Extensions/IEntityExtension.cs +++ b/source/DefaultDocumentation.Common/Internal/Extensions/IEntityExtensions.cs @@ -2,11 +2,11 @@ namespace ICSharpCode.Decompiler.TypeSystem { - internal static class IEntityExtension + internal static class IEntityExtensions { - public static bool IsDefaultConstructor(this IEntity entity) => - entity is IMethod method && method.IsConstructor + public static bool IsDefaultConstructor(this IEntity entity) + => entity is IMethod method && method.IsConstructor && method.Parameters.Count is 0 - && ((method.DeclaringType.Kind is TypeKind.Struct or TypeKind.Enum) || method.DeclaringTypeDefinition.GetConstructors().Count() == 1); + && ((method.DeclaringType.Kind is TypeKind.Struct or TypeKind.Enum) || method.DeclaringTypeDefinition?.GetConstructors().Count() == 1); } } diff --git a/source/DefaultDocumentation.Common/Internal/Extensions/XElementExtension.cs b/source/DefaultDocumentation.Common/Internal/Extensions/XElementExtension.cs deleted file mode 100644 index 4ffd4c81..00000000 --- a/source/DefaultDocumentation.Common/Internal/Extensions/XElementExtension.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace System.Xml.Linq -{ - internal static class XElementExtension - { - public static string GetCRefAttribute(this XElement element) => element.Attribute("cref")?.Value; - - public static bool HasExclude(this XElement element) => element?.Element("exclude") != null; - - public static bool HasInheritDoc(this XElement element, out XElement inheritDoc) - { - inheritDoc = element?.Element("inheritdoc"); - return inheritDoc != null; - } - } -} diff --git a/source/DefaultDocumentation.Common/Internal/Extensions/XElementExtensions.cs b/source/DefaultDocumentation.Common/Internal/Extensions/XElementExtensions.cs new file mode 100644 index 00000000..3cbc57bf --- /dev/null +++ b/source/DefaultDocumentation.Common/Internal/Extensions/XElementExtensions.cs @@ -0,0 +1,18 @@ +using System.Diagnostics.CodeAnalysis; + +namespace System.Xml.Linq +{ + internal static class XElementExtensions + { + public static string? GetCRefAttribute(this XElement element) => element.Attribute("cref")?.Value; + + public static bool HasExclude(this XElement? element) => element?.Element("exclude") != null; + + public static bool HasInheritDoc(this XElement? element, [NotNullWhen(true)] out XElement? inheritDoc) + { + inheritDoc = element?.Element("inheritdoc"); + + return inheritDoc != null; + } + } +} diff --git a/source/DefaultDocumentation.Common/Internal/GeneralContext.cs b/source/DefaultDocumentation.Common/Internal/GeneralContext.cs index 7fd2e0f7..fc0e3a82 100644 --- a/source/DefaultDocumentation.Common/Internal/GeneralContext.cs +++ b/source/DefaultDocumentation.Common/Internal/GeneralContext.cs @@ -12,14 +12,12 @@ internal sealed class GeneralContext : Context, IGeneralContext { private readonly Dictionary _contexts; private readonly ConcurrentDictionary _fileNames; - private readonly ConcurrentDictionary _urls; - private readonly IUrlFactory[] _urlFactories; public IEnumerable AllFileNameFactory => _contexts .Values .Concat(Enumerable.Repeat(this, 1)) - .Select(c => c.FileNameFactory) - .Where(f => f != null) + .Where(c => c.FileNameFactory != null) + .Select(c => c.FileNameFactory!) .Distinct(); public GeneralContext( @@ -29,6 +27,8 @@ public GeneralContext( IReadOnlyDictionary items) : base(config, availableTypes) { + ArgumentNullException.ThrowIfNull(FileNameFactory, nameof(FileNameFactory)); + Dictionary availableElements = availableTypes .Where(t => typeof(IElement).IsAssignableFrom(t) && !t.IsAbstract) .Select(t => (IElement)Activator.CreateInstance(t)) @@ -55,17 +55,16 @@ public GeneralContext( .Where(t => typeof(DocItem).IsAssignableFrom(t) && !t.IsAbstract) .Select(t => (t, GetSetting(t.Name))) .Where(t => t.Item2 != null) - .ToDictionary(t => t.t, t => new Context(t.Item2, availableTypes)); + .ToDictionary(t => t.t, t => new Context(t.Item2!, availableTypes)); _fileNames = new ConcurrentDictionary(); - _urls = new ConcurrentDictionary(); - string[] urlFactories = GetSetting(nameof(IRawSettings.UrlFactories)); + string[] urlFactories = GetSetting(nameof(IRawSettings.UrlFactories)) ?? Array.Empty(); Dictionary availableUrlFactories = availableTypes .Where(t => typeof(IUrlFactory).IsAssignableFrom(t) && !t.IsAbstract) .Select(t => (IUrlFactory)Activator.CreateInstance(t)) .GroupBy(w => w.Name) .ToDictionary(w => w.Key, w => w.Last()); - _urlFactories = urlFactories + UrlFactories = urlFactories .Select(id => availableUrlFactories.TryGetValue(id, out IUrlFactory urlFactory) ? urlFactory @@ -78,7 +77,7 @@ public GeneralContext( Settings.Logger.Info($"Elements that will be used:{string.Concat(Elements.Select(e => $"{Environment.NewLine} {e.Key}: {e.Value.GetType().AssemblyQualifiedName}"))}"); Settings.Logger.Info($"FileNameFactory that will be used: {FileNameFactory?.GetType().AssemblyQualifiedName}"); - Settings.Logger.Info($"UrlFactories that will be used:{string.Concat(_urlFactories?.Select(s => $"{Environment.NewLine} {s.GetType().AssemblyQualifiedName}") ?? Enumerable.Empty())}"); + Settings.Logger.Info($"UrlFactories that will be used:{string.Concat(UrlFactories.Select(s => $"{Environment.NewLine} {s.GetType().AssemblyQualifiedName}"))}"); Settings.Logger.Info($"Sections that will be used:{string.Concat(Sections?.Select(s => $"{Environment.NewLine} {s.GetType().AssemblyQualifiedName}") ?? Enumerable.Empty())}"); foreach (KeyValuePair pair in _contexts) @@ -96,11 +95,11 @@ public GeneralContext( public IReadOnlyDictionary Elements { get; } - public IContext GetContext(Type type) => _contexts.TryGetValue(type, out Context context) ? context : this; + public IEnumerable UrlFactories { get; } - public string GetFileName(DocItem item) => _fileNames.GetOrAdd(item, i => (this.GetContext(item)?.FileNameFactory ?? FileNameFactory).GetFileName(this, i)); + public IContext GetContext(Type? type) => type != null && _contexts.TryGetValue(type, out Context context) ? context : this; - public string GetUrl(string id) => _urls.GetOrAdd(id, i => _urlFactories.Select(f => f.GetUrl(this, i)).FirstOrDefault(url => url is not null)); + public string GetFileName(DocItem item) => _fileNames.GetOrAdd(item, i => (this.GetContext(item).FileNameFactory ?? FileNameFactory!).GetFileName(this, i)); #endregion } diff --git a/source/DefaultDocumentation.Common/Internal/PageContext.cs b/source/DefaultDocumentation.Common/Internal/PageContext.cs new file mode 100644 index 00000000..b240f301 --- /dev/null +++ b/source/DefaultDocumentation.Common/Internal/PageContext.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using DefaultDocumentation.Api; +using DefaultDocumentation.Models; + +namespace DefaultDocumentation.Internal +{ + internal sealed class PageContext : IPageContext + { + private sealed class StringComparer : IEqualityComparer + { + public bool Equals(string? x, string? y) => string.Equals(x, y, StringComparison.OrdinalIgnoreCase); + +#if NET5_0_OR_GREATER + public int GetHashCode(string obj) => obj.GetHashCode(StringComparison.OrdinalIgnoreCase); +#else + public int GetHashCode(string obj) => obj.ToUpperInvariant().GetHashCode(); +#endif + } + + private readonly IGeneralContext _context; + private readonly Dictionary _data; + + public PageContext(IGeneralContext context, DocItem item) + { + _data = new Dictionary(new StringComparer()); + + _context = context; + DocItem = item; + } + + #region IPageContext + + public object? this[string key] + { + get => _data.TryGetValue(key, out object? value) ? value : null; + set => _data[key] = value; + } + + public DocItem DocItem { get; } + + public ISettings Settings => _context.Settings; + + public IReadOnlyDictionary Items => _context.Items; + + public IReadOnlyDictionary Elements => _context.Elements; + + public IFileNameFactory? FileNameFactory => _context.FileNameFactory; + + public IEnumerable? Sections => _context.Sections; + + public IEnumerable UrlFactories => _context.UrlFactories; + + public IContext GetContext(Type? type) => _context.GetContext(type); + + public string GetFileName(DocItem item) => _context.GetFileName(item); + + public T? GetSetting(string name) => _context.GetSetting(name); + + #endregion + } +} diff --git a/source/DefaultDocumentation.Common/Internal/PageWriter.cs b/source/DefaultDocumentation.Common/Internal/PageWriter.cs index af2d74f8..05225429 100644 --- a/source/DefaultDocumentation.Common/Internal/PageWriter.cs +++ b/source/DefaultDocumentation.Common/Internal/PageWriter.cs @@ -1,37 +1,22 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Text; using DefaultDocumentation.Api; -using DefaultDocumentation.Models; namespace DefaultDocumentation.Internal { internal sealed class PageWriter : IWriter { - private sealed class StringComparer : IEqualityComparer - { - public bool Equals(string x, string y) => string.Equals(x, y, StringComparison.OrdinalIgnoreCase); - - public int GetHashCode(string obj) => obj.ToUpperInvariant().GetHashCode(); - } - private readonly StringBuilder _builder; - private readonly Dictionary _data; - public PageWriter(StringBuilder builder, IGeneralContext context, DocItem item) + public PageWriter(StringBuilder builder, IPageContext context) { _builder = builder; - _data = new Dictionary(new StringComparer()); Context = context; - DocItem = item; } #region IWriter - public IGeneralContext Context { get; } - - public DocItem DocItem { get; } + public IPageContext Context { get; } public int Length { @@ -39,12 +24,6 @@ public int Length set => _builder.Length = value; } - public object this[string key] - { - get => _data.TryGetValue(key, out object value) ? value : null; - set => _data[key] = value; - } - public IWriter Append(string value) { _builder.Append(value); diff --git a/source/DefaultDocumentation.Common/Internal/Settings.cs b/source/DefaultDocumentation.Common/Internal/Settings.cs index 95cde793..0cae77e5 100644 --- a/source/DefaultDocumentation.Common/Internal/Settings.cs +++ b/source/DefaultDocumentation.Common/Internal/Settings.cs @@ -16,21 +16,23 @@ public sealed class Settings : ISettings public Settings( ILogger logger, - string assemblyFilePath, - string documentationFilePath, - string projectDirectoryPath, - string outputDirectoryPath, - string assemblyPageName, + string? assemblyFilePath, + string? documentationFilePath, + string? projectDirectoryPath, + string? outputDirectoryPath, + string? assemblyPageName, GeneratedAccessModifiers generatedAccessModifiers, GeneratedPages generatedPages, bool includeUndocumentedItems, - string linksOutputFile, - string linksBaseUrl, - IEnumerable externlinksFilePaths) + string? linksOutputFile, + string? linksBaseUrl, + IEnumerable? externlinksFilePaths) { - Logger = logger ?? throw new ArgumentNullException(nameof(logger)); + ArgumentNullException.ThrowIfNull(logger); - AssemblyFile = !string.IsNullOrEmpty(assemblyFilePath) ? new FileInfo(assemblyFilePath) : throw new ArgumentNullException(nameof(assemblyFilePath)); + Logger = logger; + + AssemblyFile = !string.IsNullOrEmpty(assemblyFilePath) ? new FileInfo(assemblyFilePath) : throw new ArgumentException("assembly path cannot be null or empty", nameof(assemblyFilePath)); DocumentationFile = string.IsNullOrEmpty(documentationFilePath) ? new FileInfo(Path.Combine(AssemblyFile.Directory.FullName, Path.GetFileNameWithoutExtension(AssemblyFile.Name) + ".xml")) : new FileInfo(documentationFilePath); ProjectDirectory = string.IsNullOrEmpty(projectDirectoryPath) ? null : new DirectoryInfo(projectDirectoryPath); OutputDirectory = string.IsNullOrEmpty(outputDirectoryPath) ? DocumentationFile.Directory : new DirectoryInfo(outputDirectoryPath); @@ -85,11 +87,11 @@ private static IEnumerable GetFilePaths(string filePath) public FileInfo DocumentationFile { get; } - public DirectoryInfo ProjectDirectory { get; } + public DirectoryInfo? ProjectDirectory { get; } public DirectoryInfo OutputDirectory { get; } - public string AssemblyPageName { get; } + public string? AssemblyPageName { get; } public GeneratedPages GeneratedPages { get; } @@ -97,7 +99,7 @@ private static IEnumerable GetFilePaths(string filePath) public bool IncludeUndocumentedItems { get; } - public FileInfo LinksOutputFile { get; } + public FileInfo? LinksOutputFile { get; } public string LinksBaseUrl { get; } diff --git a/source/DefaultDocumentation.Console/DefaultDocumentation.Console.csproj b/source/DefaultDocumentation.Console/DefaultDocumentation.Console.csproj index 13ebd489..12e63ed5 100644 --- a/source/DefaultDocumentation.Console/DefaultDocumentation.Console.csproj +++ b/source/DefaultDocumentation.Console/DefaultDocumentation.Console.csproj @@ -1,24 +1,25 @@  - - Create a simple markdown documentation from the Visual Studio xml one. - DefaultDocumentation - Exe - - netcoreapp3.1; - net6.0; - net7.0; - - defaultdocumentation - true - true - true - - - - + + Create a simple markdown documentation from the Visual Studio xml one. + DefaultDocumentation + Exe + + net6.0; + net8.0; + + defaultdocumentation + true + true + true + + + + + + + + + - - - \ No newline at end of file diff --git a/source/DefaultDocumentation.Markdown/DefaultDocumentation.Markdown.csproj b/source/DefaultDocumentation.Markdown/DefaultDocumentation.Markdown.csproj index 1275d1b3..80f66a52 100644 --- a/source/DefaultDocumentation.Markdown/DefaultDocumentation.Markdown.csproj +++ b/source/DefaultDocumentation.Markdown/DefaultDocumentation.Markdown.csproj @@ -1,12 +1,18 @@  - - Default implementation for Markdown documentation generation in DefaultDocumentation. - netstandard2.0 - true - enable - - - - - + + + Default implementation for Markdown documentation generation in DefaultDocumentation. + netstandard2.0 + true + enable + + + + + + + + + + diff --git a/source/DefaultDocumentation.Markdown/Elements/CElement.cs b/source/DefaultDocumentation.Markdown/Elements/CElement.cs index e7f21013..79b6861f 100644 --- a/source/DefaultDocumentation.Markdown/Elements/CElement.cs +++ b/source/DefaultDocumentation.Markdown/Elements/CElement.cs @@ -19,6 +19,9 @@ public sealed class CElement : IElement /// public void Write(IWriter writer, XElement element) { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(element); + writer .Append("`") .Append(element.Value) diff --git a/source/DefaultDocumentation.Markdown/Elements/CodeElement.cs b/source/DefaultDocumentation.Markdown/Elements/CodeElement.cs index a35ea19e..2dc447f8 100644 --- a/source/DefaultDocumentation.Markdown/Elements/CodeElement.cs +++ b/source/DefaultDocumentation.Markdown/Elements/CodeElement.cs @@ -49,6 +49,9 @@ private static string GetCode(ISettings settings, string source, string? region /// public void Write(IWriter writer, XElement element) { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(element); + if (writer.GetDisplayAsSingleLine()) { return; diff --git a/source/DefaultDocumentation.Markdown/Elements/ListElement.cs b/source/DefaultDocumentation.Markdown/Elements/ListElement.cs index b5340a5b..40ec5fe9 100644 --- a/source/DefaultDocumentation.Markdown/Elements/ListElement.cs +++ b/source/DefaultDocumentation.Markdown/Elements/ListElement.cs @@ -119,6 +119,9 @@ private static void WriteTable(IWriter writer, XElement element) /// public void Write(IWriter writer, XElement element) { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(element); + if (writer.GetDisplayAsSingleLine()) { return; diff --git a/source/DefaultDocumentation.Markdown/Elements/NoteElement.cs b/source/DefaultDocumentation.Markdown/Elements/NoteElement.cs index 9ebe6f14..b7437c0a 100644 --- a/source/DefaultDocumentation.Markdown/Elements/NoteElement.cs +++ b/source/DefaultDocumentation.Markdown/Elements/NoteElement.cs @@ -21,6 +21,9 @@ public sealed class NoteElement : IElement /// public void Write(IWriter writer, XElement element) { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(element); + if (writer.GetDisplayAsSingleLine()) { return; diff --git a/source/DefaultDocumentation.Markdown/Elements/ParamRefElement.cs b/source/DefaultDocumentation.Markdown/Elements/ParamRefElement.cs index c07c4c17..01945af8 100644 --- a/source/DefaultDocumentation.Markdown/Elements/ParamRefElement.cs +++ b/source/DefaultDocumentation.Markdown/Elements/ParamRefElement.cs @@ -22,6 +22,9 @@ public sealed class ParamRefElement : IElement /// public void Write(IWriter writer, XElement element) { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(element); + string? name = element.GetNameAttribute(); if (name != null) diff --git a/source/DefaultDocumentation.Markdown/Elements/SeeElement.cs b/source/DefaultDocumentation.Markdown/Elements/SeeElement.cs index fe927034..16d594ac 100644 --- a/source/DefaultDocumentation.Markdown/Elements/SeeElement.cs +++ b/source/DefaultDocumentation.Markdown/Elements/SeeElement.cs @@ -21,6 +21,9 @@ public sealed class SeeElement : IElement /// public void Write(IWriter writer, XElement element) { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(element); + string? @ref = element.GetCRefAttribute(); if (@ref is not null) { diff --git a/source/DefaultDocumentation.Markdown/Elements/TypeParamRefElement.cs b/source/DefaultDocumentation.Markdown/Elements/TypeParamRefElement.cs index d1710218..e46d5616 100644 --- a/source/DefaultDocumentation.Markdown/Elements/TypeParamRefElement.cs +++ b/source/DefaultDocumentation.Markdown/Elements/TypeParamRefElement.cs @@ -22,6 +22,9 @@ public sealed class TypeParamRefElement : IElement /// public void Write(IWriter writer, XElement element) { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(element); + string? name = element.GetNameAttribute(); if (name != null) diff --git a/source/DefaultDocumentation.Markdown/Extensions/DocItemExtension.cs b/source/DefaultDocumentation.Markdown/Extensions/DocItemExtensions.cs similarity index 67% rename from source/DefaultDocumentation.Markdown/Extensions/DocItemExtension.cs rename to source/DefaultDocumentation.Markdown/Extensions/DocItemExtensions.cs index d3d1334a..4c054439 100644 --- a/source/DefaultDocumentation.Markdown/Extensions/DocItemExtension.cs +++ b/source/DefaultDocumentation.Markdown/Extensions/DocItemExtensions.cs @@ -5,7 +5,7 @@ namespace DefaultDocumentation.Models /// /// Provides extension methods on the type. /// - public static class DocItemExtension + public static class DocItemExtensions { /// /// Gets the long name of a , being its full name without its namespace. @@ -13,6 +13,11 @@ public static class DocItemExtension /// /// The for which to get its long name. /// The long name of the . - public static string GetLongName(this DocItem item) => string.Join(".", item.GetParents().Skip(2).Select(p => p.Name).Concat(Enumerable.Repeat(item.Name, 1))); + public static string GetLongName(this DocItem item) + { + ArgumentNullException.ThrowIfNull(item); + + return string.Join(".", item.GetParents().Skip(2).Select(p => p.Name).Concat(Enumerable.Repeat(item.Name, 1))); + } } } diff --git a/source/DefaultDocumentation.Markdown/Extensions/IGeneralContextExtension.cs b/source/DefaultDocumentation.Markdown/Extensions/IGeneralContextExtensions.cs similarity index 76% rename from source/DefaultDocumentation.Markdown/Extensions/IGeneralContextExtension.cs rename to source/DefaultDocumentation.Markdown/Extensions/IGeneralContextExtensions.cs index 13231e41..a30c6433 100644 --- a/source/DefaultDocumentation.Markdown/Extensions/IGeneralContextExtension.cs +++ b/source/DefaultDocumentation.Markdown/Extensions/IGeneralContextExtensions.cs @@ -9,11 +9,12 @@ namespace DefaultDocumentation /// /// Provides extension methods on the type. /// - public static class IGeneralContextExtension + public static class IGeneralContextExtensions { private const string NestedTypeVisibilitiesKey = "Markdown.NestedTypeVisibilities"; private const string RemoveFileExtensionFromUrlKey = "Markdown.RemoveFileExtensionFromUrl"; private const string InvalidCharReplacementKey = "Markdown.InvalidCharReplacement"; + private const string UseFullUrlKey = "Markdown.UseFullUrl"; /// /// Gets the Markdown.NestedTypeVisibilities setting. @@ -23,6 +24,9 @@ public static class IGeneralContextExtension /// The to use. public static NestedTypeVisibilities GetNestedTypeVisibilities(this IGeneralContext context, Type type) { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(type); + NestedTypeVisibilities value = context.GetSetting(type, c => c.GetSetting(NestedTypeVisibilitiesKey)) ?? NestedTypeVisibilities.Default; if (value is NestedTypeVisibilities.Default) @@ -38,14 +42,36 @@ public static NestedTypeVisibilities GetNestedTypeVisibilities(this IGeneralCont /// /// The of the current documentation file. /// Whether to include the file extension in urls. - public static bool GetRemoveFileExtensionFromUrl(this IGeneralContext context) => context.GetSetting(RemoveFileExtensionFromUrlKey); + public static bool GetRemoveFileExtensionFromUrl(this IGeneralContext context) + { + ArgumentNullException.ThrowIfNull(context); + + return context.GetSetting(RemoveFileExtensionFromUrlKey); + } /// /// Gets the Markdown.InvalidCharReplacement setting. /// /// The of the current documentation file. /// The to use to replace invalid chars in generated file name. - public static string? GetInvalidCharReplacement(this IGeneralContext context) => context.GetSetting(InvalidCharReplacementKey); + public static string? GetInvalidCharReplacement(this IGeneralContext context) + { + ArgumentNullException.ThrowIfNull(context); + + return context.GetSetting(InvalidCharReplacementKey); + } + + /// + /// Gets the Markdown.UseFullUrl setting. + /// + /// The of the current documentation file. + /// The to use to replace invalid chars in generated file name. + public static bool GetUseFullUrl(this IGeneralContext context) + { + ArgumentNullException.ThrowIfNull(context); + + return context.GetSetting(UseFullUrlKey); + } /// /// Gets the children of a specific type of a instance. @@ -57,6 +83,9 @@ public static NestedTypeVisibilities GetNestedTypeVisibilities(this IGeneralCont public static IEnumerable GetChildren(this IGeneralContext context, DocItem item) where T : DocItem { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(item); + IEnumerable GetAllChildren(DocItem item) { foreach (DocItem child in context.Items.Values.Where(i => i.Parent == item)) diff --git a/source/DefaultDocumentation.Markdown/Extensions/IWriterExtension.cs b/source/DefaultDocumentation.Markdown/Extensions/IWriterExtensions.cs similarity index 79% rename from source/DefaultDocumentation.Markdown/Extensions/IWriterExtension.cs rename to source/DefaultDocumentation.Markdown/Extensions/IWriterExtensions.cs index 2e577e3e..4cc343d6 100644 --- a/source/DefaultDocumentation.Markdown/Extensions/IWriterExtension.cs +++ b/source/DefaultDocumentation.Markdown/Extensions/IWriterExtensions.cs @@ -15,7 +15,7 @@ namespace DefaultDocumentation.Markdown.Extensions /// /// Provides extension methods on the type. /// - public static class IWriterExtension + public static class IWriterExtensions { private static readonly CSharpAmbience _nameAmbience = new() { @@ -30,22 +30,30 @@ public static class IWriterExtension /// /// Gets the current item that is being processed by this . - /// It can be different from the when inlining child documentation in its parent page. + /// It can be different from the when inlining child documentation in its parent page. /// /// The for which to get the current item. /// The for which the documentation is being generated. - public static DocItem GetCurrentItem(this IWriter writer) => writer[CurrentItemKey] as DocItem ?? writer.DocItem; + public static DocItem GetCurrentItem(this IWriter writer) + { + ArgumentNullException.ThrowIfNull(writer); + + return writer.Context[CurrentItemKey] as DocItem ?? writer.Context.DocItem; + } /// /// Sets the current item that is being processed by this . - /// It can be different from the when inlining child documentation in its parent page. + /// It can be different from the when inlining child documentation in its parent page. /// /// The for which to set the current item. /// The for which the documentation is being generated. /// The given . public static IWriter SetCurrentItem(this IWriter writer, DocItem value) { - writer[CurrentItemKey] = value; + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(value); + + writer.Context[CurrentItemKey] = value; return writer; } @@ -56,7 +64,12 @@ public static IWriter SetCurrentItem(this IWriter writer, DocItem value) /// /// The for which to get this setting. /// Whether all futur data to happend should stay on the same line. - public static bool GetDisplayAsSingleLine(this IWriter writer) => writer[DisplayAsSingleLineKey] as bool? ?? false; + public static bool GetDisplayAsSingleLine(this IWriter writer) + { + ArgumentNullException.ThrowIfNull(writer); + + return writer.Context[DisplayAsSingleLineKey] as bool? ?? false; + } /// /// Sets whether all futur data appended to the given should stay on the same line (usefull for table). @@ -67,7 +80,9 @@ public static IWriter SetCurrentItem(this IWriter writer, DocItem value) /// The given . public static IWriter SetDisplayAsSingleLine(this IWriter writer, bool? value) { - writer[DisplayAsSingleLineKey] = value; + ArgumentNullException.ThrowIfNull(writer); + + writer.Context[DisplayAsSingleLineKey] = value; return writer; } @@ -79,9 +94,13 @@ public static IWriter SetDisplayAsSingleLine(this IWriter writer, bool? value) /// The for which to get this setting. /// Whether line break in the xml documentation should be ignored in the generated markdown. /// Markdown.IgnoreLineBreak - public static bool GetIgnoreLineBreak(this IWriter writer) => - writer[IgnoreLineBreakLineKey] as bool? - ?? writer.Context.GetSetting(writer.GetCurrentItem(), c => c.GetSetting(IgnoreLineBreakLineKey)).GetValueOrDefault(); + public static bool GetIgnoreLineBreak(this IWriter writer) + { + ArgumentNullException.ThrowIfNull(writer); + + return writer.Context[IgnoreLineBreakLineKey] as bool? + ?? writer.Context.GetSetting(writer.GetCurrentItem(), c => c.GetSetting(IgnoreLineBreakLineKey)).GetValueOrDefault(); + } /// /// Sets whether line break in the xml documentation should be ignored in the generated markdown. @@ -93,7 +112,9 @@ writer[IgnoreLineBreakLineKey] as bool? /// Markdown.IgnoreLineBreak public static IWriter SetIgnoreLineBreakLine(this IWriter writer, bool? value) { - writer[IgnoreLineBreakLineKey] = value; + ArgumentNullException.ThrowIfNull(writer); + + writer.Context[IgnoreLineBreakLineKey] = value; return writer; } @@ -106,8 +127,10 @@ public static IWriter SetIgnoreLineBreakLine(this IWriter writer, bool? value) /// The displayed name of the link. /// The tooltip of the link. /// The given . - public static IWriter AppendUrl(this IWriter writer, string url, string? displayedName = null, string? tooltip = null) + public static IWriter AppendUrl(this IWriter writer, string? url, string? displayedName = null, string? tooltip = null) { + ArgumentNullException.ThrowIfNull(writer); + if (string.IsNullOrEmpty(url)) { writer.Append((displayedName ?? "").Prettify()); @@ -116,11 +139,11 @@ public static IWriter AppendUrl(this IWriter writer, string url, string? display { writer .Append("[") - .Append((displayedName ?? url).Prettify()) + .Append((displayedName ?? url!).Prettify()) .Append("](") - .Append(url) + .Append(url!) .Append(" '") - .Append(tooltip ?? url) + .Append(tooltip ?? url!) .Append("')"); } @@ -134,19 +157,30 @@ public static IWriter AppendUrl(this IWriter writer, string url, string? display /// The to link to. /// The displayed name of the link. /// The given . - public static IWriter AppendLink(this IWriter writer, DocItem item, string? displayedName = null) => writer.AppendUrl(writer.Context.GetUrl(item), displayedName ?? item.Name, item.FullName); + public static IWriter AppendLink(this IWriter writer, DocItem item, string? displayedName = null) + { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(item); + + return writer.AppendUrl(writer.Context.GetUrl(item), displayedName ?? item.Name, item.FullName); + } /// - /// Append an link to an id using to resolve the url in the markdown format. + /// Append an link to an id using to resolve the url in the markdown format. /// /// The to use. /// The id to link to. /// The displayed name of the link. /// The given . - public static IWriter AppendLink(this IWriter writer, string id, string? displayedName = null) => - writer.Context.Items.TryGetValue(id, out DocItem item) - ? writer.AppendLink(item, displayedName) - : writer.AppendUrl(writer.Context.GetUrl(id), displayedName ?? id.Substring(2), id.Substring(2)); + public static IWriter AppendLink(this IWriter writer, string id, string? displayedName = null) + { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(id); + + return writer.Context.Items.TryGetValue(id, out DocItem item) + ? writer.AppendLink(item, displayedName) + : writer.AppendUrl(writer.Context.GetUrl(id), displayedName ?? id.Substring(2), id.Substring(2)); + } /// /// Append an link to an in the markdown format. @@ -157,6 +191,10 @@ public static IWriter AppendLink(this IWriter writer, string id, string? display /// The given . public static IWriter AppendLink(this IWriter writer, DocItem item, INamedElement element) { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(item); + ArgumentNullException.ThrowIfNull(element); + IWriter HandleParameterizedType(ParameterizedType genericType) { string id = genericType.GetDefinition().GetIdString(); @@ -238,8 +276,8 @@ IWriter HandleTupleType(TupleType tupleType) _ when type is ParameterizedType genericType => HandleParameterizedType(genericType), _ => writer.AppendLink(type.GetDefinition().GetIdString()) }, - IMember member => writer.AppendLink(member.MemberDefinition.GetIdString(), _nameAmbience.ConvertSymbol(member).Replace("?", string.Empty)), - IEntity entity => writer.AppendLink(entity.GetIdString(), _nameAmbience.ConvertSymbol(entity).Replace("?", string.Empty)), + IMember member => writer.AppendLink(member.MemberDefinition.GetIdString(), member.ToString(_nameAmbience).Replace("?", string.Empty)), + IEntity entity => writer.AppendLink(entity.GetIdString(), entity.ToString(_nameAmbience).Replace("?", string.Empty)), _ => writer.Append(element.FullName) }; } @@ -249,19 +287,28 @@ IWriter HandleTupleType(TupleType tupleType) /// /// The to check. /// The given . - public static IWriter EnsureLineStart(this IWriter writer) => - writer.Length > 0 && (!writer.EndsWith(Environment.NewLine) || (writer.GetDisplayAsSingleLine() && !writer.EndsWith("
"))) - ? writer.AppendLine() - : writer; + public static IWriter EnsureLineStart(this IWriter writer) + { + ArgumentNullException.ThrowIfNull(writer); + + return writer.Length > 0 && (!writer.EndsWith(Environment.NewLine) || (writer.GetDisplayAsSingleLine() && !writer.EndsWith("
"))) + ? writer.AppendLine() + : writer; + } /// /// Calls and . /// /// The to write to. /// The given . - public static IWriter EnsureLineStartAndAppendLine(this IWriter writer) => writer - .EnsureLineStart() - .AppendLine(); + public static IWriter EnsureLineStartAndAppendLine(this IWriter writer) + { + ArgumentNullException.ThrowIfNull(writer); + + return writer + .EnsureLineStart() + .AppendLine(); + } /// /// Appends a decorating the with a . @@ -271,6 +318,8 @@ public static IWriter EnsureLineStartAndAppendLine(this IWriter writer) => write /// The given . public static IWriter AppendAsMarkdown(this IWriter writer, XElement? element) { + ArgumentNullException.ThrowIfNull(writer); + new MarkdownWriter(writer) .SetIgnoreLineBreakLine(element?.GetIgnoreLineBreak()) .Append(element); @@ -284,13 +333,24 @@ public static IWriter AppendAsMarkdown(this IWriter writer, XElement? element) /// The to decorate. /// The string to prefix every new line with. /// The decorated . - public static IWriter ToPrefixedWriter(this IWriter writer, string prefix) => new PrefixedWriter(writer, prefix); + public static IWriter ToPrefixedWriter(this IWriter writer, string prefix) + { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(prefix); + + return new PrefixedWriter(writer, prefix); + } /// /// Decorates the given with a to override its setting in a given scope. /// /// The to decorate. /// The decorated . - public static IWriter ToOverrideWriter(this IWriter writer) => new OverrideWriter(writer); + public static IWriter ToOverrideWriter(this IWriter writer) + { + ArgumentNullException.ThrowIfNull(writer); + + return new OverrideWriter(writer); + } } } diff --git a/source/DefaultDocumentation.Markdown/FileNameFactories/AMarkdownFactory.cs b/source/DefaultDocumentation.Markdown/FileNameFactories/BaseMarkdownFileNameFactory.cs similarity index 89% rename from source/DefaultDocumentation.Markdown/FileNameFactories/AMarkdownFactory.cs rename to source/DefaultDocumentation.Markdown/FileNameFactories/BaseMarkdownFileNameFactory.cs index 4949f52d..504a35be 100644 --- a/source/DefaultDocumentation.Markdown/FileNameFactories/AMarkdownFactory.cs +++ b/source/DefaultDocumentation.Markdown/FileNameFactories/BaseMarkdownFileNameFactory.cs @@ -13,7 +13,7 @@ namespace DefaultDocumentation.Markdown.FileNameFactories /// Base implementation of the to generate file with a .md extension. /// It will also replace invalid char that may be present with the Markdown.InvalidCharReplacement setting. /// - public abstract class AMarkdownFactory : IFileNameFactory + public abstract class BaseMarkdownFileNameFactory : IFileNameFactory { /// public abstract string Name { get; } @@ -27,8 +27,11 @@ public abstract class AMarkdownFactory : IFileNameFactory protected abstract string GetMarkdownFileName(IGeneralContext context, DocItem item); /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1851:Possible multiple enumerations of 'IEnumerable' collection", Justification = "Exepected")] public void Clean(IGeneralContext context) { + ArgumentNullException.ThrowIfNull(context); + context.Settings.Logger.Debug($"Cleaning output folder \"{context.Settings.OutputDirectory}\""); if (context.Settings.OutputDirectory.Exists) @@ -40,7 +43,7 @@ public void Clean(IGeneralContext context) foreach (FileInfo file in files) { i = 3; - start: +start: try { file.Delete(); diff --git a/source/DefaultDocumentation.Markdown/FileNameFactories/FullNameFactory.cs b/source/DefaultDocumentation.Markdown/FileNameFactories/FullNameFactory.cs index fa347a51..da6dd96b 100644 --- a/source/DefaultDocumentation.Markdown/FileNameFactories/FullNameFactory.cs +++ b/source/DefaultDocumentation.Markdown/FileNameFactories/FullNameFactory.cs @@ -5,7 +5,7 @@ namespace DefaultDocumentation.Markdown.FileNameFactories /// /// implementation using as file name. /// - public sealed class FullNameFactory : AMarkdownFactory + public sealed class FullNameFactory : BaseMarkdownFileNameFactory { /// /// The name of this implementation used at the configuration level. diff --git a/source/DefaultDocumentation.Markdown/FileNameFactories/Md5Factory.cs b/source/DefaultDocumentation.Markdown/FileNameFactories/Md5Factory.cs index 514d3104..acf40135 100644 --- a/source/DefaultDocumentation.Markdown/FileNameFactories/Md5Factory.cs +++ b/source/DefaultDocumentation.Markdown/FileNameFactories/Md5Factory.cs @@ -9,7 +9,7 @@ namespace DefaultDocumentation.Markdown.FileNameFactories /// /// implementation using an md5 on the as file name. /// - public sealed class Md5Factory : AMarkdownFactory + public sealed class Md5Factory : BaseMarkdownFileNameFactory { /// /// The name of this implementation used at the configuration level. diff --git a/source/DefaultDocumentation.Markdown/FileNameFactories/NameAndMd5MixFactory.cs b/source/DefaultDocumentation.Markdown/FileNameFactories/NameAndMd5MixFactory.cs index 278879d6..bb342364 100644 --- a/source/DefaultDocumentation.Markdown/FileNameFactories/NameAndMd5MixFactory.cs +++ b/source/DefaultDocumentation.Markdown/FileNameFactories/NameAndMd5MixFactory.cs @@ -6,7 +6,7 @@ namespace DefaultDocumentation.Markdown.FileNameFactories /// /// implementation using and an md5 on the as file name. /// - public sealed class NameAndMd5MixFactory : AMarkdownFactory + public sealed class NameAndMd5MixFactory : BaseMarkdownFileNameFactory { /// /// The name of this implementation used at the configuration level. diff --git a/source/DefaultDocumentation.Markdown/FileNameFactories/NameFactory.cs b/source/DefaultDocumentation.Markdown/FileNameFactories/NameFactory.cs index b8979465..6caec723 100644 --- a/source/DefaultDocumentation.Markdown/FileNameFactories/NameFactory.cs +++ b/source/DefaultDocumentation.Markdown/FileNameFactories/NameFactory.cs @@ -5,7 +5,7 @@ namespace DefaultDocumentation.Markdown.FileNameFactories /// /// implementation using as file name. /// - public sealed class NameFactory : AMarkdownFactory + public sealed class NameFactory : BaseMarkdownFileNameFactory { /// /// The name of this implementation used at the configuration level. diff --git a/source/DefaultDocumentation.Markdown/Internal/CodeRegion.cs b/source/DefaultDocumentation.Markdown/Internal/CodeRegion.cs index b1f97375..74db4009 100644 --- a/source/DefaultDocumentation.Markdown/Internal/CodeRegion.cs +++ b/source/DefaultDocumentation.Markdown/Internal/CodeRegion.cs @@ -30,6 +30,7 @@ internal static class CodeRegion } } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1851:Possible multiple enumerations of 'IEnumerable' collection", Justification = "Expected")] public static string? Extract(string fileContent, string region) { (int start, int end)[] comments = GetComments(fileContent).ToArray(); diff --git a/source/DefaultDocumentation.Markdown/Internal/Extensions/StringExtension.cs b/source/DefaultDocumentation.Markdown/Internal/Extensions/StringExtensions.cs similarity index 96% rename from source/DefaultDocumentation.Markdown/Internal/Extensions/StringExtension.cs rename to source/DefaultDocumentation.Markdown/Internal/Extensions/StringExtensions.cs index cdde9f65..ea71e494 100644 --- a/source/DefaultDocumentation.Markdown/Internal/Extensions/StringExtension.cs +++ b/source/DefaultDocumentation.Markdown/Internal/Extensions/StringExtensions.cs @@ -1,6 +1,6 @@ namespace System { - internal static class StringExtension + internal static class StringExtensions { public static string Prettify(this string value) { diff --git a/source/DefaultDocumentation.Markdown/Internal/Extensions/XElementExtension.cs b/source/DefaultDocumentation.Markdown/Internal/Extensions/XElementExtensions.cs similarity index 97% rename from source/DefaultDocumentation.Markdown/Internal/Extensions/XElementExtension.cs rename to source/DefaultDocumentation.Markdown/Internal/Extensions/XElementExtensions.cs index cdb8bec4..204ca8d2 100644 --- a/source/DefaultDocumentation.Markdown/Internal/Extensions/XElementExtension.cs +++ b/source/DefaultDocumentation.Markdown/Internal/Extensions/XElementExtensions.cs @@ -2,7 +2,7 @@ namespace System.Xml.Linq { - internal static class XElementExtension + internal static class XElementExtensions { public static IEnumerable GetItems(this XElement element) => element.Elements("item"); diff --git a/source/DefaultDocumentation.Markdown/Sections/ChildrenSection.cs b/source/DefaultDocumentation.Markdown/Sections/ChildrenSection.cs index a5f0adc0..e2a4c9e0 100644 --- a/source/DefaultDocumentation.Markdown/Sections/ChildrenSection.cs +++ b/source/DefaultDocumentation.Markdown/Sections/ChildrenSection.cs @@ -45,7 +45,10 @@ protected ChildrenSection(string name, string title) /// public void Write(IWriter writer) { + ArgumentNullException.ThrowIfNull(writer); + bool titleWritten = false; + foreach (DocItem item in GetChildren(writer.Context, writer.GetCurrentItem()) ?? Array.Empty()) { if (!titleWritten) diff --git a/source/DefaultDocumentation.Markdown/Sections/DefinitionSection.cs b/source/DefaultDocumentation.Markdown/Sections/DefinitionSection.cs index cd618424..25e6979a 100644 --- a/source/DefaultDocumentation.Markdown/Sections/DefinitionSection.cs +++ b/source/DefaultDocumentation.Markdown/Sections/DefinitionSection.cs @@ -31,6 +31,8 @@ public sealed class DefinitionSection : ISection | ConversionFlags.ShowModifiers | ConversionFlags.ShowTypeParameterList | ConversionFlags.ShowTypeParameterVarianceModifier + | ConversionFlags.SupportRecordClasses + | ConversionFlags.SupportRecordStructs }; private static readonly CSharpAmbience _baseTypeAmbience = new() @@ -95,6 +97,7 @@ public sealed class DefinitionSection : ISection | ConversionFlags.ShowParameterNames | ConversionFlags.ShowReturnType | ConversionFlags.UseFullyQualifiedTypeNames + | ConversionFlags.SupportInitAccessors }; private static readonly CSharpAmbience _eventAmbience = new() @@ -187,7 +190,7 @@ static IWriter Write(IWriter writer, Action writeAction) { FieldDocItem item => Write(writer, w => { - w.Append(_fieldAmbience.ConvertSymbol(item.Field)); + w.Append(item.Field.ToString(_fieldAmbience)); if (item.Field.IsConst) { @@ -207,21 +210,21 @@ static IWriter Write(IWriter writer, Action writeAction) w.Append(";"); }), - PropertyDocItem item => Write(writer, w => w.Append(_propertyAmbience.ConvertSymbol(item.Property))), - EventDocItem item => Write(writer, w => w.Append(_eventAmbience.ConvertSymbol(item.Event))), - ConstructorDocItem item => Write(writer, w => w.Append(_methodAmbience.ConvertSymbol(item.Method)).Append(";")), - OperatorDocItem item => Write(writer, w => w.Append(_methodAmbience.ConvertSymbol(item.Method)).Append(";")), - ExplicitInterfaceImplementationDocItem item when item.Member is IEvent => Write(writer, w => w.Append(_eventAmbience.ConvertSymbol(item.Member))), - ExplicitInterfaceImplementationDocItem item when item.Member is IProperty => Write(writer, w => w.Append(_propertyAmbience.ConvertSymbol(item.Member))), + PropertyDocItem item => Write(writer, w => w.Append(item.Property.ToString(_propertyAmbience))), + EventDocItem item => Write(writer, w => w.Append(item.Event.ToString(_eventAmbience))), + ConstructorDocItem item => Write(writer, w => w.Append(item.Method.ToString(_methodAmbience)).Append(";")), + OperatorDocItem item => Write(writer, w => w.Append(item.Method.ToString(_methodAmbience)).Append(";")), + ExplicitInterfaceImplementationDocItem item when item.Member is IEvent => Write(writer, w => w.Append(item.Member.ToString(_eventAmbience))), + ExplicitInterfaceImplementationDocItem item when item.Member is IProperty => Write(writer, w => w.Append(item.Member.ToString(_propertyAmbience))), ExplicitInterfaceImplementationDocItem item when item.Member is IMethod => Write(writer, w => { - w.Append(_methodAmbience.ConvertSymbol(item.Member)); + w.Append(item.Member.ToString(_methodAmbience)); WriteConstraints(w, item); w.Append(";"); }), MethodDocItem item => Write(writer, w => { - w.Append(_methodAmbience.ConvertSymbol(item.Method)); + w.Append(item.Method.ToString(_methodAmbience)); WriteConstraints(w, item); w.Append(";"); }), @@ -230,18 +233,18 @@ static IWriter Write(IWriter writer, Action writeAction) IType enumType = item.Type.GetEnumUnderlyingType(); w - .Append(_typeAmbience.ConvertSymbol(item.Type)) + .Append(item.Type.ToString(_typeAmbience)) .Append(enumType.IsKnownType(KnownTypeCode.Int32) ? string.Empty : " : " + enumType.FullName); }), DelegateDocItem item => Write(writer, w => { - w.Append(_delegateAmbience.ConvertSymbol(item.Type)); + w.Append(item.Type.ToString(_delegateAmbience)); WriteConstraints(writer, item); w.Append(";"); }), TypeDocItem item => Write(writer, w => { - w.Append(_typeAmbience.ConvertSymbol(item.Type)); + w.Append(item.Type.ToString(_typeAmbience)); IType baseType = item.Type.DirectBaseTypes.FirstOrDefault(t => t.Kind == TypeKind.Class && !t.IsKnownType(KnownTypeCode.Object) && !t.IsKnownType(KnownTypeCode.ValueType)); if (baseType != null) @@ -254,7 +257,7 @@ static IWriter Write(IWriter writer, Action writeAction) foreach (IType @interface in item.Type.DirectBaseTypes.Where(t => t.Kind == TypeKind.Interface && t.GetDefinition()?.Accessibility == Accessibility.Public)) { w - .AppendLine(baseType is null ? " :" : ",") + .Append(baseType is null ? " : " : ", ") .Append(_baseTypeAmbience.ConvertType(@interface)); baseType = @interface; diff --git a/source/DefaultDocumentation.Markdown/Sections/DerivedSection.cs b/source/DefaultDocumentation.Markdown/Sections/DerivedSection.cs index 8b740956..7095f658 100644 --- a/source/DefaultDocumentation.Markdown/Sections/DerivedSection.cs +++ b/source/DefaultDocumentation.Markdown/Sections/DerivedSection.cs @@ -23,6 +23,8 @@ public sealed class DerivedSection : ISection /// public void Write(IWriter writer) { + ArgumentNullException.ThrowIfNull(writer); + if (writer.GetCurrentItem() is TypeDocItem typeItem) { List derived = writer.Context.Items.Values.OfType().Where(i => i.Type.DirectBaseTypes.Select(t => t.GetDefinition() ?? t).Contains(typeItem.Type)).OrderBy(i => i.FullName).ToList(); diff --git a/source/DefaultDocumentation.Markdown/Sections/ExceptionSection.cs b/source/DefaultDocumentation.Markdown/Sections/ExceptionSection.cs index 3e691640..2eb48d44 100644 --- a/source/DefaultDocumentation.Markdown/Sections/ExceptionSection.cs +++ b/source/DefaultDocumentation.Markdown/Sections/ExceptionSection.cs @@ -21,7 +21,10 @@ public sealed class ExceptionSection : ISection /// public void Write(IWriter writer) { + ArgumentNullException.ThrowIfNull(writer); + bool titleWritten = false; + foreach (XElement exception in writer.GetCurrentItem().Documentation?.Elements(Name) ?? Enumerable.Empty()) { if (!titleWritten) diff --git a/source/DefaultDocumentation.Markdown/Sections/FooterSection.cs b/source/DefaultDocumentation.Markdown/Sections/FooterSection.cs index c837f646..ffde99a6 100644 --- a/source/DefaultDocumentation.Markdown/Sections/FooterSection.cs +++ b/source/DefaultDocumentation.Markdown/Sections/FooterSection.cs @@ -19,7 +19,9 @@ public sealed class FooterSection : ISection /// public void Write(IWriter writer) { - if (writer.GetCurrentItem() != writer.DocItem) + ArgumentNullException.ThrowIfNull(writer); + + if (writer.GetCurrentItem() != writer.Context.DocItem) { return; } diff --git a/source/DefaultDocumentation.Markdown/Sections/HeaderSection.cs b/source/DefaultDocumentation.Markdown/Sections/HeaderSection.cs index b07c7e24..93f6184f 100644 --- a/source/DefaultDocumentation.Markdown/Sections/HeaderSection.cs +++ b/source/DefaultDocumentation.Markdown/Sections/HeaderSection.cs @@ -21,7 +21,9 @@ public sealed class HeaderSection : ISection /// public void Write(IWriter writer) { - if (writer.GetCurrentItem() != writer.DocItem) + ArgumentNullException.ThrowIfNull(writer); + + if (writer.GetCurrentItem() != writer.Context.DocItem) { return; } @@ -36,7 +38,7 @@ public void Write(IWriter writer) } bool firstWritten = false; - foreach (DocItem parent in writer.DocItem.GetParents().Skip(1)) + foreach (DocItem parent in writer.Context.DocItem.GetParents().Skip(1)) { if (!firstWritten) { diff --git a/source/DefaultDocumentation.Markdown/Sections/SeeAlsoSection.cs b/source/DefaultDocumentation.Markdown/Sections/SeeAlsoSection.cs index 59d9c657..372d35af 100644 --- a/source/DefaultDocumentation.Markdown/Sections/SeeAlsoSection.cs +++ b/source/DefaultDocumentation.Markdown/Sections/SeeAlsoSection.cs @@ -22,7 +22,10 @@ public sealed class SeeAlsoSection : ISection /// public void Write(IWriter writer) { + ArgumentNullException.ThrowIfNull(writer); + bool titleWritten = false; + foreach (XElement seeAlso in writer.GetCurrentItem().Documentation?.Elements(Name) ?? Enumerable.Empty()) { if (!titleWritten) diff --git a/source/DefaultDocumentation.Markdown/Sections/TableOfContentsSection.cs b/source/DefaultDocumentation.Markdown/Sections/TableOfContentsSection.cs index e7b0e595..cbf8780d 100644 --- a/source/DefaultDocumentation.Markdown/Sections/TableOfContentsSection.cs +++ b/source/DefaultDocumentation.Markdown/Sections/TableOfContentsSection.cs @@ -132,6 +132,8 @@ private static void WriteChildren(IWriter writer, Modes modes, IEnumerable public void Write(IWriter writer) { + ArgumentNullException.ThrowIfNull(writer); + Modes modes = writer.Context.GetSetting(writer.GetCurrentItem(), c => c.GetSetting("Markdown.TableOfContentsModes")).GetValueOrDefault(); Write(writer, modes, writer.GetCurrentItem()); diff --git a/source/DefaultDocumentation.Markdown/Sections/TitleSection.cs b/source/DefaultDocumentation.Markdown/Sections/TitleSection.cs index 70c41bda..c11f1577 100644 --- a/source/DefaultDocumentation.Markdown/Sections/TitleSection.cs +++ b/source/DefaultDocumentation.Markdown/Sections/TitleSection.cs @@ -25,6 +25,8 @@ public sealed class TitleSection : ISection /// public void Write(IWriter writer) { + ArgumentNullException.ThrowIfNull(writer); + DocItem currentItem = writer.GetCurrentItem(); Action? action = currentItem switch @@ -55,13 +57,17 @@ public void Write(IWriter writer) { if (!currentItem.HasOwnPage(writer.Context)) { - string url = writer.Context.GetUrl(currentItem); - int startIndex = url.IndexOf('#') + 1; - writer - .EnsureLineStartAndAppendLine() - .Append(""); + string? url = writer.Context.GetUrl(currentItem); + + if (url != null) + { + int startIndex = url.IndexOf('#') + 1; + writer + .EnsureLineStartAndAppendLine() + .Append(""); + } } writer.EnsureLineStartAndAppendLine(); diff --git a/source/DefaultDocumentation.Markdown/UrlFactories/DocItemFactory.cs b/source/DefaultDocumentation.Markdown/UrlFactories/DocItemFactory.cs index 1efe0652..97a029fd 100644 --- a/source/DefaultDocumentation.Markdown/UrlFactories/DocItemFactory.cs +++ b/source/DefaultDocumentation.Markdown/UrlFactories/DocItemFactory.cs @@ -19,8 +19,11 @@ public sealed class DocItemFactory : IUrlFactory public string Name => ConfigName; /// - public string? GetUrl(IGeneralContext context, string id) + public string? GetUrl(IPageContext context, string id) { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(id); + if (!context.Items.TryGetValue(id, out DocItem item)) { return null; @@ -38,10 +41,17 @@ public sealed class DocItemFactory : IUrlFactory } string url = context.GetFileName(pagedItem); + + if (context.GetUseFullUrl() && !string.IsNullOrWhiteSpace(context.Settings.LinksBaseUrl)) + { + url = context.Settings.LinksBaseUrl!.Trim('/') + '/' + url; + } + if (context.GetRemoveFileExtensionFromUrl() && Path.HasExtension(url)) { url = url.Substring(0, url.Length - Path.GetExtension(url).Length); } + if (item != pagedItem) { url += "#" + PathCleaner.Clean(item.FullName, context.GetInvalidCharReplacement()); diff --git a/source/DefaultDocumentation.Markdown/UrlFactories/DotnetApiFactory.cs b/source/DefaultDocumentation.Markdown/UrlFactories/DotnetApiFactory.cs index 200b7ce0..51f273b5 100644 --- a/source/DefaultDocumentation.Markdown/UrlFactories/DotnetApiFactory.cs +++ b/source/DefaultDocumentation.Markdown/UrlFactories/DotnetApiFactory.cs @@ -17,8 +17,11 @@ public sealed class DotnetApiFactory : IUrlFactory public string Name => ConfigName; /// - public string GetUrl(IGeneralContext context, string id) + public string GetUrl(IPageContext context, string id) { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(id); + id = id.Substring(2); int parametersIndex = id.IndexOf("(", StringComparison.Ordinal); if (parametersIndex > 0) diff --git a/source/DefaultDocumentation.Markdown/Writers/MarkdownWriter.cs b/source/DefaultDocumentation.Markdown/Writers/MarkdownWriter.cs index 16da589b..ec8aef3f 100644 --- a/source/DefaultDocumentation.Markdown/Writers/MarkdownWriter.cs +++ b/source/DefaultDocumentation.Markdown/Writers/MarkdownWriter.cs @@ -1,7 +1,6 @@ using System; using DefaultDocumentation.Api; using DefaultDocumentation.Markdown.Extensions; -using DefaultDocumentation.Models; namespace DefaultDocumentation.Markdown.Writers { @@ -25,10 +24,7 @@ public MarkdownWriter(IWriter writer) #region IWriter /// - public IGeneralContext Context => _writer.Context; - - /// - public DocItem DocItem => _writer.DocItem; + public IPageContext Context => _writer.Context; /// public int Length @@ -37,13 +33,6 @@ public int Length set => _writer.Length = value; } - /// - public object? this[string key] - { - get => _writer[key]; - set => _writer[key] = value; - } - /// public IWriter Append(string value) { diff --git a/source/DefaultDocumentation.Markdown/Writers/OverrideWriter.cs b/source/DefaultDocumentation.Markdown/Writers/OverrideWriter.cs index eab1f0d1..f7fbb270 100644 --- a/source/DefaultDocumentation.Markdown/Writers/OverrideWriter.cs +++ b/source/DefaultDocumentation.Markdown/Writers/OverrideWriter.cs @@ -17,8 +17,49 @@ private sealed class StringComparer : IEqualityComparer public int GetHashCode(string obj) => obj.ToUpperInvariant().GetHashCode(); } + private sealed class OverrideContext : IPageContext + { + private readonly IPageContext _context; + private readonly Dictionary _data; + + public OverrideContext(IPageContext context) + { + _context = context; + _data = new Dictionary(new StringComparer()); + } + + #region IPageContext + + public object? this[string key] + { + get => (_data.TryGetValue(key, out object? value) ? value : null) ?? _context[key]; + set => _data[key] = value; + } + + public DocItem DocItem => _context.DocItem; + + public ISettings Settings => _context.Settings; + + public IReadOnlyDictionary Items => _context.Items; + + public IReadOnlyDictionary Elements => _context.Elements; + + public IEnumerable UrlFactories => _context.UrlFactories; + + public IFileNameFactory? FileNameFactory => _context.FileNameFactory; + + public IEnumerable? Sections => _context.Sections; + + public IContext GetContext(Type? type) => _context.GetContext(type); + + public string GetFileName(DocItem item) => _context.GetFileName(item); + + public T? GetSetting(string name) => _context.GetSetting(name); + + #endregion + } + private readonly IWriter _writer; - private readonly Dictionary _data; /// /// Initializes a new instance of the type. @@ -26,17 +67,16 @@ private sealed class StringComparer : IEqualityComparer /// The instance to decorate. public OverrideWriter(IWriter writer) { + ArgumentNullException.ThrowIfNull(writer); + _writer = writer; - _data = new Dictionary(new StringComparer()); + Context = new OverrideContext(writer.Context); } #region IWriter /// - public IGeneralContext Context => _writer.Context; - - /// - public DocItem DocItem => _writer.DocItem; + public IPageContext Context { get; } /// public int Length @@ -45,13 +85,6 @@ public int Length set => _writer.Length = value; } - /// - public object? this[string key] - { - get => (_data.TryGetValue(key, out object? value) ? value : null) ?? _writer[key]; - set => _data[key] = value; - } - /// public IWriter Append(string value) { diff --git a/source/DefaultDocumentation.Markdown/Writers/PrefixedWriter.cs b/source/DefaultDocumentation.Markdown/Writers/PrefixedWriter.cs index 1bd08c90..981108c1 100644 --- a/source/DefaultDocumentation.Markdown/Writers/PrefixedWriter.cs +++ b/source/DefaultDocumentation.Markdown/Writers/PrefixedWriter.cs @@ -1,6 +1,5 @@ using System; using DefaultDocumentation.Api; -using DefaultDocumentation.Models; namespace DefaultDocumentation.Markdown.Writers { @@ -19,6 +18,9 @@ public sealed class PrefixedWriter : IWriter /// The prefix to use at every new line start. public PrefixedWriter(IWriter writer, string prefix) { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(prefix); + _writer = writer; _prefix = prefix; } @@ -26,10 +28,7 @@ public PrefixedWriter(IWriter writer, string prefix) #region IWriter /// - public IGeneralContext Context => _writer.Context; - - /// - public DocItem DocItem => _writer.DocItem; + public IPageContext Context => _writer.Context; /// public int Length @@ -38,13 +37,6 @@ public int Length set => _writer.Length = value; } - /// - public object? this[string key] - { - get => _writer[key]; - set => _writer[key] = value; - } - /// public IWriter Append(string value) { diff --git a/source/DefaultDocumentation.Test/AssemblyInfo.cs b/source/DefaultDocumentation.Test/AssemblyInfo.cs index baa1dd20..e44283ed 100644 --- a/source/DefaultDocumentation.Test/AssemblyInfo.cs +++ b/source/DefaultDocumentation.Test/AssemblyInfo.cs @@ -46,10 +46,14 @@ public interface IInterface event Action SecondEvent; } - private static class ClassWithTypeParameter - { } + private static class ClassWithTypeParameter; + + public sealed record ClassRecord( + int Property); + + public readonly record struct StructRecord; - private static event Action Event; + private static event Action? Event; private const int _constField = 42; @@ -57,9 +61,7 @@ private static class ClassWithTypeParameter private const char _constCharField = 'e'; -#pragma warning disable CS0649 private static readonly int _field; -#pragma warning restore CS0649 private static int Property { get; } @@ -74,7 +76,7 @@ private static void MethodWithGenericConstrains() where T5 : class? { } - public object Current { get; } + public object? Current { get; } public bool MoveNext() => false; @@ -83,7 +85,7 @@ public void Reset() int IInterface.Property { get; set; } - public event Action SecondEvent; + public event Action? SecondEvent; event Action IInterface.Event { @@ -106,7 +108,7 @@ void IInterface.Method() public static readonly AssemblyDocItem AssemblyDocItem = new("Test", "Test", null); - public static readonly NamespaceDocItem NamespaceDocItem = new(AssemblyDocItem, typeof(AssemblyInfo).Namespace, null); + public static readonly NamespaceDocItem NamespaceDocItem = new(AssemblyDocItem, typeof(AssemblyInfo).Namespace!, null); public static readonly ClassDocItem ClassDocItem = new(NamespaceDocItem, Get($"T:{typeof(AssemblyInfo).FullName}"), null); public static readonly EventDocItem EventDocItem = new(ClassDocItem, Get($"E:{typeof(AssemblyInfo).FullName}.{nameof(Event)}"), null); @@ -120,9 +122,9 @@ void IInterface.Method() public static readonly MethodDocItem MethodWithReturnDocItem = new(ClassDocItem, Get($"M:{typeof(AssemblyInfo).FullName}.{nameof(MoveNext)}"), null); public static readonly ConstructorDocItem ConstructorDocItem = new(ClassDocItem, Get($"M:{typeof(AssemblyInfo).FullName}.#ctor"), null); public static readonly OperatorDocItem OperatorDocItem = new(ClassDocItem, Get($"M:{typeof(AssemblyInfo).FullName}.op_Addition({typeof(AssemblyInfo).FullName},System.Int32)"), null); - public static readonly ExplicitInterfaceImplementationDocItem ExplicitEventDocItem = new(ClassDocItem, Get($"E:{typeof(AssemblyInfo).FullName}.{typeof(IInterface).FullName.Replace('.', '#').Replace('+', '#')}#{nameof(IInterface.Event)}"), null); - public static readonly ExplicitInterfaceImplementationDocItem ExplicitPropertyDocItem = new(ClassDocItem, Get($"P:{typeof(AssemblyInfo).FullName}.{typeof(IInterface).FullName.Replace('.', '#').Replace('+', '#')}#{nameof(IInterface.Property)}"), null); - public static readonly ExplicitInterfaceImplementationDocItem ExplicitMethodDocItem = new(ClassDocItem, Get($"M:{typeof(AssemblyInfo).FullName}.{typeof(IInterface).FullName.Replace('.', '#').Replace('+', '#')}#{nameof(IInterface.Method)}"), null); + public static readonly ExplicitInterfaceImplementationDocItem ExplicitEventDocItem = new(ClassDocItem, Get($"E:{typeof(AssemblyInfo).FullName}.{typeof(IInterface).FullName!.Replace('.', '#').Replace('+', '#')}#{nameof(IInterface.Event)}"), null); + public static readonly ExplicitInterfaceImplementationDocItem ExplicitPropertyDocItem = new(ClassDocItem, Get($"P:{typeof(AssemblyInfo).FullName}.{typeof(IInterface).FullName!.Replace('.', '#').Replace('+', '#')}#{nameof(IInterface.Property)}"), null); + public static readonly ExplicitInterfaceImplementationDocItem ExplicitMethodDocItem = new(ClassDocItem, Get($"M:{typeof(AssemblyInfo).FullName}.{typeof(IInterface).FullName!.Replace('.', '#').Replace('+', '#')}#{nameof(IInterface.Method)}"), null); public static readonly EnumDocItem EnumDocItem = new(ClassDocItem, Get($"T:{typeof(Enum).FullName}"), null); public static readonly EnumFieldDocItem EnumFieldDocItem = new(EnumDocItem, Get($"F:{typeof(Enum).FullName}.{nameof(Enum.Value)}"), null); @@ -139,5 +141,10 @@ void IInterface.Method() public static readonly InterfaceDocItem InterfaceDocItem = new(ClassDocItem, Get($"T:{typeof(IInterface).FullName}"), null); public static readonly MethodDocItem InterfaceMethodDocItem = new(InterfaceDocItem, Get($"M:{typeof(IInterface).FullName}.{nameof(IInterface.Method)}"), null); public static readonly EventDocItem InterfaceEventDocItem = new(InterfaceDocItem, Get($"E:{typeof(IInterface).FullName}.{nameof(IInterface.SecondEvent)}"), null); + + public static readonly ClassDocItem ClassRecordDocItem = new(NamespaceDocItem, Get($"T:{typeof(AssemblyInfo).FullName}.{nameof(ClassRecord)}"), null); + public static readonly PropertyDocItem RecordPropertyDocItem = new(ClassRecordDocItem, Get($"P:{typeof(AssemblyInfo).FullName}.{nameof(ClassRecord)}.{nameof(ClassRecord.Property)}"), null); + + public static readonly StructDocItem StructRecordDocItem = new(NamespaceDocItem, Get($"T:{typeof(AssemblyInfo).FullName}.{nameof(StructRecord)}"), null); } } diff --git a/source/DefaultDocumentation.Test/DefaultDocumentation.Test.csproj b/source/DefaultDocumentation.Test/DefaultDocumentation.Test.csproj index a2062f9f..a4ef9c92 100644 --- a/source/DefaultDocumentation.Test/DefaultDocumentation.Test.csproj +++ b/source/DefaultDocumentation.Test/DefaultDocumentation.Test.csproj @@ -1,20 +1,28 @@  - - net7.0 - DefaultDocumentation.Markdown - false - - - - - - - - - + + net8.0 + DefaultDocumentation + enable + false + + + + + + + + + + + + + + + + + + + - - - diff --git a/source/DefaultDocumentation.Test/Extensions/DocItemExtensionTest.cs b/source/DefaultDocumentation.Test/Extensions/DocItemExtensionsTest.cs similarity index 99% rename from source/DefaultDocumentation.Test/Extensions/DocItemExtensionTest.cs rename to source/DefaultDocumentation.Test/Extensions/DocItemExtensionsTest.cs index 7c28b699..0a66d580 100644 --- a/source/DefaultDocumentation.Test/Extensions/DocItemExtensionTest.cs +++ b/source/DefaultDocumentation.Test/Extensions/DocItemExtensionsTest.cs @@ -7,7 +7,7 @@ namespace DefaultDocumentation.Models { - public sealed class DocItemExtensionTest + public sealed class DocItemExtensionsTest { public static IEnumerable HasOwnPageData { diff --git a/source/DefaultDocumentation.Test/Extensions/IGeneralContextExtensionTest.cs b/source/DefaultDocumentation.Test/Extensions/IGeneralContextExtensionsTest.cs similarity index 96% rename from source/DefaultDocumentation.Test/Extensions/IGeneralContextExtensionTest.cs rename to source/DefaultDocumentation.Test/Extensions/IGeneralContextExtensionsTest.cs index 07ab6d2a..35046bdb 100644 --- a/source/DefaultDocumentation.Test/Extensions/IGeneralContextExtensionTest.cs +++ b/source/DefaultDocumentation.Test/Extensions/IGeneralContextExtensionsTest.cs @@ -5,7 +5,7 @@ namespace DefaultDocumentation { - public sealed class IGeneralContextExtensionTest + public sealed class IGeneralContextExtensionsTest { [Fact] public void GetContext_Should_return_GetContext_Type() diff --git a/source/DefaultDocumentation.Test/Markdown/AWriterTest.cs b/source/DefaultDocumentation.Test/Markdown/AWriterTest.cs index 83003e10..868978a3 100644 --- a/source/DefaultDocumentation.Test/Markdown/AWriterTest.cs +++ b/source/DefaultDocumentation.Test/Markdown/AWriterTest.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; using DefaultDocumentation.Api; +using DefaultDocumentation.Internal; using DefaultDocumentation.Models; using NLog; using NSubstitute; @@ -12,9 +12,9 @@ namespace DefaultDocumentation.Markdown { public abstract class AWriterTest { - private sealed class FileNameFactory : IFileNameFactory + private sealed class DummyFileNameFactory : IFileNameFactory { - public string Name { get; } + public string Name { get; } = "Dummy"; public void Clean(IGeneralContext context) { } @@ -22,17 +22,15 @@ public void Clean(IGeneralContext context) public string GetFileName(IGeneralContext context, DocItem item) => item.Name; } - private sealed class UrlFactory : IUrlFactory + private sealed class DummyUrlFactory : IUrlFactory { - public string Name { get; } + public string Name { get; } = "Dummy"; - public string GetUrl(IGeneralContext context, string id) => id; + public string GetUrl(IPageContext context, string id) => id; } private sealed class GeneralContext : IGeneralContext { - private IEnumerable _urlFactories; - public GeneralContext( ISettings settings, IFileNameFactory fileNameFactory, @@ -45,7 +43,7 @@ public GeneralContext( Items = items; Elements = elements; FileNameFactory = fileNameFactory; - _urlFactories = urlFactories; + UrlFactories = urlFactories; Sections = sections; } @@ -57,25 +55,25 @@ public GeneralContext( public IFileNameFactory FileNameFactory { get; } + public IEnumerable UrlFactories { get; } + public IEnumerable Sections { get; } - public IContext GetContext(Type type) => this; + public IContext GetContext(Type? type) => this; public string GetFileName(DocItem item) => FileNameFactory.GetFileName(this, item); - public T GetSetting(string name) => name switch + public T? GetSetting(string name) => name switch { "Markdown.NestedTypeVisibilities" => (T)(object)(NestedTypeVisibilities.Namespace | NestedTypeVisibilities.DeclaringType), _ => default }; - - public string GetUrl(string id) => _urlFactories.Select(f => f.GetUrl(this, id)).FirstOrDefault(url => url is not null) ?? ""; } protected readonly StringBuilder _builder; protected readonly DocItem _docItem; protected readonly Lazy _settings; - protected readonly Lazy _context; + protected readonly Lazy _context; protected AWriterTest() { @@ -92,22 +90,24 @@ protected AWriterTest() return settings; }); - _context = new Lazy(() => new GeneralContext( - _settings.Value, - GetFileNameFactory(), - GetUrlFactories(), - GetItems(), - GetElements(), - GetSections())); + _context = new Lazy(() => new PageContext( + new GeneralContext( + _settings.Value, + GetFileNameFactory(), + GetUrlFactories(), + GetItems(), + GetElements(), + GetSections()), + _docItem)); } protected virtual GeneratedPages GetGeneratedPages() => GeneratedPages.Assembly | GeneratedPages.Namespaces | GeneratedPages.Types | GeneratedPages.Members; - protected virtual IFileNameFactory GetFileNameFactory() => new FileNameFactory(); + protected virtual IFileNameFactory GetFileNameFactory() => new DummyFileNameFactory(); protected virtual IUrlFactory[] GetUrlFactories() => new IUrlFactory[] { - new UrlFactory() + new DummyUrlFactory() }; protected virtual ISection[] GetSections() => Array.Empty(); diff --git a/source/DefaultDocumentation.Test/Markdown/Elements/AElementTest.cs b/source/DefaultDocumentation.Test/Markdown/Elements/AElementTest.cs index 7ebb3eba..19fc07ec 100644 --- a/source/DefaultDocumentation.Test/Markdown/Elements/AElementTest.cs +++ b/source/DefaultDocumentation.Test/Markdown/Elements/AElementTest.cs @@ -1,7 +1,8 @@ using System; using System.Xml.Linq; -using DefaultDocumentation.Models; using DefaultDocumentation.Api; +using DefaultDocumentation.Internal; +using DefaultDocumentation.Models; using NFluent; namespace DefaultDocumentation.Markdown.Elements @@ -18,20 +19,20 @@ protected AElementTest() _elementWriter = new T(); } - protected void Test(DocItem item, Func initializer, XElement input, string expectedOutput) + protected void Test(DocItem item, Func initializer, XElement input, string? expectedOutput) { _builder.Clear(); - IWriter writer = initializer(new PageWriter(_builder, _context.Value, item)); + IWriter writer = initializer(new PageWriter(_builder, new PageContext(_context.Value, item))); _elementWriter.Write(writer, input); Check.That(_builder.ToString()).IsEqualTo(expectedOutput); } - protected void Test(Func initializer, XElement input, string expectedOutput) => Test(_docItem, initializer, input, expectedOutput); + protected void Test(Func initializer, XElement input, string? expectedOutput) => Test(_docItem, initializer, input, expectedOutput); - protected void Test(DocItem item, XElement input, string expectedOutput) => Test(item, static w => w, input, expectedOutput); + protected void Test(DocItem item, XElement input, string? expectedOutput) => Test(item, static w => w, input, expectedOutput); - protected void Test(XElement input, string expectedOutput) => Test(_docItem, input, expectedOutput); + protected void Test(XElement input, string? expectedOutput) => Test(_docItem, input, expectedOutput); } } diff --git a/source/DefaultDocumentation.Test/Markdown/Elements/CodeElementTest.cs b/source/DefaultDocumentation.Test/Markdown/Elements/CodeElementTest.cs index 3a8c993e..9e6eff09 100644 --- a/source/DefaultDocumentation.Test/Markdown/Elements/CodeElementTest.cs +++ b/source/DefaultDocumentation.Test/Markdown/Elements/CodeElementTest.cs @@ -157,7 +157,7 @@ public void Write_should_throw_When_source_does_not_exist() [Fact] public void Write_should_write_from_source_When_attribute_present() { - using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory.FullName, Guid.NewGuid().ToString("N")), "test"); + using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory!.FullName, Guid.NewGuid().ToString("N")), "test"); Test( new XElement("code", new XAttribute("source", file.Info.FullName)), @@ -169,7 +169,7 @@ public void Write_should_write_from_source_When_attribute_present() [Fact] public void Write_should_write_from_relative_source_When_attribute_present() { - using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory.FullName, Guid.NewGuid().ToString("N")), "test"); + using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory!.FullName, Guid.NewGuid().ToString("N")), "test"); Test( new XElement("code", new XAttribute("source", file.Info.Name)), @@ -181,7 +181,7 @@ public void Write_should_write_from_relative_source_When_attribute_present() [Fact] public void Write_should_throw_When_region_does_not_exist() { - using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory.FullName, Guid.NewGuid().ToString("N")), SingleRegion); + using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory!.FullName, Guid.NewGuid().ToString("N")), SingleRegion); Check .ThatCode(() => Test(new XElement("code", new XAttribute("source", file.Info.FullName), new XAttribute("region", "bar")), null)) @@ -191,7 +191,7 @@ public void Write_should_throw_When_region_does_not_exist() [Fact] public void Write_should_write_region_When_attribute_present() { - using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory.FullName, Guid.NewGuid().ToString("N")), SingleRegion); + using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory!.FullName, Guid.NewGuid().ToString("N")), SingleRegion); Test( new XElement("code", new XAttribute("source", file.Info.FullName), new XAttribute("region", "Foo")), @@ -203,7 +203,7 @@ public void Write_should_write_region_When_attribute_present() [Fact] public void Write_should_write_region_When_attribute_present_and_inside_an_other_region() { - using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory.FullName, Guid.NewGuid().ToString("N")), DoubleRegion); + using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory!.FullName, Guid.NewGuid().ToString("N")), DoubleRegion); Test( new XElement("code", new XAttribute("source", file.Info.FullName), new XAttribute("region", "The Bar Region")), @@ -215,7 +215,7 @@ public void Write_should_write_region_When_attribute_present_and_inside_an_other [Fact] public void Write_should_write_region_When_attribute_present_and_contains_an_other_region() { - using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory.FullName, Guid.NewGuid().ToString("N")), DoubleRegion); + using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory!.FullName, Guid.NewGuid().ToString("N")), DoubleRegion); Test( new XElement("code", new XAttribute("source", file.Info.FullName), new XAttribute("region", "Foo")), @@ -227,7 +227,7 @@ public void Write_should_write_region_When_attribute_present_and_contains_an_oth [Fact] public void Write_should_write_region_When_attribute_present_and_in_comment() { - using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory.FullName, Guid.NewGuid().ToString("N")), RegionInComment); + using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory!.FullName, Guid.NewGuid().ToString("N")), RegionInComment); Test( new XElement("code", new XAttribute("source", file.Info.FullName), new XAttribute("region", "Foo")), @@ -239,7 +239,7 @@ public void Write_should_write_region_When_attribute_present_and_in_comment() [Fact] public void Write_should_throw_When_no_end_region() { - using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory.FullName, Guid.NewGuid().ToString("N")), NoEndRegion); + using TempFile file = new(Path.Combine(_settings.Value.ProjectDirectory!.FullName, Guid.NewGuid().ToString("N")), NoEndRegion); Check .ThatCode(() => Test(new XElement("code", new XAttribute("source", file.Info.FullName), new XAttribute("region", "Foo")), null)) diff --git a/source/DefaultDocumentation.Test/Markdown/Sections/ASectionTest.cs b/source/DefaultDocumentation.Test/Markdown/Sections/ASectionTest.cs index bddce63a..bfa69404 100644 --- a/source/DefaultDocumentation.Test/Markdown/Sections/ASectionTest.cs +++ b/source/DefaultDocumentation.Test/Markdown/Sections/ASectionTest.cs @@ -1,6 +1,7 @@ using System; -using DefaultDocumentation.Models; using DefaultDocumentation.Api; +using DefaultDocumentation.Internal; +using DefaultDocumentation.Models; using NFluent; namespace DefaultDocumentation.Markdown.Sections @@ -20,7 +21,7 @@ protected ASectionTest() protected void Test(DocItem item, Func initializer, string expectedOutput) { _builder.Clear(); - IWriter writer = initializer(new PageWriter(_builder, _context.Value, item)); + IWriter writer = initializer(new PageWriter(_builder, new PageContext(_context.Value, item))); _sectionWriter.Write(writer); diff --git a/source/DefaultDocumentation.Test/Markdown/Sections/DefinitionSectionTest.cs b/source/DefaultDocumentation.Test/Markdown/Sections/DefinitionSectionTest.cs index 934fa9aa..3fb6f057 100644 --- a/source/DefaultDocumentation.Test/Markdown/Sections/DefinitionSectionTest.cs +++ b/source/DefaultDocumentation.Test/Markdown/Sections/DefinitionSectionTest.cs @@ -18,9 +18,7 @@ public void Write_should_not_write_When_no_IDefinedDocItem() => Test( public void Write_should_write_When_TypeDocItem() => Test( AssemblyInfo.ClassDocItem, @"```csharp -public sealed class AssemblyInfo : -DefaultDocumentation.AssemblyInfo.IInterface, -System.Collections.IEnumerator +public sealed class AssemblyInfo : DefaultDocumentation.AssemblyInfo.IInterface, System.Collections.IEnumerator ```"); [Fact] @@ -30,9 +28,7 @@ public void Write_should_write_newline_When_needed() => Test( @"pouet ```csharp -public sealed class AssemblyInfo : -DefaultDocumentation.AssemblyInfo.IInterface, -System.Collections.IEnumerator +public sealed class AssemblyInfo : DefaultDocumentation.AssemblyInfo.IInterface, System.Collections.IEnumerator ```"); [Fact] @@ -74,7 +70,7 @@ public void Write_should_write_When_PropertyDocItem() => Test( public void Write_should_write_When_EventDocItem() => Test( AssemblyInfo.EventDocItem, @"```csharp -private static event Action Event; +private static event Action? Event; ```"); [Fact] @@ -150,6 +146,27 @@ public void Write_should_write_When_TypeDocItem_and_implement() => Test( new ClassDocItem(AssemblyInfo.NamespaceDocItem, AssemblyInfo.Get($"T:{typeof(DefinitionSectionTest).FullName}"), null), @"```csharp public sealed class DefinitionSectionTest : DefaultDocumentation.Markdown.Sections.ASectionTest +```"); + + [Fact] + public void Write_should_write_When_TypeDocItem_for_record_class() => Test( + AssemblyInfo.ClassRecordDocItem, +@"```csharp +public sealed record AssemblyInfo.ClassRecord : System.IEquatable +```"); + + [Fact] + public void Write_should_write_When_TypeDocItem_for_record_struct() => Test( + AssemblyInfo.StructRecordDocItem, +@"```csharp +public readonly record struct AssemblyInfo.StructRecord : System.IEquatable +```"); + + //[Fact] to uncomment once https://github.com/icsharpcode/ILSpy/issues/3159 is solved + public void Write_should_write_When_PropertyDocItem_with_init() => Test( + AssemblyInfo.RecordPropertyDocItem, +@"```csharp +public int Property { get; init; } ```"); } } diff --git a/source/DefaultDocumentation.Test/Markdown/Sections/EventTypeSectionTest.cs b/source/DefaultDocumentation.Test/Markdown/Sections/EventTypeSectionTest.cs index 7e2301b5..e0d6f750 100644 --- a/source/DefaultDocumentation.Test/Markdown/Sections/EventTypeSectionTest.cs +++ b/source/DefaultDocumentation.Test/Markdown/Sections/EventTypeSectionTest.cs @@ -1,5 +1,4 @@ -using DefaultDocumentation.Models; -using NFluent; +using NFluent; using Xunit; namespace DefaultDocumentation.Markdown.Sections @@ -11,7 +10,7 @@ public sealed class EventTypeSectionTest : ASectionTest [Fact] public void Write_should_not_write_When_not_EventDocItem() => Test( - default(DocItem), + AssemblyInfo.AssemblyDocItem, string.Empty); [Fact] diff --git a/source/DefaultDocumentation.Test/Markdown/Sections/FieldValueSectionTest.cs b/source/DefaultDocumentation.Test/Markdown/Sections/FieldValueSectionTest.cs index f22f45f4..23551426 100644 --- a/source/DefaultDocumentation.Test/Markdown/Sections/FieldValueSectionTest.cs +++ b/source/DefaultDocumentation.Test/Markdown/Sections/FieldValueSectionTest.cs @@ -1,5 +1,4 @@ -using DefaultDocumentation.Models; -using NFluent; +using NFluent; using Xunit; namespace DefaultDocumentation.Markdown.Sections @@ -11,7 +10,7 @@ public sealed class FieldValueSectionTest : ASectionTest [Fact] public void Write_should_not_write_When_not_FieldDocItem() => Test( - default(DocItem), + AssemblyInfo.AssemblyDocItem, string.Empty); [Fact] diff --git a/source/DefaultDocumentation.Test/Markdown/Sections/ParametersSectionTest.cs b/source/DefaultDocumentation.Test/Markdown/Sections/ParametersSectionTest.cs index 88d1c966..02f57ba3 100644 --- a/source/DefaultDocumentation.Test/Markdown/Sections/ParametersSectionTest.cs +++ b/source/DefaultDocumentation.Test/Markdown/Sections/ParametersSectionTest.cs @@ -16,15 +16,9 @@ protected override IReadOnlyDictionary GetItems() => .Concat(Enumerable.Repeat(AssemblyInfo.ClassDocItem, 1)) .ToDictionary(i => i.Id); - protected override IUrlFactory[] GetUrlFactories() => new IUrlFactory[] - { - new DocItemFactory() - }; - - protected override ISection[] GetSections() => new ISection[] - { - new TitleSection() - }; + protected override IUrlFactory[] GetUrlFactories() => [new DocItemFactory()]; + + protected override ISection[] GetSections() => [new TitleSection()]; [Fact] public void Name_should_be_Parameters() => Check.That(Name).IsEqualTo("Parameters"); diff --git a/source/DefaultDocumentation.Test/Markdown/Sections/ReturnsSectionTest.cs b/source/DefaultDocumentation.Test/Markdown/Sections/ReturnsSectionTest.cs index aedd19e7..540c6ca2 100644 --- a/source/DefaultDocumentation.Test/Markdown/Sections/ReturnsSectionTest.cs +++ b/source/DefaultDocumentation.Test/Markdown/Sections/ReturnsSectionTest.cs @@ -1,5 +1,4 @@ using System.Xml.Linq; -using DefaultDocumentation.Models; using DefaultDocumentation.Models.Members; using NFluent; using Xunit; @@ -13,7 +12,7 @@ public sealed class ReturnsSectionTest : ASectionTest [Fact] public void Write_should_not_write_When_not_correct_DocItem() => Test( - default(DocItem), + AssemblyInfo.AssemblyDocItem, string.Empty); [Fact] diff --git a/source/DefaultDocumentation.Test/Markdown/Sections/TitleSectionTest.cs b/source/DefaultDocumentation.Test/Markdown/Sections/TitleSectionTest.cs index 2242071a..9347bce3 100644 --- a/source/DefaultDocumentation.Test/Markdown/Sections/TitleSectionTest.cs +++ b/source/DefaultDocumentation.Test/Markdown/Sections/TitleSectionTest.cs @@ -10,10 +10,7 @@ namespace DefaultDocumentation.Markdown.Sections { public sealed class TitleSectionTest : ASectionTest { - protected override IUrlFactory[] GetUrlFactories() => new IUrlFactory[] - { - new DocItemFactory() - }; + protected override IUrlFactory[] GetUrlFactories() => [new DocItemFactory()]; protected override IReadOnlyDictionary GetItems() => AssemblyInfo.MethodWithParameterDocItem.Parameters diff --git a/source/DefaultDocumentation.Test/Markdown/Sections/ValueSectionTest.cs b/source/DefaultDocumentation.Test/Markdown/Sections/ValueSectionTest.cs index 1b76485b..265fe804 100644 --- a/source/DefaultDocumentation.Test/Markdown/Sections/ValueSectionTest.cs +++ b/source/DefaultDocumentation.Test/Markdown/Sections/ValueSectionTest.cs @@ -1,5 +1,4 @@ using System.Xml.Linq; -using DefaultDocumentation.Models; using DefaultDocumentation.Models.Members; using NFluent; using Xunit; @@ -13,7 +12,7 @@ public sealed class ValueSectionTest : ASectionTest [Fact] public void Write_should_not_write_When_not_PropertyDocItem() => Test( - default(DocItem), + AssemblyInfo.AssemblyDocItem, string.Empty); [Fact] diff --git a/source/DefaultDocumentation.Test/Models/DocItemTest.cs b/source/DefaultDocumentation.Test/Models/DocItemTest.cs index 5fb113b7..c98fb1aa 100644 --- a/source/DefaultDocumentation.Test/Models/DocItemTest.cs +++ b/source/DefaultDocumentation.Test/Models/DocItemTest.cs @@ -7,6 +7,6 @@ namespace DefaultDocumentation.Models public sealed class DocItemTest { [Fact] - public void DocItem_Should_throw_When_fullName_is_null() => Check.ThatCode(() => new AssemblyDocItem(null, string.Empty, null)).Throws(); + public void DocItem_Should_throw_When_fullName_is_null() => Check.ThatCode(() => new AssemblyDocItem(null!, string.Empty, null)).Throws(); } } diff --git a/source/DefaultDocumentation.Test/Models/ExternDocItemTest.cs b/source/DefaultDocumentation.Test/Models/ExternDocItemTest.cs index c2996d40..aab1106e 100644 --- a/source/DefaultDocumentation.Test/Models/ExternDocItemTest.cs +++ b/source/DefaultDocumentation.Test/Models/ExternDocItemTest.cs @@ -7,7 +7,7 @@ namespace DefaultDocumentation.Models public sealed class ExternDocItemTest { [Fact] - public void ExternDocItem_Should_throw_When_url_is_null() => Check.ThatCode(() => new ExternDocItem("T:id", null, null)).Throws(); + public void ExternDocItem_Should_throw_When_url_is_null() => Check.ThatCode(() => new ExternDocItem("T:id", null!, null)).Throws(); [Fact] public void Url_Should_return_url() => Check.That(new ExternDocItem("T:id", "test", null).Url).IsEqualTo("test"); diff --git a/source/DefaultDocumentation.Test/Models/Members/ConstructorDocItemTest.cs b/source/DefaultDocumentation.Test/Models/Members/ConstructorDocItemTest.cs index 7b96eab5..56eb3718 100644 --- a/source/DefaultDocumentation.Test/Models/Members/ConstructorDocItemTest.cs +++ b/source/DefaultDocumentation.Test/Models/Members/ConstructorDocItemTest.cs @@ -9,13 +9,13 @@ public sealed class ConstructorDocItemTest [Fact] public void ConstructorDocItem_Should_throw_When_parent_is_null() { - Check.ThatCode(() => new ConstructorDocItem(null, null, null)).Throws(); + Check.ThatCode(() => new ConstructorDocItem(null!, null!, null)).Throws(); } [Fact] public void ConstructorDocItem_Should_throw_When_method_is_null() { - Check.ThatCode(() => new ConstructorDocItem(AssemblyInfo.ClassDocItem, null, null)).Throws(); + Check.ThatCode(() => new ConstructorDocItem(AssemblyInfo.ClassDocItem, null!, null)).Throws(); } } } diff --git a/source/DefaultDocumentation.Test/Models/Members/EnumFieldDocItemTest.cs b/source/DefaultDocumentation.Test/Models/Members/EnumFieldDocItemTest.cs index 1cc4750d..928b7d8e 100644 --- a/source/DefaultDocumentation.Test/Models/Members/EnumFieldDocItemTest.cs +++ b/source/DefaultDocumentation.Test/Models/Members/EnumFieldDocItemTest.cs @@ -9,13 +9,13 @@ public sealed class EnumFieldDocItemTest [Fact] public void EnumFieldDocItem_Should_throw_When_parent_is_null() { - Check.ThatCode(() => new EnumFieldDocItem(null, null, null)).Throws(); + Check.ThatCode(() => new EnumFieldDocItem(null!, null!, null)).Throws(); } [Fact] public void EnumFieldDocItem_Should_throw_When_field_is_null() { - Check.ThatCode(() => new EnumFieldDocItem(AssemblyInfo.EnumDocItem, null, null)).Throws(); + Check.ThatCode(() => new EnumFieldDocItem(AssemblyInfo.EnumDocItem, null!, null)).Throws(); } } } diff --git a/source/DefaultDocumentation.Test/Models/Members/EventDocItemTest.cs b/source/DefaultDocumentation.Test/Models/Members/EventDocItemTest.cs index b296509d..c7ab1aed 100644 --- a/source/DefaultDocumentation.Test/Models/Members/EventDocItemTest.cs +++ b/source/DefaultDocumentation.Test/Models/Members/EventDocItemTest.cs @@ -9,13 +9,13 @@ public sealed class EventDocItemTest [Fact] public void EventDocItem_Should_throw_When_parent_is_null() { - Check.ThatCode(() => new EventDocItem(null, null, null)).Throws(); + Check.ThatCode(() => new EventDocItem(null!, null!, null)).Throws(); } [Fact] public void EventDocItem_Should_throw_When_event_is_null() { - Check.ThatCode(() => new EventDocItem(AssemblyInfo.ClassDocItem, null, null)).Throws(); + Check.ThatCode(() => new EventDocItem(AssemblyInfo.ClassDocItem, null!, null)).Throws(); } } } diff --git a/source/DefaultDocumentation.Test/Models/Members/ExplicitInterfaceImplementationDocItemTest.cs b/source/DefaultDocumentation.Test/Models/Members/ExplicitInterfaceImplementationDocItemTest.cs index b489fff1..ff8c72f8 100644 --- a/source/DefaultDocumentation.Test/Models/Members/ExplicitInterfaceImplementationDocItemTest.cs +++ b/source/DefaultDocumentation.Test/Models/Members/ExplicitInterfaceImplementationDocItemTest.cs @@ -10,37 +10,37 @@ public sealed class ExplicitInterfaceImplementationDocItemTest [Fact] public void ExplicitInterfaceImplementationDocItem_Should_throw_When_parent_is_null_for_property() { - Check.ThatCode(() => new ExplicitInterfaceImplementationDocItem(null, default(IProperty), null)).Throws(); + Check.ThatCode(() => new ExplicitInterfaceImplementationDocItem(null!, default(IProperty)!, null)).Throws(); } [Fact] public void ExplicitInterfaceImplementationDocItem_Should_throw_When_method_is_null_for_property() { - Check.ThatCode(() => new ExplicitInterfaceImplementationDocItem(AssemblyInfo.ClassDocItem, default(IProperty), null)).Throws(); + Check.ThatCode(() => new ExplicitInterfaceImplementationDocItem(AssemblyInfo.ClassDocItem, default(IProperty)!, null)).Throws(); } [Fact] public void ExplicitInterfaceImplementationDocItem_Should_throw_When_parent_is_null_for_method() { - Check.ThatCode(() => new ExplicitInterfaceImplementationDocItem(null, default(IMethod), null)).Throws(); + Check.ThatCode(() => new ExplicitInterfaceImplementationDocItem(null!, default(IMethod)!, null)).Throws(); } [Fact] public void ExplicitInterfaceImplementationDocItem_Should_throw_When_method_is_null_for_method() { - Check.ThatCode(() => new ExplicitInterfaceImplementationDocItem(AssemblyInfo.ClassDocItem, default(IMethod), null)).Throws(); + Check.ThatCode(() => new ExplicitInterfaceImplementationDocItem(AssemblyInfo.ClassDocItem, default(IMethod)!, null)).Throws(); } [Fact] public void ExplicitInterfaceImplementationDocItem_Should_throw_When_parent_is_null_for_event() { - Check.ThatCode(() => new ExplicitInterfaceImplementationDocItem(null, default(IEvent), null)).Throws(); + Check.ThatCode(() => new ExplicitInterfaceImplementationDocItem(null!, default(IEvent)!, null)).Throws(); } [Fact] public void ExplicitInterfaceImplementationDocItem_Should_throw_When_method_is_null_for_event() { - Check.ThatCode(() => new ExplicitInterfaceImplementationDocItem(AssemblyInfo.ClassDocItem, default(IEvent), null)).Throws(); + Check.ThatCode(() => new ExplicitInterfaceImplementationDocItem(AssemblyInfo.ClassDocItem, default(IEvent)!, null)).Throws(); } } } diff --git a/source/DefaultDocumentation.Test/Models/Members/FieldDocItemTest.cs b/source/DefaultDocumentation.Test/Models/Members/FieldDocItemTest.cs index 7d6a167c..6dd39b8b 100644 --- a/source/DefaultDocumentation.Test/Models/Members/FieldDocItemTest.cs +++ b/source/DefaultDocumentation.Test/Models/Members/FieldDocItemTest.cs @@ -9,13 +9,13 @@ public sealed class FieldDocItemTest [Fact] public void FieldDocItem_Should_throw_When_parent_is_null() { - Check.ThatCode(() => new FieldDocItem(null, null, null)).Throws(); + Check.ThatCode(() => new FieldDocItem(null!, null!, null)).Throws(); } [Fact] public void FieldDocItem_Should_throw_When_field_is_null() { - Check.ThatCode(() => new FieldDocItem(AssemblyInfo.ClassDocItem, null, null)).Throws(); + Check.ThatCode(() => new FieldDocItem(AssemblyInfo.ClassDocItem, null!, null)).Throws(); } } } diff --git a/source/DefaultDocumentation.Test/Models/Members/MethodDocItemTest.cs b/source/DefaultDocumentation.Test/Models/Members/MethodDocItemTest.cs index 4630bb4e..2312c7b3 100644 --- a/source/DefaultDocumentation.Test/Models/Members/MethodDocItemTest.cs +++ b/source/DefaultDocumentation.Test/Models/Members/MethodDocItemTest.cs @@ -9,13 +9,13 @@ public sealed class EventDMethodDocItemTestcItemTest [Fact] public void MethodDocItem_Should_throw_When_parent_is_null() { - Check.ThatCode(() => new MethodDocItem(null, null, null)).Throws(); + Check.ThatCode(() => new MethodDocItem(null!, null!, null)).Throws(); } [Fact] public void MethodDocItem_Should_throw_When_event_is_null() { - Check.ThatCode(() => new MethodDocItem(AssemblyInfo.ClassDocItem, null, null)).Throws(); + Check.ThatCode(() => new MethodDocItem(AssemblyInfo.ClassDocItem, null!, null)).Throws(); } } } diff --git a/source/DefaultDocumentation.Test/Models/Members/OperatorDocItemTest.cs b/source/DefaultDocumentation.Test/Models/Members/OperatorDocItemTest.cs index 215ae51e..148a59c0 100644 --- a/source/DefaultDocumentation.Test/Models/Members/OperatorDocItemTest.cs +++ b/source/DefaultDocumentation.Test/Models/Members/OperatorDocItemTest.cs @@ -9,13 +9,13 @@ public sealed class OperatorDocItemTest [Fact] public void OperatorDocItem_Should_throw_When_parent_is_null() { - Check.ThatCode(() => new OperatorDocItem(null, null, null)).Throws(); + Check.ThatCode(() => new OperatorDocItem(null!, null!, null)).Throws(); } [Fact] public void OperatorDocItem_Should_throw_When_event_is_null() { - Check.ThatCode(() => new OperatorDocItem(AssemblyInfo.ClassDocItem, null, null)).Throws(); + Check.ThatCode(() => new OperatorDocItem(AssemblyInfo.ClassDocItem, null!, null)).Throws(); } } } diff --git a/source/DefaultDocumentation.Test/Models/Members/PropertyDocItemTest.cs b/source/DefaultDocumentation.Test/Models/Members/PropertyDocItemTest.cs index 05e5f91e..d17ffcbf 100644 --- a/source/DefaultDocumentation.Test/Models/Members/PropertyDocItemTest.cs +++ b/source/DefaultDocumentation.Test/Models/Members/PropertyDocItemTest.cs @@ -9,13 +9,13 @@ public sealed class PropertyDocItemTest [Fact] public void PropertyDocItem_Should_throw_When_parent_is_null() { - Check.ThatCode(() => new PropertyDocItem(null, null, null)).Throws(); + Check.ThatCode(() => new PropertyDocItem(null!, null!, null)).Throws(); } [Fact] public void PropertyDocItem_Should_throw_When_event_is_null() { - Check.ThatCode(() => new PropertyDocItem(AssemblyInfo.ClassDocItem, null, null)).Throws(); + Check.ThatCode(() => new PropertyDocItem(AssemblyInfo.ClassDocItem, null!, null)).Throws(); } } } diff --git a/source/DefaultDocumentation.Test/Models/NamespaceDocItemTest.cs b/source/DefaultDocumentation.Test/Models/NamespaceDocItemTest.cs index 8cbb2775..5cdf3e9b 100644 --- a/source/DefaultDocumentation.Test/Models/NamespaceDocItemTest.cs +++ b/source/DefaultDocumentation.Test/Models/NamespaceDocItemTest.cs @@ -7,6 +7,6 @@ namespace DefaultDocumentation.Models public sealed class NamespaceDocItemTest { [Fact] - public void NamespaceDocItem_Should_throw_When_parent_is_null() => Check.ThatCode(() => new NamespaceDocItem(null, null, null)).Throws(); + public void NamespaceDocItem_Should_throw_When_parent_is_null() => Check.ThatCode(() => new NamespaceDocItem(null!, null!, null)).Throws(); } } diff --git a/source/DefaultDocumentation.Test/Models/Types/TypeDocItemTest.cs b/source/DefaultDocumentation.Test/Models/Types/TypeDocItemTest.cs index b121467f..88db3c1a 100644 --- a/source/DefaultDocumentation.Test/Models/Types/TypeDocItemTest.cs +++ b/source/DefaultDocumentation.Test/Models/Types/TypeDocItemTest.cs @@ -9,13 +9,13 @@ public sealed class TypeDocItemTest [Fact] public void TypeDocItem_Should_throw_When_parent_is_null() { - Check.ThatCode(() => new ClassDocItem(null, null, null)).Throws(); + Check.ThatCode(() => new ClassDocItem(null!, null!, null)).Throws(); } [Fact] public void TypeDocItem_Should_throw_When_type_is_null() { - Check.ThatCode(() => new ClassDocItem(AssemblyInfo.NamespaceDocItem, null, null)).Throws(); + Check.ThatCode(() => new ClassDocItem(AssemblyInfo.NamespaceDocItem, null!, null)).Throws(); } } } diff --git a/source/DefaultDocumentation.Test/PageWriter.cs b/source/DefaultDocumentation.Test/PageWriter.cs deleted file mode 100644 index 8bdf10ce..00000000 --- a/source/DefaultDocumentation.Test/PageWriter.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Collections.Generic; -using System.Text; -using DefaultDocumentation.Models; -using DefaultDocumentation.Api; - -namespace DefaultDocumentation.Markdown -{ - public sealed class PageWriter : IWriter - { - private readonly StringBuilder _builder; - private readonly Dictionary _data; - - public PageWriter(StringBuilder builder, IGeneralContext context, DocItem item) - { - _builder = builder; - _data = new Dictionary(); - - Context = context; - DocItem = item; - } - - #region IWriter - - public IGeneralContext Context { get; } - - public DocItem DocItem { get; } - - public int Length - { - get => _builder.Length; - set => _builder.Length = value; - } - - public object this[string key] - { - get => _data.TryGetValue(key, out object value) ? value : null; - set => _data[key] = value; - } - - public IWriter Append(string value) - { - _builder.Append(value); - - return this; - } - - public IWriter AppendLine() - { - if (Length > 0) - { - _builder.AppendLine(); - } - - return this; - } - - public bool EndsWith(string value) - { - if (_builder.Length < value.Length) - { - return false; - } - - for (int i = 0; i < value.Length; ++i) - { - if (value[i] != _builder[_builder.Length - value.Length + i]) - { - return false; - } - } - - return true; - } - - #endregion - } -} diff --git a/source/DefaultDocumentation/DefaultDocumentation.csproj b/source/DefaultDocumentation/DefaultDocumentation.csproj index d05eebe3..f908ee6a 100644 --- a/source/DefaultDocumentation/DefaultDocumentation.csproj +++ b/source/DefaultDocumentation/DefaultDocumentation.csproj @@ -1,34 +1,36 @@  - - Create a simple markdown documentation from the Visual Studio xml one. - netstandard2.0 - true - build - false - true - true - $(TargetsForTfmSpecificContentInPackage);PackBuildOutputs - - - - + + Create a simple markdown documentation from the Visual Studio xml one. + netstandard2.0 + true + build + false + true + true + $(TargetsForTfmSpecificContentInPackage);PackBuildOutputs + - - - + + + - - - + + + + + + + + + + + + + + + + + - - - - - - - - - diff --git a/source/Directory.Build.props b/source/Directory.Build.props index 111db77a..43d66276 100644 --- a/source/Directory.Build.props +++ b/source/Directory.Build.props @@ -1,4 +1,5 @@ + latest true @@ -36,6 +37,7 @@ + @@ -48,9 +50,10 @@ - - - - + + + + + diff --git a/source/Sample/DefaultDocumentation.PluginExample/DefaultDocumentation.PluginExample.csproj b/source/Sample/DefaultDocumentation.PluginExample/DefaultDocumentation.PluginExample.csproj index e5e8d9ac..e6a4c73b 100644 --- a/source/Sample/DefaultDocumentation.PluginExample/DefaultDocumentation.PluginExample.csproj +++ b/source/Sample/DefaultDocumentation.PluginExample/DefaultDocumentation.PluginExample.csproj @@ -1,16 +1,24 @@  - - netstandard2.0 - true - true - false - - - - + + netstandard2.0 + true + true + false + enable + false + + + + + + + + + + + + + - - - diff --git a/source/Sample/DefaultDocumentation.PluginExample/FolderFileNameFactory.cs b/source/Sample/DefaultDocumentation.PluginExample/FolderFileNameFactory.cs index 06faf6e5..faff826c 100644 --- a/source/Sample/DefaultDocumentation.PluginExample/FolderFileNameFactory.cs +++ b/source/Sample/DefaultDocumentation.PluginExample/FolderFileNameFactory.cs @@ -14,14 +14,19 @@ public sealed class FolderFileNameFactory : IFileNameFactory private const string InvalidCharReplacementKey = "Markdown.InvalidCharReplacement"; - public static string GetInvalidCharReplacement(IGeneralContext context) => context.GetSetting(InvalidCharReplacementKey); + private static string? GetInvalidCharReplacement(IGeneralContext context) + { + ArgumentNullException.ThrowIfNull(context); + + return context.GetSetting(InvalidCharReplacementKey); + } private static class PathCleaner { private static readonly string[] toTrimChars = new[] { '=', ' ' }.Select(c => $"{c}").ToArray(); private static readonly string[] invalidChars = new[] { '\"', '<', '>', ':', '*', '?' }.Concat(Path.GetInvalidPathChars()).Select(c => $"{c}").ToArray(); - public static string Clean(string value, string invalidCharReplacement) + public static string Clean(string value, string? invalidCharReplacement) { foreach (string toTrimChar in toTrimChars) { @@ -43,8 +48,11 @@ public static string Clean(string value, string invalidCharReplacement) public string Name => "Folder"; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1851:Possible multiple enumerations of 'IEnumerable' collection", Justification = "Expected")] public void Clean(IGeneralContext context) { + ArgumentNullException.ThrowIfNull(context); + context.Settings.Logger.Debug($"Cleaning output folder \"{context.Settings.OutputDirectory}\""); if (context.Settings.OutputDirectory.Exists) @@ -56,7 +64,7 @@ public void Clean(IGeneralContext context) foreach (FileInfo file in files) { i = 3; - start: +start: try { file.Delete(); @@ -83,6 +91,9 @@ public void Clean(IGeneralContext context) public string GetFileName(IGeneralContext context, DocItem item) { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(item); + return PathCleaner.Clean(item is AssemblyDocItem ? item.FullName : string.Join("/", item.GetParents().Skip(1).Select(p => p.Name).Concat(Enumerable.Repeat(item.Name, 1))), GetInvalidCharReplacement(context)) + ".md"; } } diff --git a/source/Sample/DefaultDocumentation.PluginExample/NewElement.cs b/source/Sample/DefaultDocumentation.PluginExample/NewElement.cs index b613038d..ab40f0eb 100644 --- a/source/Sample/DefaultDocumentation.PluginExample/NewElement.cs +++ b/source/Sample/DefaultDocumentation.PluginExample/NewElement.cs @@ -9,6 +9,9 @@ public sealed class NewElement : IElement public void Write(IWriter writer, XElement element) { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(element); + writer.Append("hello ").Append(element.Value); } } diff --git a/source/Sample/DefaultDocumentation.PluginExample/NewSection.cs b/source/Sample/DefaultDocumentation.PluginExample/NewSection.cs index 82e5865c..7b535958 100644 --- a/source/Sample/DefaultDocumentation.PluginExample/NewSection.cs +++ b/source/Sample/DefaultDocumentation.PluginExample/NewSection.cs @@ -8,6 +8,8 @@ public sealed class NewSection : ISection public void Write(IWriter writer) { + ArgumentNullException.ThrowIfNull(writer); + writer.Append("helloworld"); } }