diff --git a/.editorconfig b/.editorconfig index 32e3ef9..0e13eaa 100644 --- a/.editorconfig +++ b/.editorconfig @@ -350,6 +350,7 @@ resharper_csharp_indent_anonymous_method_block = false resharper_csharp_indent_nested_for_stmt = true resharper_csharp_indent_nested_foreach_stmt = true resharper_csharp_indent_nested_while_stmt = true +resharper_csharp_indent_raw_literal_string = indent resharper_csharp_int_align = false resharper_csharp_keep_existing_arrangement = true resharper_csharp_nested_ternary_style = simple_wrap diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 3ae7ac3..aa1a630 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -22,10 +22,10 @@ jobs: matrix: os: [ubuntu-latest, windows-latest, macos-latest] steps: - - name: Install .NET - uses: actions/setup-dotnet@v3 - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 + - name: Install .NET + uses: actions/setup-dotnet@v4 - name: Restore run: .\build.ps1 restore - name: Build diff --git a/Directory.Build.props b/Directory.Build.props index fd140ff..87eab66 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,7 +7,7 @@ enable enable true - $(NoWarn);1591;1998;NU1507;NU5105;FL0013 + $(NoWarn);1591;1998;NU1507;NU5105 en-US embedded ejball diff --git a/Directory.Packages.props b/Directory.Packages.props index 6fcb9f4..107a378 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,16 +3,12 @@ true - - - - - - + + + - - + \ No newline at end of file diff --git a/XmlDocMarkdown.sln b/XmlDocMarkdown.sln index 2fd0b4c..2ff797b 100644 --- a/XmlDocMarkdown.sln +++ b/XmlDocMarkdown.sln @@ -4,18 +4,12 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XmlDocMarkdown.Core", "src\XmlDocMarkdown.Core\XmlDocMarkdown.Core.csproj", "{AB8545BC-43CD-4E55-BCA5-6FF239D08047}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XmlDocMarkdown", "src\XmlDocMarkdown\XmlDocMarkdown.csproj", "{D1ED4B64-41E6-47DC-9CA6-86D0EE303323}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExampleAssembly", "tools\ExampleAssembly\ExampleAssembly.csproj", "{A2A20621-33DB-41CE-95E7-37480968F3B3}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XmlDocMarkdown.Tests", "tests\XmlDocMarkdown.Tests\XmlDocMarkdown.Tests.csproj", "{62914CA9-ECF5-455A-9E59-0F7128CAE7BF}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cake.XmlDocMarkdown", "src\Cake.XmlDocMarkdown\Cake.XmlDocMarkdown.csproj", "{0AF70790-11F5-4445-B79C-97EA61A828C3}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XmlDocGen", "tools\XmlDocGen\XmlDocGen.csproj", "{9076EBBA-E2EF-468C-9139-4B67E4FC69FE}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "xmldocmd", "src\xmldocmd\xmldocmd.csproj", "{9A94F733-DCBA-457F-AB0C-F33640773802}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Build", "tools\Build\Build.csproj", "{D787BB4B-8E32-4155-B855-9B06D1CA5FED}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{79C28083-1FE9-45CA-A22F-E19E5D3905B4}" @@ -43,10 +37,6 @@ Global {AB8545BC-43CD-4E55-BCA5-6FF239D08047}.Debug|Any CPU.Build.0 = Debug|Any CPU {AB8545BC-43CD-4E55-BCA5-6FF239D08047}.Release|Any CPU.ActiveCfg = Release|Any CPU {AB8545BC-43CD-4E55-BCA5-6FF239D08047}.Release|Any CPU.Build.0 = Release|Any CPU - {D1ED4B64-41E6-47DC-9CA6-86D0EE303323}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D1ED4B64-41E6-47DC-9CA6-86D0EE303323}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D1ED4B64-41E6-47DC-9CA6-86D0EE303323}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D1ED4B64-41E6-47DC-9CA6-86D0EE303323}.Release|Any CPU.Build.0 = Release|Any CPU {A2A20621-33DB-41CE-95E7-37480968F3B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A2A20621-33DB-41CE-95E7-37480968F3B3}.Debug|Any CPU.Build.0 = Debug|Any CPU {A2A20621-33DB-41CE-95E7-37480968F3B3}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -55,26 +45,14 @@ Global {62914CA9-ECF5-455A-9E59-0F7128CAE7BF}.Debug|Any CPU.Build.0 = Debug|Any CPU {62914CA9-ECF5-455A-9E59-0F7128CAE7BF}.Release|Any CPU.ActiveCfg = Release|Any CPU {62914CA9-ECF5-455A-9E59-0F7128CAE7BF}.Release|Any CPU.Build.0 = Release|Any CPU - {0AF70790-11F5-4445-B79C-97EA61A828C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0AF70790-11F5-4445-B79C-97EA61A828C3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0AF70790-11F5-4445-B79C-97EA61A828C3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0AF70790-11F5-4445-B79C-97EA61A828C3}.Release|Any CPU.Build.0 = Release|Any CPU {9076EBBA-E2EF-468C-9139-4B67E4FC69FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9076EBBA-E2EF-468C-9139-4B67E4FC69FE}.Debug|Any CPU.Build.0 = Debug|Any CPU {9076EBBA-E2EF-468C-9139-4B67E4FC69FE}.Release|Any CPU.ActiveCfg = Release|Any CPU {9076EBBA-E2EF-468C-9139-4B67E4FC69FE}.Release|Any CPU.Build.0 = Release|Any CPU - {9A94F733-DCBA-457F-AB0C-F33640773802}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9A94F733-DCBA-457F-AB0C-F33640773802}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9A94F733-DCBA-457F-AB0C-F33640773802}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9A94F733-DCBA-457F-AB0C-F33640773802}.Release|Any CPU.Build.0 = Release|Any CPU {D787BB4B-8E32-4155-B855-9B06D1CA5FED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D787BB4B-8E32-4155-B855-9B06D1CA5FED}.Debug|Any CPU.Build.0 = Debug|Any CPU {D787BB4B-8E32-4155-B855-9B06D1CA5FED}.Release|Any CPU.ActiveCfg = Release|Any CPU {D787BB4B-8E32-4155-B855-9B06D1CA5FED}.Release|Any CPU.Build.0 = Release|Any CPU - {3CE29B0C-6CA6-4AF2-B2A8-A1DD8EDC0B39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3CE29B0C-6CA6-4AF2-B2A8-A1DD8EDC0B39}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3CE29B0C-6CA6-4AF2-B2A8-A1DD8EDC0B39}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3CE29B0C-6CA6-4AF2-B2A8-A1DD8EDC0B39}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index 5986f88..0000000 --- a/docs/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -_drafts/ -_site/ - -Thumbs.db -.jekyll-metadata diff --git a/docs/ExampleAssembly.InnerNamespace/ExampleInnerClass.md b/docs/ExampleAssembly.InnerNamespace/ExampleInnerClass.md index 29f7bb3..efc527c 100644 --- a/docs/ExampleAssembly.InnerNamespace/ExampleInnerClass.md +++ b/docs/ExampleAssembly.InnerNamespace/ExampleInnerClass.md @@ -15,6 +15,5 @@ public class ExampleInnerClass ## See Also * namespace [ExampleAssembly.InnerNamespace](../ExampleAssembly.md) -* [ExampleInnerClass.cs](../../tests/ExampleAssembly/InnerNamespace/ExampleInnerClass.cs) diff --git a/docs/ExampleAssembly/ExampleAbstractClass.md b/docs/ExampleAssembly/ExampleAbstractClass.md index 4b2d754..846e967 100644 --- a/docs/ExampleAssembly/ExampleAbstractClass.md +++ b/docs/ExampleAssembly/ExampleAbstractClass.md @@ -27,6 +27,5 @@ public abstract class ExampleAbstractClass ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleAbstractClass.cs](../../tests/ExampleAssembly/ExampleAbstractClass.cs) diff --git a/docs/ExampleAssembly/ExampleAttribute.md b/docs/ExampleAssembly/ExampleAttribute.md index 4cd649f..6c65a1f 100644 --- a/docs/ExampleAssembly/ExampleAttribute.md +++ b/docs/ExampleAssembly/ExampleAttribute.md @@ -17,6 +17,5 @@ public sealed class ExampleAttribute : Attribute ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleAttribute.cs](../../tests/ExampleAssembly/ExampleAttribute.cs) diff --git a/docs/ExampleAssembly/ExampleClass.md b/docs/ExampleAssembly/ExampleClass.md index dd3b911..a8aa463 100644 --- a/docs/ExampleAssembly/ExampleClass.md +++ b/docs/ExampleAssembly/ExampleClass.md @@ -56,6 +56,5 @@ public class ExampleClass : IExampleContravariantInterface, * interface [IExampleContravariantInterface<T>](./IExampleContravariantInterface-1.md) * interface [IExampleCovariantInterface<T>](./IExampleCovariantInterface-1.md) * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleClass.cs](../../tests/ExampleAssembly/ExampleClass.cs) diff --git a/docs/ExampleAssembly/ExampleDeepClass.md b/docs/ExampleAssembly/ExampleDeepClass.md index 8016729..9fcaf98 100644 --- a/docs/ExampleAssembly/ExampleDeepClass.md +++ b/docs/ExampleAssembly/ExampleDeepClass.md @@ -28,6 +28,5 @@ The [`ExampleDeepClass`](./ExampleDeepClass.md) class has a [`NestedDelegate`](. ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleDeepClass.cs](../../tests/ExampleAssembly/ExampleDeepClass.cs) diff --git a/docs/ExampleAssembly/ExampleDelegate.md b/docs/ExampleAssembly/ExampleDelegate.md index c0758b6..bb648ad 100644 --- a/docs/ExampleAssembly/ExampleDelegate.md +++ b/docs/ExampleAssembly/ExampleDelegate.md @@ -9,6 +9,5 @@ public delegate void ExampleDelegate(); ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleDelegate.cs](../../tests/ExampleAssembly/ExampleDelegate.cs) diff --git a/docs/ExampleAssembly/ExampleDerivedClass.md b/docs/ExampleAssembly/ExampleDerivedClass.md index 9bb7242..1a756f2 100644 --- a/docs/ExampleAssembly/ExampleDerivedClass.md +++ b/docs/ExampleAssembly/ExampleDerivedClass.md @@ -25,6 +25,5 @@ public class ExampleDerivedClass : ExampleClass, IEnumerable, IEnumerabl * interface [IExampleCovariantInterface<T>](./IExampleCovariantInterface-1.md) * interface [IExampleInterface](./IExampleInterface.md) * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleDerivedClass.cs](../../tests/ExampleAssembly/ExampleDerivedClass.cs) diff --git a/docs/ExampleAssembly/ExampleEnum.md b/docs/ExampleAssembly/ExampleEnum.md index e63c93b..16e54d1 100644 --- a/docs/ExampleAssembly/ExampleEnum.md +++ b/docs/ExampleAssembly/ExampleEnum.md @@ -19,6 +19,5 @@ public enum ExampleEnum ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleEnum.cs](../../tests/ExampleAssembly/ExampleEnum.cs) diff --git a/docs/ExampleAssembly/ExampleException.md b/docs/ExampleAssembly/ExampleException.md index d8e7016..c6e773f 100644 --- a/docs/ExampleAssembly/ExampleException.md +++ b/docs/ExampleAssembly/ExampleException.md @@ -15,6 +15,5 @@ public class ExampleException : Exception ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleException.cs](../../tests/ExampleAssembly/ExampleException.cs) diff --git a/docs/ExampleAssembly/ExampleFlagsEnum.md b/docs/ExampleAssembly/ExampleFlagsEnum.md index a0bda31..befe58a 100644 --- a/docs/ExampleAssembly/ExampleFlagsEnum.md +++ b/docs/ExampleAssembly/ExampleFlagsEnum.md @@ -22,6 +22,5 @@ public enum ExampleFlagsEnum ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleFlagsEnum.cs](../../tests/ExampleAssembly/ExampleFlagsEnum.cs) diff --git a/docs/ExampleAssembly/ExampleGenericClass-1.md b/docs/ExampleAssembly/ExampleGenericClass-1.md index 7351ed9..54523e4 100644 --- a/docs/ExampleAssembly/ExampleGenericClass-1.md +++ b/docs/ExampleAssembly/ExampleGenericClass-1.md @@ -23,6 +23,5 @@ public class ExampleGenericClass ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleGenericClass.cs](../../tests/ExampleAssembly/ExampleGenericClass.cs) diff --git a/docs/ExampleAssembly/ExampleGenericDelegate-3.md b/docs/ExampleAssembly/ExampleGenericDelegate-3.md index 2a294af..dcea169 100644 --- a/docs/ExampleAssembly/ExampleGenericDelegate-3.md +++ b/docs/ExampleAssembly/ExampleGenericDelegate-3.md @@ -23,6 +23,5 @@ The result. ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleGenericDelegate.cs](../../tests/ExampleAssembly/ExampleGenericDelegate.cs) diff --git a/docs/ExampleAssembly/ExampleLongEnum.md b/docs/ExampleAssembly/ExampleLongEnum.md index 039d84b..8a0dab6 100644 --- a/docs/ExampleAssembly/ExampleLongEnum.md +++ b/docs/ExampleAssembly/ExampleLongEnum.md @@ -16,6 +16,5 @@ public enum ExampleLongEnum : ulong ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleLongEnum.cs](../../tests/ExampleAssembly/ExampleLongEnum.cs) diff --git a/docs/ExampleAssembly/ExampleLongSummary.md b/docs/ExampleAssembly/ExampleLongSummary.md index ded5806..819fc7c 100644 --- a/docs/ExampleAssembly/ExampleLongSummary.md +++ b/docs/ExampleAssembly/ExampleLongSummary.md @@ -72,6 +72,5 @@ description ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleLongSummary.cs](../../tests/ExampleAssembly/ExampleLongSummary.cs) diff --git a/docs/ExampleAssembly/ExampleRecord-1.md b/docs/ExampleAssembly/ExampleRecord-1.md index b0b85a8..72c4f74 100644 --- a/docs/ExampleAssembly/ExampleRecord-1.md +++ b/docs/ExampleAssembly/ExampleRecord-1.md @@ -24,6 +24,5 @@ public record ExampleRecord ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleRecord.cs](../../tests/ExampleAssembly/ExampleRecord.cs) diff --git a/docs/ExampleAssembly/ExampleRefOutDelegate.md b/docs/ExampleAssembly/ExampleRefOutDelegate.md index 7c7c674..f85a790 100644 --- a/docs/ExampleAssembly/ExampleRefOutDelegate.md +++ b/docs/ExampleAssembly/ExampleRefOutDelegate.md @@ -9,6 +9,5 @@ public delegate void ExampleRefOutDelegate(ref bool isRef, out bool isOut, bool ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleRefOutDelegate.cs](../../tests/ExampleAssembly/ExampleRefOutDelegate.cs) diff --git a/docs/ExampleAssembly/ExampleSealedClass.md b/docs/ExampleAssembly/ExampleSealedClass.md index 0d07c83..72d7fb9 100644 --- a/docs/ExampleAssembly/ExampleSealedClass.md +++ b/docs/ExampleAssembly/ExampleSealedClass.md @@ -15,6 +15,5 @@ public sealed class ExampleSealedClass ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleSealedClass.cs](../../tests/ExampleAssembly/ExampleSealedClass.cs) diff --git a/docs/ExampleAssembly/ExampleSealedRecord.md b/docs/ExampleAssembly/ExampleSealedRecord.md index 944a3c8..e88c9d5 100644 --- a/docs/ExampleAssembly/ExampleSealedRecord.md +++ b/docs/ExampleAssembly/ExampleSealedRecord.md @@ -16,6 +16,5 @@ public record ExampleSealedRecord ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleSealedRecord.cs](../../tests/ExampleAssembly/ExampleSealedRecord.cs) diff --git a/docs/ExampleAssembly/ExampleStaticClass.md b/docs/ExampleAssembly/ExampleStaticClass.md index 3d54ccb..7b1278e 100644 --- a/docs/ExampleAssembly/ExampleStaticClass.md +++ b/docs/ExampleAssembly/ExampleStaticClass.md @@ -16,6 +16,5 @@ public static class ExampleStaticClass ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleStaticClass.cs](../../tests/ExampleAssembly/ExampleStaticClass.cs) diff --git a/docs/ExampleAssembly/ExampleStruct.md b/docs/ExampleAssembly/ExampleStruct.md index 2fb1c71..05587d3 100644 --- a/docs/ExampleAssembly/ExampleStruct.md +++ b/docs/ExampleAssembly/ExampleStruct.md @@ -41,6 +41,5 @@ public struct ExampleStruct ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleStruct.cs](../../tests/ExampleAssembly/ExampleStruct.cs) diff --git a/docs/ExampleAssembly/ExampleTriGenericClass-3.md b/docs/ExampleAssembly/ExampleTriGenericClass-3.md index 42ff0b8..e27b65b 100644 --- a/docs/ExampleAssembly/ExampleTriGenericClass-3.md +++ b/docs/ExampleAssembly/ExampleTriGenericClass-3.md @@ -23,6 +23,5 @@ public class ExampleTriGenericClass ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleTriGenericClass.cs](../../tests/ExampleAssembly/ExampleTriGenericClass.cs) diff --git a/docs/ExampleAssembly/ExampleTuple-1.md b/docs/ExampleAssembly/ExampleTuple-1.md index 878ac91..fa21b12 100644 --- a/docs/ExampleAssembly/ExampleTuple-1.md +++ b/docs/ExampleAssembly/ExampleTuple-1.md @@ -13,6 +13,5 @@ public struct ExampleTuple ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleTuple.cs](../../tests/ExampleAssembly/ExampleTuple.cs) diff --git a/docs/ExampleAssembly/ExampleTuple-2.md b/docs/ExampleAssembly/ExampleTuple-2.md index f65583b..86e0205 100644 --- a/docs/ExampleAssembly/ExampleTuple-2.md +++ b/docs/ExampleAssembly/ExampleTuple-2.md @@ -14,6 +14,5 @@ public struct ExampleTuple ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleTuple.cs](../../tests/ExampleAssembly/ExampleTuple.cs) diff --git a/docs/ExampleAssembly/ExampleTuple-3.md b/docs/ExampleAssembly/ExampleTuple-3.md index ebd2605..ec1f636 100644 --- a/docs/ExampleAssembly/ExampleTuple-3.md +++ b/docs/ExampleAssembly/ExampleTuple-3.md @@ -15,6 +15,5 @@ public struct ExampleTuple ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleTuple.cs](../../tests/ExampleAssembly/ExampleTuple.cs) diff --git a/docs/ExampleAssembly/ExampleTuple-4.md b/docs/ExampleAssembly/ExampleTuple-4.md index ecda45f..1130408 100644 --- a/docs/ExampleAssembly/ExampleTuple-4.md +++ b/docs/ExampleAssembly/ExampleTuple-4.md @@ -16,6 +16,5 @@ public struct ExampleTuple ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleTuple.cs](../../tests/ExampleAssembly/ExampleTuple.cs) diff --git a/docs/ExampleAssembly/ExampleTuple.md b/docs/ExampleAssembly/ExampleTuple.md index 8317a78..9a6a9c8 100644 --- a/docs/ExampleAssembly/ExampleTuple.md +++ b/docs/ExampleAssembly/ExampleTuple.md @@ -9,6 +9,5 @@ public static class ExampleTuple ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleTuple.cs](../../tests/ExampleAssembly/ExampleTuple.cs) diff --git a/docs/ExampleAssembly/ExampleUnbrowsableClass.md b/docs/ExampleAssembly/ExampleUnbrowsableClass.md index ab43577..f22436b 100644 --- a/docs/ExampleAssembly/ExampleUnbrowsableClass.md +++ b/docs/ExampleAssembly/ExampleUnbrowsableClass.md @@ -16,6 +16,5 @@ public class ExampleUnbrowsableClass ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [ExampleUnbrowsableClass.cs](../../tests/ExampleAssembly/ExampleUnbrowsableClass.cs) diff --git a/docs/ExampleAssembly/IExampleContravariantInterface-1.md b/docs/ExampleAssembly/IExampleContravariantInterface-1.md index 5d9d013..aaa3dcf 100644 --- a/docs/ExampleAssembly/IExampleContravariantInterface-1.md +++ b/docs/ExampleAssembly/IExampleContravariantInterface-1.md @@ -9,6 +9,5 @@ public interface IExampleContravariantInterface ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [IExampleContravariantInterface.cs](../../tests/ExampleAssembly/IExampleContravariantInterface.cs) diff --git a/docs/ExampleAssembly/IExampleCovariantInterface-1.md b/docs/ExampleAssembly/IExampleCovariantInterface-1.md index cb2cb20..9701a67 100644 --- a/docs/ExampleAssembly/IExampleCovariantInterface-1.md +++ b/docs/ExampleAssembly/IExampleCovariantInterface-1.md @@ -9,6 +9,5 @@ public interface IExampleCovariantInterface ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [IExampleCovariantInterface.cs](../../tests/ExampleAssembly/IExampleCovariantInterface.cs) diff --git a/docs/ExampleAssembly/IExampleDerivedInterface.md b/docs/ExampleAssembly/IExampleDerivedInterface.md index 1007f05..3a0f60d 100644 --- a/docs/ExampleAssembly/IExampleDerivedInterface.md +++ b/docs/ExampleAssembly/IExampleDerivedInterface.md @@ -11,6 +11,5 @@ public interface IExampleDerivedInterface : IDictionary diff --git a/docs/ExampleAssembly/IExampleInterface.md b/docs/ExampleAssembly/IExampleInterface.md index 1b1ae3b..855f17d 100644 --- a/docs/ExampleAssembly/IExampleInterface.md +++ b/docs/ExampleAssembly/IExampleInterface.md @@ -15,6 +15,5 @@ public interface IExampleInterface ## See Also * namespace [ExampleAssembly](../ExampleAssembly.md) -* [IExampleInterface.cs](../../tests/ExampleAssembly/IExampleInterface.cs) diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 5724100..0000000 --- a/docs/README.md +++ /dev/null @@ -1 +0,0 @@ -### http://ejball.com/XmlDocMarkdown/ diff --git a/docs/XmlDocMarkdown.Core.md b/docs/XmlDocMarkdown.Core.md index 35d49e5..922cb4c 100644 --- a/docs/XmlDocMarkdown.Core.md +++ b/docs/XmlDocMarkdown.Core.md @@ -5,7 +5,6 @@ | public type | description | | --- | --- | | class [ExternalDocumentation](./XmlDocMarkdown.Core/ExternalDocumentation.md) | Configures external documentation. | -| class [XmlDocInput](./XmlDocMarkdown.Core/XmlDocInput.md) | The input for generating Markdown from .NET XML documentation comments. | | class [XmlDocMarkdownApp](./XmlDocMarkdown.Core/XmlDocMarkdownApp.md) | Implements the command-line application. | | static class [XmlDocMarkdownGenerator](./XmlDocMarkdown.Core/XmlDocMarkdownGenerator.md) | Generates Markdown from .NET XML documentation comments. | | class [XmlDocMarkdownResult](./XmlDocMarkdown.Core/XmlDocMarkdownResult.md) | The names of files that were added, changed, or removed. | diff --git a/docs/XmlDocMarkdown.Core/ExternalDocumentation.md b/docs/XmlDocMarkdown.Core/ExternalDocumentation.md index a800768..6f1faea 100644 --- a/docs/XmlDocMarkdown.Core/ExternalDocumentation.md +++ b/docs/XmlDocMarkdown.Core/ExternalDocumentation.md @@ -16,6 +16,5 @@ public sealed class ExternalDocumentation ## See Also * namespace [XmlDocMarkdown.Core](../XmlDocMarkdown.Core.md) -* [ExternalDocumentation.cs](../../src/XmlDocMarkdown.Core/ExternalDocumentation.cs) diff --git a/docs/XmlDocMarkdown.Core/XmlDocInput.md b/docs/XmlDocMarkdown.Core/XmlDocInput.md deleted file mode 100644 index 2d2505c..0000000 --- a/docs/XmlDocMarkdown.Core/XmlDocInput.md +++ /dev/null @@ -1,23 +0,0 @@ -# XmlDocInput class - -The input for generating Markdown from .NET XML documentation comments. - -```csharp -public sealed class XmlDocInput -``` - -## Public Members - -| name | description | -| --- | --- | -| [XmlDocInput](XmlDocInput/XmlDocInput.md)() | The default constructor. | -| [Assembly](XmlDocInput/Assembly.md) { get; set; } | An already-loaded assembly. | -| [AssemblyPath](XmlDocInput/AssemblyPath.md) { get; set; } | The path of the assembly to load. | -| [XmlDocPath](XmlDocInput/XmlDocPath.md) { get; set; } | The path of the XML documentation for the assembly. | - -## See Also - -* namespace [XmlDocMarkdown.Core](../XmlDocMarkdown.Core.md) -* [XmlDocInput.cs](../../src/XmlDocMarkdown.Core/XmlDocInput.cs) - - diff --git a/docs/XmlDocMarkdown.Core/XmlDocInput/Assembly.md b/docs/XmlDocMarkdown.Core/XmlDocInput/Assembly.md deleted file mode 100644 index 0300314..0000000 --- a/docs/XmlDocMarkdown.Core/XmlDocInput/Assembly.md +++ /dev/null @@ -1,18 +0,0 @@ -# XmlDocInput.Assembly property - -An already-loaded assembly. - -```csharp -public Assembly? Assembly { get; set; } -``` - -## Remarks - -Optional; uses `AssemblyPath` if omitted. - -## See Also - -* class [XmlDocInput](../XmlDocInput.md) -* namespace [XmlDocMarkdown.Core](../../XmlDocMarkdown.Core.md) - - diff --git a/docs/XmlDocMarkdown.Core/XmlDocInput/AssemblyPath.md b/docs/XmlDocMarkdown.Core/XmlDocInput/AssemblyPath.md deleted file mode 100644 index 2e47157..0000000 --- a/docs/XmlDocMarkdown.Core/XmlDocInput/AssemblyPath.md +++ /dev/null @@ -1,18 +0,0 @@ -# XmlDocInput.AssemblyPath property - -The path of the assembly to load. - -```csharp -public string? AssemblyPath { get; set; } -``` - -## Remarks - -Optional; uses `Assembly` if omitted. - -## See Also - -* class [XmlDocInput](../XmlDocInput.md) -* namespace [XmlDocMarkdown.Core](../../XmlDocMarkdown.Core.md) - - diff --git a/docs/XmlDocMarkdown.Core/XmlDocInput/XmlDocInput.md b/docs/XmlDocMarkdown.Core/XmlDocInput/XmlDocInput.md deleted file mode 100644 index f32014f..0000000 --- a/docs/XmlDocMarkdown.Core/XmlDocInput/XmlDocInput.md +++ /dev/null @@ -1,14 +0,0 @@ -# XmlDocInput constructor - -The default constructor. - -```csharp -public XmlDocInput() -``` - -## See Also - -* class [XmlDocInput](../XmlDocInput.md) -* namespace [XmlDocMarkdown.Core](../../XmlDocMarkdown.Core.md) - - diff --git a/docs/XmlDocMarkdown.Core/XmlDocInput/XmlDocPath.md b/docs/XmlDocMarkdown.Core/XmlDocInput/XmlDocPath.md deleted file mode 100644 index 8444c24..0000000 --- a/docs/XmlDocMarkdown.Core/XmlDocInput/XmlDocPath.md +++ /dev/null @@ -1,18 +0,0 @@ -# XmlDocInput.XmlDocPath property - -The path of the XML documentation for the assembly. - -```csharp -public string? XmlDocPath { get; set; } -``` - -## Remarks - -Optional; changes the extension of `AssemblyPath` or `Assembly.Location` if omitted. - -## See Also - -* class [XmlDocInput](../XmlDocInput.md) -* namespace [XmlDocMarkdown.Core](../../XmlDocMarkdown.Core.md) - - diff --git a/docs/XmlDocMarkdown.Core/XmlDocMarkdownApp.md b/docs/XmlDocMarkdown.Core/XmlDocMarkdownApp.md index aa6e8cf..9d5f280 100644 --- a/docs/XmlDocMarkdown.Core/XmlDocMarkdownApp.md +++ b/docs/XmlDocMarkdown.Core/XmlDocMarkdownApp.md @@ -16,6 +16,5 @@ public sealed class XmlDocMarkdownApp ## See Also * namespace [XmlDocMarkdown.Core](../XmlDocMarkdown.Core.md) -* [XmlDocMarkdownApp.cs](../../src/XmlDocMarkdown.Core/XmlDocMarkdownApp.cs) diff --git a/docs/XmlDocMarkdown.Core/XmlDocMarkdownApp/Run.md b/docs/XmlDocMarkdown.Core/XmlDocMarkdownApp/Run.md index e62b610..f04f02b 100644 --- a/docs/XmlDocMarkdown.Core/XmlDocMarkdownApp/Run.md +++ b/docs/XmlDocMarkdown.Core/XmlDocMarkdownApp/Run.md @@ -3,12 +3,14 @@ Run the command-line application. ```csharp -public static int Run(IReadOnlyList args) +public static int Run(IReadOnlyList args, + Action? configure = null) ``` | parameter | description | | --- | --- | | args | The command-line arguments. | +| configure | Called to configure the settings. | ## Return Value @@ -16,6 +18,7 @@ The exit code. ## See Also +* class [XmlDocMarkdownSettings](../XmlDocMarkdownSettings.md) * class [XmlDocMarkdownApp](../XmlDocMarkdownApp.md) * namespace [XmlDocMarkdown.Core](../../XmlDocMarkdown.Core.md) diff --git a/docs/XmlDocMarkdown.Core/XmlDocMarkdownGenerator.md b/docs/XmlDocMarkdown.Core/XmlDocMarkdownGenerator.md index 3666650..72198a7 100644 --- a/docs/XmlDocMarkdown.Core/XmlDocMarkdownGenerator.md +++ b/docs/XmlDocMarkdown.Core/XmlDocMarkdownGenerator.md @@ -10,11 +10,10 @@ public static class XmlDocMarkdownGenerator | name | description | | --- | --- | -| static [Generate](XmlDocMarkdownGenerator/Generate.md)(…) | Generates Markdown from .NET XML documentation comments. (2 methods) | +| static [Generate](XmlDocMarkdownGenerator/Generate.md)(…) | Generates Markdown from .NET XML documentation comments. | ## See Also * namespace [XmlDocMarkdown.Core](../XmlDocMarkdown.Core.md) -* [XmlDocMarkdownGenerator.cs](../../src/XmlDocMarkdown.Core/XmlDocMarkdownGenerator.cs) diff --git a/docs/XmlDocMarkdown.Core/XmlDocMarkdownGenerator/Generate.md b/docs/XmlDocMarkdown.Core/XmlDocMarkdownGenerator/Generate.md index 9673b68..fc3f6c0 100644 --- a/docs/XmlDocMarkdown.Core/XmlDocMarkdownGenerator/Generate.md +++ b/docs/XmlDocMarkdown.Core/XmlDocMarkdownGenerator/Generate.md @@ -1,15 +1,15 @@ -# XmlDocMarkdownGenerator.Generate method (1 of 2) +# XmlDocMarkdownGenerator.Generate method Generates Markdown from .NET XML documentation comments. ```csharp -public static XmlDocMarkdownResult Generate(string inputPath, string outputPath, +public static XmlDocMarkdownResult Generate(Assembly assembly, string outputPath, XmlDocMarkdownSettings? settings) ``` | parameter | description | | --- | --- | -| inputPath | The input assembly. | +| assembly | The input assembly. | | outputPath | The output directory. | | settings | The settings. | @@ -24,33 +24,4 @@ The names of files that were added, changed, or removed. * class [XmlDocMarkdownGenerator](../XmlDocMarkdownGenerator.md) * namespace [XmlDocMarkdown.Core](../../XmlDocMarkdown.Core.md) ---- - -# XmlDocMarkdownGenerator.Generate method (2 of 2) - -Generates Markdown from .NET XML documentation comments. - -```csharp -public static XmlDocMarkdownResult Generate(XmlDocInput input, string outputPath, - XmlDocMarkdownSettings? settings) -``` - -| parameter | description | -| --- | --- | -| input | The input. | -| outputPath | The output directory. | -| settings | The settings. | - -## Return Value - -The names of files that were added, changed, or removed. - -## See Also - -* class [XmlDocMarkdownResult](../XmlDocMarkdownResult.md) -* class [XmlDocInput](../XmlDocInput.md) -* class [XmlDocMarkdownSettings](../XmlDocMarkdownSettings.md) -* class [XmlDocMarkdownGenerator](../XmlDocMarkdownGenerator.md) -* namespace [XmlDocMarkdown.Core](../../XmlDocMarkdown.Core.md) - diff --git a/docs/XmlDocMarkdown.Core/XmlDocMarkdownResult.md b/docs/XmlDocMarkdown.Core/XmlDocMarkdownResult.md index b8c6822..d092559 100644 --- a/docs/XmlDocMarkdown.Core/XmlDocMarkdownResult.md +++ b/docs/XmlDocMarkdown.Core/XmlDocMarkdownResult.md @@ -19,6 +19,5 @@ public sealed class XmlDocMarkdownResult ## See Also * namespace [XmlDocMarkdown.Core](../XmlDocMarkdown.Core.md) -* [XmlDocMarkdownResult.cs](../../src/XmlDocMarkdown.Core/XmlDocMarkdownResult.cs) diff --git a/docs/XmlDocMarkdown.Core/XmlDocMarkdownSettings.md b/docs/XmlDocMarkdown.Core/XmlDocMarkdownSettings.md index c4571a3..d867b58 100644 --- a/docs/XmlDocMarkdown.Core/XmlDocMarkdownSettings.md +++ b/docs/XmlDocMarkdown.Core/XmlDocMarkdownSettings.md @@ -13,24 +13,20 @@ public class XmlDocMarkdownSettings | [XmlDocMarkdownSettings](XmlDocMarkdownSettings/XmlDocMarkdownSettings.md)() | The default constructor. | | [ExternalDocs](XmlDocMarkdownSettings/ExternalDocs.md) { get; set; } | Configures external documentation. | | [FrontMatter](XmlDocMarkdownSettings/FrontMatter.md) { get; set; } | If non-null, contains the path to a file that contains the Jekyll front matter template. | -| [GenerateToc](XmlDocMarkdownSettings/GenerateToc.md) { get; set; } | If true, generates a .yml file that can be used in a Jekyll based site. | | [IncludeObsolete](XmlDocMarkdownSettings/IncludeObsolete.md) { get; set; } | If true, generates documentation for obsolete types and members. (Default false.) | | [IsDryRun](XmlDocMarkdownSettings/IsDryRun.md) { get; set; } | If true, executes without making changes to the file system. | | [IsQuiet](XmlDocMarkdownSettings/IsQuiet.md) { get; set; } | If true, suppresses normal console output. | | [NamespacePages](XmlDocMarkdownSettings/NamespacePages.md) { get; set; } | Generate separate pages for each namespace containing list of types in each. | | [NewLine](XmlDocMarkdownSettings/NewLine.md) { get; set; } | Indicates the newline used in the output. | | [PermalinkStyle](XmlDocMarkdownSettings/PermalinkStyle.md) { get; set; } | Specify permalink style, 'none' or 'pretty' (default 'none'). 'pretty' permalinks do not contain file extensions, and when you select this option periods have to be removed from file names, for example, 'System.Console' would have to be 'SystemConsole'. since the removal of the '.md' extension would make Jekyll think '.Console' is a file extension which doesn't work. | -| [RootNamespace](XmlDocMarkdownSettings/RootNamespace.md) { get; set; } | The root namespace of the input assembly. | | [ShouldClean](XmlDocMarkdownSettings/ShouldClean.md) { get; set; } | If true, deletes previously generated files that are no longer used. | | [SkipCompilerGenerated](XmlDocMarkdownSettings/SkipCompilerGenerated.md) { get; set; } | If true, skips documentation for types and members with `[CompilerGenerated]`. | | [SkipUnbrowsable](XmlDocMarkdownSettings/SkipUnbrowsable.md) { get; set; } | If true, skips documentation for types and members with `[EditorBrowsable(EditorBrowsableState.Never)]`. | | [SourceCodePath](XmlDocMarkdownSettings/SourceCodePath.md) { get; set; } | The URL of the folder containing the source code of the assembly, e.g. at GitHub. | -| [TocPrefix](XmlDocMarkdownSettings/TocPrefix.md) { get; set; } | A path prefix to add to all links in the table of contents .yml file. | | [VisibilityLevel](XmlDocMarkdownSettings/VisibilityLevel.md) { get; set; } | The minimum visibility for documented types and members. | ## See Also * namespace [XmlDocMarkdown.Core](../XmlDocMarkdown.Core.md) -* [XmlDocMarkdownSettings.cs](../../src/XmlDocMarkdown.Core/XmlDocMarkdownSettings.cs) diff --git a/docs/XmlDocMarkdown.Core/XmlDocMarkdownSettings/GenerateToc.md b/docs/XmlDocMarkdown.Core/XmlDocMarkdownSettings/GenerateToc.md deleted file mode 100644 index 8bfc4c9..0000000 --- a/docs/XmlDocMarkdown.Core/XmlDocMarkdownSettings/GenerateToc.md +++ /dev/null @@ -1,14 +0,0 @@ -# XmlDocMarkdownSettings.GenerateToc property - -If true, generates a .yml file that can be used in a Jekyll based site. - -```csharp -public bool GenerateToc { get; set; } -``` - -## See Also - -* class [XmlDocMarkdownSettings](../XmlDocMarkdownSettings.md) -* namespace [XmlDocMarkdown.Core](../../XmlDocMarkdown.Core.md) - - diff --git a/docs/XmlDocMarkdown.Core/XmlDocMarkdownSettings/RootNamespace.md b/docs/XmlDocMarkdown.Core/XmlDocMarkdownSettings/RootNamespace.md deleted file mode 100644 index 8d467cf..0000000 --- a/docs/XmlDocMarkdown.Core/XmlDocMarkdownSettings/RootNamespace.md +++ /dev/null @@ -1,18 +0,0 @@ -# XmlDocMarkdownSettings.RootNamespace property - -The root namespace of the input assembly. - -```csharp -public string? RootNamespace { get; set; } -``` - -## Remarks - -Used to generate source code links in the See Also sections for types. If omitted, the tool guesses the root namespace from the exported types. - -## See Also - -* class [XmlDocMarkdownSettings](../XmlDocMarkdownSettings.md) -* namespace [XmlDocMarkdown.Core](../../XmlDocMarkdown.Core.md) - - diff --git a/docs/XmlDocMarkdown.Core/XmlDocMarkdownSettings/TocPrefix.md b/docs/XmlDocMarkdown.Core/XmlDocMarkdownSettings/TocPrefix.md deleted file mode 100644 index 8d92610..0000000 --- a/docs/XmlDocMarkdown.Core/XmlDocMarkdownSettings/TocPrefix.md +++ /dev/null @@ -1,14 +0,0 @@ -# XmlDocMarkdownSettings.TocPrefix property - -A path prefix to add to all links in the table of contents .yml file. - -```csharp -public string? TocPrefix { get; set; } -``` - -## See Also - -* class [XmlDocMarkdownSettings](../XmlDocMarkdownSettings.md) -* namespace [XmlDocMarkdown.Core](../../XmlDocMarkdown.Core.md) - - diff --git a/docs/XmlDocMarkdown.Core/XmlDocVisibilityLevel.md b/docs/XmlDocMarkdown.Core/XmlDocVisibilityLevel.md index faf6eb1..bf9ee62 100644 --- a/docs/XmlDocMarkdown.Core/XmlDocVisibilityLevel.md +++ b/docs/XmlDocMarkdown.Core/XmlDocVisibilityLevel.md @@ -19,6 +19,5 @@ public enum XmlDocVisibilityLevel ## See Also * namespace [XmlDocMarkdown.Core](../XmlDocMarkdown.Core.md) -* [XmlDocVisibilityLevel.cs](../../src/XmlDocMarkdown.Core/XmlDocVisibilityLevel.cs) diff --git a/docs/_config.yml b/docs/_config.yml deleted file mode 100644 index df075c9..0000000 --- a/docs/_config.yml +++ /dev/null @@ -1,3 +0,0 @@ -exclude: - - docker-compose.yml - - README.md diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html deleted file mode 100644 index dec4b1c..0000000 --- a/docs/_layouts/default.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - {{ page.title | strip_html }} - - - - - - -
-
-
- {{ content }} -
-
- -
-
-
- - - - - - - diff --git a/docs/assets/github.png b/docs/assets/github.png deleted file mode 100644 index bdd7cf4..0000000 Binary files a/docs/assets/github.png and /dev/null differ diff --git a/docs/assets/home.png b/docs/assets/home.png deleted file mode 100644 index 3f381e2..0000000 Binary files a/docs/assets/home.png and /dev/null differ diff --git a/docs/assets/page.css b/docs/assets/page.css deleted file mode 100644 index 1fd1243..0000000 --- a/docs/assets/page.css +++ /dev/null @@ -1,161 +0,0 @@ -body { - font-variant: none; -} - -.navbar-static-top { - margin-bottom: 0; -} - -.large { - font-size: 125%; -} - -.navbar-logo { - height: 24px; -} - -.page-container { - margin-top: 21px; - margin-bottom: 21px; -} - -.content-column { - padding-left: 45px; -} - -h1, .h1, h2, .h2, h3, .h3 { - margin-top: 0; - padding-top: 21px; -} - -h4, .h4, h5, .h5, h6, .h6 { - margin-top: 14px; - margin-bottom: 7px; -} - -a.anchorjs-link:active, a.anchorjs-link:hover { - text-decoration: none; -} - -nav.affix[data-toggle='toc'] { - margin-top: -60px; -} - -#toc { - width: 190px; -} - -pre { - color: inherit; -} - -code { - color: inherit; - background-color: #ecf0f1; -} - -@media (max-width: 767px) { - #toc { - display: none; - } -} - -@media (max-width: 479px) { - .content-column { - padding-left: 15px; - } - - .anchorjs-link { - display: none; - } -} - -/* apply .table, .table-striped, and .table-hover from Flatly to all tables */ -@media print { - table { - border-collapse: collapse !important; - } - table td, - table th { - background-color: #fff !important; - } -} -table { - width: 100%; - max-width: 100%; - margin-bottom: 21px; -} -table > thead > tr > th, -table > tbody > tr > th, -table > tfoot > tr > th, -table > thead > tr > td, -table > tbody > tr > td, -table > tfoot > tr > td { - padding: 8px; - line-height: 1.42857143; - vertical-align: top; - border-top: 1px solid #ecf0f1; -} -table > thead > tr > th { - vertical-align: bottom; - border-bottom: 2px solid #ecf0f1; -} -table > caption + thead > tr:first-child > th, -table > colgroup + thead > tr:first-child > th, -table > thead:first-child > tr:first-child > th, -table > caption + thead > tr:first-child > td, -table > colgroup + thead > tr:first-child > td, -table > thead:first-child > tr:first-child > td { - border-top: 0; -} -table > tbody + tbody { - border-top: 2px solid #ecf0f1; -} -table table { - background-color: #ffffff; -} -table > tbody > tr:nth-of-type(odd) { - background-color: #f9f9f9; -} -table > tbody > tr:hover { - background-color: #ecf0f1; -} - -/* undo underlined links in tables */ -table a:not(.btn), .table a:not(.btn) { - text-decoration: none; -} - -.highlight .hll { background-color: #ffffcc } -.highlight .c { color: #008000 } /* Comment */ -.highlight .err { border: 1px solid #FF0000 } /* Error */ -.highlight .k { color: #0000ff } /* Keyword */ -.highlight .cm { color: #008000 } /* Comment.Multiline */ -.highlight .cp { color: #0000ff } /* Comment.Preproc */ -.highlight .c1 { color: #008000 } /* Comment.Single */ -.highlight .cs { color: #008000 } /* Comment.Special */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .gh { font-weight: bold } /* Generic.Heading */ -.highlight .gp { font-weight: bold } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { font-weight: bold } /* Generic.Subheading */ -.highlight .kc { color: #0000ff } /* Keyword.Constant */ -.highlight .kd { color: #0000ff } /* Keyword.Declaration */ -.highlight .kn { color: #0000ff } /* Keyword.Namespace */ -.highlight .kp { color: #0000ff } /* Keyword.Pseudo */ -.highlight .kr { color: #0000ff } /* Keyword.Reserved */ -.highlight .kt { color: #2b91af } /* Keyword.Type */ -.highlight .s { color: #a31515 } /* Literal.String */ -.highlight .nc { color: #2b91af } /* Name.Class */ -.highlight .ow { color: #0000ff } /* Operator.Word */ -.highlight .sb { color: #a31515 } /* Literal.String.Backtick */ -.highlight .sc { color: #a31515 } /* Literal.String.Char */ -.highlight .sd { color: #a31515 } /* Literal.String.Doc */ -.highlight .s2 { color: #a31515 } /* Literal.String.Double */ -.highlight .se { color: #a31515 } /* Literal.String.Escape */ -.highlight .sh { color: #a31515 } /* Literal.String.Heredoc */ -.highlight .si { color: #a31515 } /* Literal.String.Interpol */ -.highlight .sx { color: #a31515 } /* Literal.String.Other */ -.highlight .sr { color: #a31515 } /* Literal.String.Regex */ -.highlight .s1 { color: #a31515 } /* Literal.String.Single */ -.highlight .ss { color: #a31515 } /* Literal.String.Symbol */ diff --git a/docs/docker-compose.yml b/docs/docker-compose.yml deleted file mode 100644 index f49f7cb..0000000 --- a/docs/docker-compose.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: '3' - -services: - jekyll: - image: jekyll/jekyll:pages - container_name: xmldocmarkdown - command: jekyll serve --force_polling --livereload - ports: - - 4000:4000 - - 35729:35729 - volumes: - - ./:/srv/jekyll diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index b6f5fb9..0000000 --- a/docs/index.md +++ /dev/null @@ -1,113 +0,0 @@ -# XmlDocMarkdown - -**XmlDocMarkdown** generates Markdown from [.NET XML documentation comments](https://msdn.microsoft.com/en-us/library/b2s063f7.aspx). - -It is distributed as a class library, .NET tool, .NET Framework console app, and Cake addin. - -For example output, see the [Markdown documents](https://github.com/ejball/XmlDocMarkdown/blob/master/docs/ExampleAssembly.md) for the [documentation](ExampleAssembly.md) of the [ExampleAssembly](https://github.com/ejball/XmlDocMarkdown/tree/master/tools/ExampleAssembly) class library. - -The goal of this tool is to generate Markdown documentation for .NET class libraries that are simple enough to be read and understood in [raw form](https://raw.githubusercontent.com/ejball/XmlDocMarkdown/master/docs/ExampleAssembly/ExampleClass.md), as [rendered in GitHub](https://github.com/ejball/XmlDocMarkdown/blob/master/docs/ExampleAssembly/ExampleClass.md), or when used to generate [web pages](https://ejball.com/XmlDocMarkdown/ExampleAssembly/ExampleClass.html) using [Jekyll](https://jekyllrb.com/) and [GitHub Pages](https://pages.github.com/). To that end, it generates standard [GitHub Flavored Markdown](https://github.github.com/gfm/) without relying on raw HTML tags. - -For a more full-featured documentation generation tool, check out [DocFX](https://dotnet.github.io/docfx/) or [Sandcastle](https://github.com/EWSoftware/SHFB). - -## Usage - -**XmlDocMarkdown** uses the `.xml` documentation files generated when your code is compiled, so make sure they are being generated, e.g. by including `true` in your `.csproj` files. - -The most reliable way to use **XmlDocMarkdown** is to build and run a command-line tool that references the **XmlDocMarkdown.Core** class library and the assembly that you want to document. This ensures that the assembly and all of its dependencies are loaded properly. - -This is easier than it sounds, because the **XmlDocMarkdown.Core** class library contains the full implementation of the command-line application via [XmlDocMarkdownApp.Run](https://ejball.com/XmlDocMarkdown/XmlDocMarkdown.Core/XmlDocMarkdownApp/Run.html). - -Example `XmlDocGen.csproj`: - -```xml - - - - Exe - net6.0 - - - - - - - - - - - -``` - -Example `Program.cs`: - -```csharp -using XmlDocMarkdown.Core; - -return XmlDocMarkdownApp.Run(args); -``` - -Build and run `XmlDocGen` like you would any console app, specifying the arguments and options as documented below. For example, from the same directory as `XmlDocGen.csproj`, you can generate documentation for the `MyLibrary` assembly into the `docs` folder: - -```sh -> dotnet run MyLibrary docs -``` - -### Arguments - -The command-line tool accepts the name of the input assembly, the path of the output directory, and a number of options. - -The output directory will be created if necessary. - -For example, the arguments `MyLibrary docs` will generate Markdown documentation in the `docs` directory for the `MyLibrary` assembly. The compiler-generated `MyLibrary.xml` file must be in the same directory as `MyLibrary.dll`. - -### Options - -* `--source `: The URL (absolute or relative) of the folder containing the source code of the assembly, e.g. at GitHub. Required to generate source code links in the See Also sections for types. This assumes that each type is defined in a `.cs` file that matches its name. -* `--namespace `: The root namespace of the input assembly. Used to generate source code links in the See Also sections for types. If omitted, the tool guesses the root namespace from the exported types. -* `--visibility (public|protected|internal|private)`: The minimum visibility for documented types and members. If `public`, only public types and members are documented. If `protected`, only public and protected types and members are documented. Similarly for `internal` and `private`. Defaults to `protected`. -* `--obsolete`: Generates documentation for obsolete types and members, which are not documented by default. -* `--external `: Generates links to external documentation for the specified namespace, which must be documented in the same repository with similar options. -* `--clean`: Delete previously generated files that are no longer used. -* `--verify`: Executes the tool without making changes to the file system, but exits with error code 1 if changes would be made. Typically used in build scripts to ensure that any changes have been reflected in the generated code. -* `--dryrun`: Executes the tool without making changes to the file system. -* `--quiet`: Suppresses normal console output. -* `--newline (auto|lf|crlf)`: Indicates the newline used in the output. Defaults to `auto`, which uses CRLF or LF, depending on the platform. - -### XmlDocMarkdown.Core (class library) - -[![NuGet](https://img.shields.io/nuget/v/XmlDocMarkdown.Core.svg)](https://www.nuget.org/packages/XmlDocMarkdown.Core) - -To implement a command-line tool, call [XmlDocMarkdownApp.Run](https://ejball.com/XmlDocMarkdown/XmlDocMarkdown.Core/XmlDocMarkdownApp/Run.html) with the command-line arguments. - -To use the library directly, call [`XmlDocMarkdownGenerator.Generate`](XmlDocMarkdown.Core/XmlDocMarkdownGenerator/Generate) with the desired input path, output path, and [`XmlDocMarkdownSettings`](XmlDocMarkdown.Core/XmlDocMarkdownSettings). - -### xmldocmd (.NET tool) - -[![NuGet](https://img.shields.io/nuget/v/xmldocmd.svg)](https://www.nuget.org/packages/xmldocmd) - -If you are documenting a modern .NET library and your dependencies are simple enough, you may be able to use this prebuilt .NET tool, but building your own command-line tool is recommended and documented above. - -To install `xmldocmd`: `dotnet tool install xmldocmd -g` - -Use the path of the input assembly as the first argument. The XML documentation file should be in the same directory as the input assembly. - -### XmlDocMarkdown (.NET Framework console app) - -[![NuGet](https://img.shields.io/nuget/v/XmlDocMarkdown.svg)](https://www.nuget.org/packages/XmlDocMarkdown) - -If you are documenting a .NET Framework library and your dependencies are simple enough, you may be able to use this prebuilt .NET Framework console app, but building your own command-line tool is recommended and documented above. - -`nuget install XmlDocMarkdown -excludeversion` will download the latest version of `XmlDocMarkdown.exe` into `XmlDocMarkdown/tools`. - -On Mac or Linux, use [Mono](http://www.mono-project.com/) to run `nuget` as well as the command-line tool itself. - -The command-line arguments and options are the same as `xmldocmd` above. - -### Cake.XmlDocMarkdown (Cake addin) - -[![NuGet](https://img.shields.io/nuget/v/Cake.XmlDocMarkdown.svg)](https://www.nuget.org/packages/Cake.XmlDocMarkdown) - -If your dependencies are simple enough, you may be able to use this Cake addin, but building your own command-line tool is recommended and documented above. - -See [https://cakebuild.net/extensions/cake-xmldocmarkdown/](https://cakebuild.net/extensions/cake-xmldocmarkdown/). diff --git a/src/Cake.XmlDocMarkdown/Cake.XmlDocMarkdown.csproj b/src/Cake.XmlDocMarkdown/Cake.XmlDocMarkdown.csproj deleted file mode 100644 index 9c65d39..0000000 --- a/src/Cake.XmlDocMarkdown/Cake.XmlDocMarkdown.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - - netstandard2.0 - A Cake addin that generates Markdown from .NET XML documentation comments. - .NET XML documentation comments Markdown Cake - true - true - https://raw.githubusercontent.com/cake-contrib/graphics/a5cf0f881c390650144b2243ae551d5b9f836196/png/cake-contrib-medium.png - $(NoWarn);NU5048 - README.md - - - - - - - - - - - - - - - - - lib/ - true - - - - diff --git a/src/Cake.XmlDocMarkdown/XmlDocCakeAddin.cs b/src/Cake.XmlDocMarkdown/XmlDocCakeAddin.cs deleted file mode 100644 index 1604dcf..0000000 --- a/src/Cake.XmlDocMarkdown/XmlDocCakeAddin.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Cake.Core; -using Cake.Core.Annotations; -using Cake.Core.Diagnostics; -using Cake.Core.IO; -using XmlDocMarkdown.Core; - -namespace Cake.XmlDocMarkdown -{ - /// - /// The Cake addin. - /// - [CakeAliasCategory("XmlDocMarkdown")] - [CakeNamespaceImport("XmlDocMarkdown.Core")] - public static class XmlDocCakeAddin - { - /// - /// Generates Markdown from .NET XML documentation comments. - /// - /// The Cake context. - /// The input assembly. - /// The output directory. - /// The settings. - /// The names of files that were added, changed, or removed. - [CakeMethodAlias] - public static XmlDocMarkdownResult XmlDocMarkdownGenerate(this ICakeContext context, FilePath inputPath, DirectoryPath outputPath, XmlDocMarkdownSettings? settings = null) => - context.XmlDocMarkdownGenerate(inputPath.FullPath, outputPath.FullPath, settings); - - /// - /// Generates Markdown from .NET XML documentation comments. - /// - /// The Cake context. - /// The input assembly. - /// The output directory. - /// The settings. - /// The names of files that were added, changed, or removed. - [CakeMethodAlias] - public static XmlDocMarkdownResult XmlDocMarkdownGenerate(this ICakeContext context, string inputPath, string outputPath, XmlDocMarkdownSettings? settings = null) - { - var result = XmlDocMarkdownGenerator.Generate(inputPath, outputPath, settings); - foreach (var message in result.Messages) - context?.Log.Write(Verbosity.Normal, LogLevel.Information, message); - return result; - } - } -} diff --git a/src/XmlDocMarkdown.Core/ArgsReader.cs b/src/XmlDocMarkdown.Core/ArgsReader.cs index adb73ed..cc40b29 100644 --- a/src/XmlDocMarkdown.Core/ArgsReader.cs +++ b/src/XmlDocMarkdown.Core/ArgsReader.cs @@ -1,215 +1,85 @@ -using System.Text.RegularExpressions; +namespace XmlDocMarkdown.Core; -namespace XmlDocMarkdown.Core +internal sealed class ArgsReader(IEnumerable args) { - /// - /// Helps process command-line arguments. - /// - /// To use this class, construct an ArgsReader with the command-line arguments from Main, - /// read the supported options one at a time with and , - /// read any normal arguments with , and finally call , - /// which throws an if any unsupported options or arguments haven't been read. - internal sealed class ArgsReader + public bool ReadFlag(string name) { - /// - /// Creates a reader for the specified command-line arguments. - /// - /// The command-line arguments from Main. - /// args is null. - public ArgsReader(IEnumerable args) - { - m_args = (args ?? throw new ArgumentNullException(nameof(args))).ToList(); - } + ArgumentNullException.ThrowIfNull(name); + if (name.Length == 0) + throw new ArgumentException("Flag name must not be empty.", nameof(name)); - /// - /// True if short options (e.g. -h) should ignore case. (Default false.) - /// - public bool ShortOptionIgnoreCase { get; set; } - - /// - /// True if long options (e.g. --help) should ignore case. (Default false.) - /// - public bool LongOptionIgnoreCase { get; set; } - - /// - /// True if long options (e.g. --dry-run) should ignore "kebab case", i.e. allow --dryrun. (Default false.) - /// - public bool LongOptionIgnoreKebabCase { get; set; } - - /// - /// True if -- is ignored and all following arguments are not read as options. (Default false.) - /// - public bool NoOptionsAfterDoubleDash { get; set; } - - /// - /// Reads the specified flag, returning true if it is found. - /// - /// The name of the specified flag. - /// True if the specified flag was found on the command line. - /// If the flag is found, the method returns true and the flag is - /// removed. If ReadFlag is called again with the same name, it will return false, - /// unless the same flag appears twice on the command line. - /// To support multiple names for the same flag, use a | to separate them, - /// e.g. use help|h|? to support three different names for a help flag. - /// Single-character names use a single hyphen, e.g. -h. Longer names - /// use a double hyphen, e.g. --help. - /// name is null. - /// One of the names is empty. - public bool ReadFlag(string name) - { - if (name == null) - throw new ArgumentNullException(nameof(name)); - if (name.Length == 0) - throw new ArgumentException("Flag name must not be empty.", nameof(name)); + var names = name.Split('|'); + if (names.Length > 1) + return names.Any(ReadFlag); - var names = name.Split('|'); - if (names.Length > 1) - return names.Any(ReadFlag); + var index = FindOptionArgumentIndex(name); + if (index == -1) + return false; - var index = FindOptionArgumentIndex(name); - if (index == -1) - return false; + m_args.RemoveAt(index); + return true; + } - m_args.RemoveAt(index); - return true; - } + public string? ReadOption(string name) + { + ArgumentNullException.ThrowIfNull(name); + if (name.Length == 0) + throw new ArgumentException("Option name must not be empty.", nameof(name)); - /// - /// Reads the value of the specified option, if any. - /// - /// The name of the specified option. - /// The specified option if it was found on the command line; null otherwise. - /// If the option is found, the method returns the command-line argument - /// after the option and both arguments are removed. If ReadOption is called again with the - /// same name, it will return null, unless the same option appears twice on the command line. - /// To support multiple names for the same option, use a vertical bar (|) to separate them, - /// e.g. use n|name to support two different names for a module option. - /// Single-character names use a single hyphen, e.g. -n example. Longer names use a - /// double hyphen, e.g. --name example. - /// name is null. - /// One of the names is empty. - /// The argument that must follow the option is missing. - public string? ReadOption(string name) - { - if (name == null) - throw new ArgumentNullException(nameof(name)); - if (name.Length == 0) - throw new ArgumentException("Option name must not be empty.", nameof(name)); - - var names = name.Split('|'); - if (names.Length > 1) - return names.Select(ReadOption).FirstOrDefault(x => x != null); - - var index = FindOptionArgumentIndex(name); - if (index == -1) - return null; - - var value = index + 1 < m_args.Count ? m_args[index + 1] : null; - if (value == null || IsOption(value)) - throw new ArgsReaderException($"Missing value after '{RenderOption(name)}'."); - - m_args.RemoveAt(index); - m_args.RemoveAt(index); - return value; - } + var names = name.Split('|'); + if (names.Length > 1) + return names.Select(ReadOption).FirstOrDefault(x => x != null); - /// - /// Reads the next non-option argument. - /// - /// The next non-option argument, or null if none remain. - /// If the next argument is an option, this method throws an exception. - /// If options can appear before normal arguments, be sure to read all options before reading - /// any normal arguments. - /// The next argument is an option. - public string? ReadArgument() - { - if (m_args.Count == 0) - return null; + var index = FindOptionArgumentIndex(name); + if (index == -1) + return null; - var value = m_args[0]; + var value = index + 1 < m_args.Count ? m_args[index + 1] : null; + if (value == null || IsOption(value)) + throw new ArgsReaderException($"Missing value after '{RenderOption(name)}'."); - if (NoOptionsAfterDoubleDash && value == "--") - { - m_args.RemoveAt(0); - m_noMoreOptions = true; - return ReadArgument(); - } + m_args.RemoveAt(index); + m_args.RemoveAt(index); + return value; + } - if (!m_noMoreOptions && IsOption(value)) - throw new ArgsReaderException($"Unexpected option '{value}'."); + public string? ReadArgument() + { + if (m_args.Count == 0) + return null; - m_args.RemoveAt(0); - return value; - } + var value = m_args[0]; - /// - /// Reads any remaining non-option arguments. - /// - /// The remaining non-option arguments, if any. - /// If any remaining arguments are options, this method throws an exception. - /// If options can appear before normal arguments, be sure to read all options before reading - /// any normal arguments. - /// A remaining argument is an option. - public IReadOnlyList ReadArguments() - { - var arguments = new List(); - while (true) - { - var argument = ReadArgument(); - if (argument == null) - return arguments; - arguments.Add(argument); - } - } + if (IsOption(value)) + throw new ArgsReaderException($"Unexpected option '{value}'."); - /// - /// Confirms that all arguments were processed. - /// - /// A command-line argument was not read. - public void VerifyComplete() - { - if (m_args.Count != 0) - throw new ArgsReaderException($"Unexpected {(IsOption(m_args[0]) ? "option" : "argument")} '{m_args[0]}'."); - } + m_args.RemoveAt(0); + return value; + } + + public void VerifyComplete() + { + if (m_args.Count != 0) + throw new ArgsReaderException($"Unexpected {(IsOption(m_args[0]) ? "option" : "argument")} '{m_args[0]}'."); + } - private static bool IsOption(string value) => value.Length >= 2 && value[0] == '-' && value != "--"; + private static bool IsOption(string value) => value.Length >= 2 && value[0] == '-' && value != "--"; - private static string RenderOption(string name) => name.Length == 1 ? $"-{name}" : $"--{name}"; + private static string RenderOption(string name) => name.Length == 1 ? $"-{name}" : $"--{name}"; - private bool IsOptionArgument(string optionName, string argument) - { - var renderedOption = RenderOption(optionName); - if (optionName.Length == 1) - { - return string.Equals(argument, renderedOption, ShortOptionIgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); - } - else - { - if (LongOptionIgnoreKebabCase) - { - argument = Regex.Replace(argument, @"\b-\b", ""); - renderedOption = Regex.Replace(renderedOption, @"\b-\b", ""); - } - - return string.Equals(argument, renderedOption, LongOptionIgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); - } - } + private static bool IsOptionArgument(string optionName, string argument) => string.Equals(argument, RenderOption(optionName), StringComparison.Ordinal); - private int FindOptionArgumentIndex(string optionName) + private int FindOptionArgumentIndex(string optionName) + { + for (var index = 0; index < m_args.Count; index++) { - for (var index = 0; index < m_args.Count; index++) - { - var arg = m_args[index]; - if (NoOptionsAfterDoubleDash && arg == "--") - break; - if (IsOptionArgument(optionName, arg)) - return index; - } - - return -1; + var arg = m_args[index]; + if (IsOptionArgument(optionName, arg)) + return index; } - private readonly List m_args; - private bool m_noMoreOptions; + return -1; } + + private readonly List m_args = (args ?? throw new ArgumentNullException(nameof(args))).ToList(); } diff --git a/src/XmlDocMarkdown.Core/ArgsReaderException.cs b/src/XmlDocMarkdown.Core/ArgsReaderException.cs index fc42aea..5d769a5 100644 --- a/src/XmlDocMarkdown.Core/ArgsReaderException.cs +++ b/src/XmlDocMarkdown.Core/ArgsReaderException.cs @@ -1,21 +1,12 @@ using System.Diagnostics.CodeAnalysis; -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +[SuppressMessage("Design", "CA1064:Exceptions should be public", Justification = "For internal use.")] +internal sealed class ArgsReaderException : Exception { - /// - /// Thrown when an error occurs while processing command-line arguments. - /// - [SuppressMessage("Design", "CA1064:Exceptions should be public", Justification = "For internal use.")] - internal sealed class ArgsReaderException : Exception + public ArgsReaderException(string message, Exception? innerException = null) + : base(message, innerException) { - /// - /// Creates an exception. - /// - /// The exception message. - /// The inner exception. - public ArgsReaderException(string message, Exception? innerException = null) - : base(message, innerException) - { - } } } diff --git a/src/XmlDocMarkdown.Core/CommonArgs.cs b/src/XmlDocMarkdown.Core/CommonArgs.cs index 827111e..9f84224 100644 --- a/src/XmlDocMarkdown.Core/CommonArgs.cs +++ b/src/XmlDocMarkdown.Core/CommonArgs.cs @@ -1,64 +1,14 @@ -namespace XmlDocMarkdown.Core -{ - internal static class CommonArgs - { - public static string? ReadSourceOption(this ArgsReader args) => args.ReadOption("source"); - - public static string? ReadNamespaceOption(this ArgsReader args) => args.ReadOption("namespace"); - - public static XmlDocVisibilityLevel? ReadVisibilityOption(this ArgsReader args) - { - var visibility = args.ReadOption("visibility"); - return visibility switch - { - "public" => XmlDocVisibilityLevel.Public, - "protected" => XmlDocVisibilityLevel.Protected, - "internal" => XmlDocVisibilityLevel.Internal, - "private" => XmlDocVisibilityLevel.Private, - null => null, - _ => throw new ArgsReaderException($"Unknown visibility option: {visibility}"), - }; - } - - public static bool ReadObsoleteFlag(this ArgsReader args) => args.ReadFlag("obsolete"); - - public static bool ReadSkipUnbrowsableFlag(this ArgsReader args) => args.ReadFlag("skip-unbrowsable"); - - public static bool ReadSkipCompilerGeneratedFlag(this ArgsReader args) => args.ReadFlag("skip-compiler-generated"); - - public static string? ReadExternalOption(this ArgsReader args) => args.ReadOption("external"); +namespace XmlDocMarkdown.Core; - public static bool ReadCleanFlag(this ArgsReader args) => args.ReadFlag("clean"); - - public static bool ReadDryRunFlag(this ArgsReader args) => args.ReadFlag("dryrun"); - - public static bool ReadHelpFlag(this ArgsReader args) => args.ReadFlag("help|h|?"); - - public static bool ReadQuietFlag(this ArgsReader args) => args.ReadFlag("quiet"); - - public static bool ReadNamespacePagesFlag(this ArgsReader args) => args.ReadFlag("namespace-pages"); - - public static string? ReadFrontMatter(this ArgsReader args) => args.ReadOption("front-matter"); - - public static string? ReadPermalinkStyle(this ArgsReader args) => args.ReadOption("permalink"); +internal static class CommonArgs +{ + public static bool ReadCleanFlag(this ArgsReader args) => args.ReadFlag("clean"); - public static bool ReadVerifyFlag(this ArgsReader args) => args.ReadFlag("verify"); + public static bool ReadDryRunFlag(this ArgsReader args) => args.ReadFlag("dryrun"); - public static bool ReadTocFlag(this ArgsReader args) => args.ReadFlag("toc"); + public static bool ReadHelpFlag(this ArgsReader args) => args.ReadFlag("help|h|?"); - public static string? ReadTocPrefix(this ArgsReader args) => args.ReadOption("toc-prefix"); + public static bool ReadQuietFlag(this ArgsReader args) => args.ReadFlag("quiet"); - public static string? ReadNewLineOption(this ArgsReader args) - { - var value = args.ReadOption("newline"); - return value switch - { - "auto" => null, - "lf" => "\n", - "crlf" => "\r\n", - null => null, - _ => throw new ArgsReaderException($"Invalid new line '{value}'. (Should be 'auto', 'lf', or 'crlf'.)"), - }; - } - } + public static bool ReadVerifyFlag(this ArgsReader args) => args.ReadFlag("verify"); } diff --git a/src/XmlDocMarkdown.Core/ExternalDocumentation.cs b/src/XmlDocMarkdown.Core/ExternalDocumentation.cs index 92eb27a..611f282 100644 --- a/src/XmlDocMarkdown.Core/ExternalDocumentation.cs +++ b/src/XmlDocMarkdown.Core/ExternalDocumentation.cs @@ -1,13 +1,12 @@ -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +/// +/// Configures external documentation. +/// +public sealed class ExternalDocumentation { /// - /// Configures external documentation. + /// The namespace to configure. /// - public sealed class ExternalDocumentation - { - /// - /// The namespace to configure. - /// - public string? Namespace { get; set; } - } + public string? Namespace { get; set; } } diff --git a/src/XmlDocMarkdown.Core/MarkdownGenerator.cs b/src/XmlDocMarkdown.Core/MarkdownGenerator.cs index c9f445e..67b87c0 100644 --- a/src/XmlDocMarkdown.Core/MarkdownGenerator.cs +++ b/src/XmlDocMarkdown.Core/MarkdownGenerator.cs @@ -6,105 +6,141 @@ using System.Text; using System.Text.RegularExpressions; -namespace XmlDocMarkdown.Core -{ - internal sealed class MarkdownGenerator - { - public string? NewLine { get; set; } - - public string? SourceCodePath { get; set; } - - public string? RootNamespace { get; set; } +namespace XmlDocMarkdown.Core; - public string? RootPageLocation { get; set; } +internal sealed class MarkdownGenerator +{ + public string? NewLine { get; set; } - public bool IncludeObsolete { get; set; } + public bool IncludeObsolete { get; set; } - public bool SkipUnbrowsable { get; set; } + public bool SkipUnbrowsable { get; set; } - public bool SkipCompilerGenerated { get; set; } + public bool SkipCompilerGenerated { get; set; } - public bool NamespacePages { get; set; } + public bool NamespacePages { get; set; } - public XmlDocVisibilityLevel Visibility { get; set; } + public XmlDocVisibilityLevel Visibility { get; set; } - public IReadOnlyList? ExternalDocs { get; set; } + public IReadOnlyList? ExternalDocs { get; set; } - public IReadOnlyList GenerateOutput(Assembly assembly, XmlDocAssembly xmlDocAssembly) => - DoGenerateOutput(assembly, xmlDocAssembly).ToList(); + public string? FrontMatter { get; internal set; } - public static string GetCodeGenComment(string assemblyName) => $""; + public IReadOnlyList GenerateOutput(Assembly assembly, XmlDocAssembly xmlDocAssembly) => + DoGenerateOutput(assembly, xmlDocAssembly).ToList(); - private string ActualNewLine => NewLine ?? Environment.NewLine; + public static string GetCodeGenComment(string assemblyName) => $""; - public string? FrontMatter { get; internal set; } + private string ActualNewLine => NewLine ?? Environment.NewLine; - public bool PermalinkPretty { get; internal set; } + public bool PermalinkPretty { get; internal set; } - private IEnumerable DoGenerateOutput(Assembly assembly, XmlDocAssembly xmlDocAssembly) - { - var extension = GetFileExtension(); - var assemblyName = assembly.GetName().Name; - var assemblyFilePath = assembly.Modules.FirstOrDefault()?.FullyQualifiedName; - var assemblyFileName = assemblyFilePath != null ? Path.GetFileName(assemblyFilePath) : assemblyName; - - var visibleTypes = assembly - .DefinedTypes - .Where(IsVisible) - .ToList(); + public string? RootPageLocation { get; set; } - var membersByXmlDocName = visibleTypes - .Where(x => new[] { TypeKind.Record, TypeKind.Class, TypeKind.Struct, TypeKind.Interface }.Contains(GetTypeKind(x))) - .SelectMany(x => x.DeclaredMembers) - .Where(x => !(x is TypeInfo) && IsVisible(x)) - .Concat(visibleTypes) - .GroupBy(XmlDocUtility.GetXmlDocRef) - .ToDictionary(x => x.Key!, x => x.Single()); + private IEnumerable DoGenerateOutput(Assembly assembly, XmlDocAssembly xmlDocAssembly) + { + var extension = GetFileExtension(); + var assemblyName = assembly.GetName().Name!; + var assemblyFilePath = assembly.Modules.FirstOrDefault()?.FullyQualifiedName; + var assemblyFileName = assemblyFilePath != null ? Path.GetFileName(assemblyFilePath) : assemblyName; + + var visibleTypes = assembly + .DefinedTypes + .Where(IsVisible) + .ToList(); + + var membersByXmlDocName = visibleTypes + .Where(x => new[] { TypeKind.Record, TypeKind.Class, TypeKind.Struct, TypeKind.Interface }.Contains(GetTypeKind(x))) + .SelectMany(x => x.DeclaredMembers) + .Where(x => !(x is TypeInfo) && IsVisible(x)) + .Concat(visibleTypes) + .GroupBy(XmlDocUtility.GetXmlDocRef) + .ToDictionary(x => x.Key!, x => x.Single()); + + var visibleTypeRecords = visibleTypes + .Select(typeInfo => new + { + TypeInfo = typeInfo, + Path = GetTypeUriName(typeInfo) + ".md", + ShortName = GetShortName(typeInfo), + ShortSignature = GetShortSignature(typeInfo), + Namespace = GetNamespaceName(typeInfo), + Visibility = GetTypeVisibility(typeInfo), + }) + .ToList(); + + var visibleNamespaceRecords = visibleTypeRecords + .Where(x => x.TypeInfo.DeclaringType == null) + .GroupBy(x => x.Namespace) + .Select(ng => new + { + Namespace = ng.Key, + Types = ng + .OrderBy(x => x.ShortName, StringComparer.OrdinalIgnoreCase) + .ThenBy(x => x.Path, StringComparer.OrdinalIgnoreCase) + .ToList(), + }) + .OrderBy(x => x.Namespace, StringComparer.OrdinalIgnoreCase) + .ToList(); + + var rootPath = GetAssemblyUriName(assembly); + var safeAssemblyName = GetSafeName(rootPath); + RootPageLocation = $"{safeAssemblyName}" + (PermalinkPretty ? "Assembly.md" : ".md"); + var context = new MarkdownContext(xmlDocAssembly, membersByXmlDocName, assemblyFileName, RootPageLocation); + yield return CreateNamedText(context.PageLocation, writer => + { + var front = GetFrontMatter(assemblyName, $"{safeAssemblyName}" + (PermalinkPretty ? "Assembly" : "") + extension); + if (!string.IsNullOrEmpty(front)) + writer.WriteLine(front!); + + writer.WriteLine($"# {assemblyName} assembly"); - var visibleTypeRecords = visibleTypes - .Select(typeInfo => new - { - TypeInfo = typeInfo, - Path = GetTypeUriName(typeInfo) + ".md", - ShortName = GetShortName(typeInfo), - ShortSignature = GetShortSignature(typeInfo), - Namespace = GetNamespaceName(typeInfo), - Visibility = GetTypeVisibility(typeInfo), - }) - .ToList(); + foreach (var group in visibleNamespaceRecords) + { + writer.WriteLine(); + writer.WriteLine($"## {group.Namespace} namespace"); - var visibleNamespaceRecords = visibleTypeRecords - .Where(x => x.TypeInfo.DeclaringType == null) - .GroupBy(x => x.Namespace) - .Select(ng => new + foreach (var typeGroup in group.Types.GroupBy(x => x.Visibility)) { - Namespace = ng.Key, - Types = ng - .OrderBy(x => x.ShortName, StringComparer.OrdinalIgnoreCase) - .ThenBy(x => x.Path, StringComparer.OrdinalIgnoreCase) - .ToList(), - }) - .OrderBy(x => x.Namespace, StringComparer.OrdinalIgnoreCase) - .ToList(); + writer.WriteLine(); + writer.WriteLine($"| {(typeGroup.Key == XmlDocVisibilityLevel.Public ? "public" : "internal")} type | description |"); + writer.WriteLine("| --- | --- |"); + foreach (var typeInfo in typeGroup) + { + var relative = GetPermalink(typeInfo.Path); + var safeRelative = GetSafeName(relative); + if (PermalinkPretty) + safeRelative += "Type"; + var rel = MakeRelative(context.PageLocation, $"{GetNamespaceUriName(group.Namespace)}/{safeRelative}"); + var typeText = GetShortSignatureMarkdown(typeInfo.ShortSignature, rel); + var summaryText = GetShortSummaryMarkdown(xmlDocAssembly, typeInfo.TypeInfo, context); + writer.WriteLine($"| {typeText} | {summaryText} |"); + } + } + } - var sourceCodePath = SourceCodePath?.Trim('/'); - var rootPath = GetAssemblyUriName(assembly); - var safeAssemblyName = GetSafeName(rootPath); - var rootNamespace = RootNamespace ?? - visibleNamespaceRecords.OrderBy(x => x.Namespace.Length).ThenByDescending(x => x.Types.Count).Select(x => x.Namespace).FirstOrDefault(x => x.Length != 0); - RootPageLocation = $"{safeAssemblyName}" + (PermalinkPretty ? "Assembly.md" : ".md"); - var context = new MarkdownContext(xmlDocAssembly, membersByXmlDocName, assemblyFileName, sourceCodePath, rootNamespace, RootPageLocation); - yield return CreateNamedText(context.PageLocation, null, assemblyName, writer => - { - var front = GetFrontMatter(assemblyName, $"{safeAssemblyName}" + (PermalinkPretty ? "Assembly" : "") + extension); - if (!string.IsNullOrEmpty(front)) - writer.WriteLine(front!); + writer.WriteLine(); + writer.WriteLine(GetCodeGenComment(assemblyFileName)); + }); - writer.WriteLine($"# {assemblyName} assembly"); + // create separate parent pages for each namespace in the assembly. + foreach (var group in visibleNamespaceRecords) + { + var parentPageLocation = RootPageLocation; + var parentContext = context; - foreach (var group in visibleNamespaceRecords) + if (NamespacePages) + { + var namespacePath = group.Namespace; + var safeNamespacePath = GetSafeName(namespacePath); + parentPageLocation = $"{safeNamespacePath}Namespace.md"; + parentContext = new MarkdownContext(context, null, parentPageLocation); + yield return CreateNamedText(parentPageLocation, writer => { - writer.WriteLine(); + var front = GetFrontMatter(namespacePath, $"{safeNamespacePath}Namespace"); + if (!string.IsNullOrEmpty(front)) + writer.WriteLine(front!); + writer.WriteLine($"## {group.Namespace} namespace"); foreach (var typeGroup in group.Types.GroupBy(x => x.Visibility)) @@ -124,2189 +160,2124 @@ private IEnumerable DoGenerateOutput(Assembly assembly, XmlDocAssembl writer.WriteLine($"| {typeText} | {summaryText} |"); } } - } - writer.WriteLine(); - writer.WriteLine(GetCodeGenComment(assemblyFileName)); - }); + writer.WriteLine(); + writer.WriteLine(GetCodeGenComment(assemblyFileName)); + }); + } - // create separate parent pages for each namespace in the assembly. - foreach (var group in visibleNamespaceRecords) + foreach (var visibleTypeRecord in visibleTypeRecords) { - var parentPageLocation = RootPageLocation; - var parentContext = context; - - if (NamespacePages) + if (visibleTypeRecord.Namespace == group.Namespace) { - var namespacePath = group.Namespace; - var safeNamespacePath = GetSafeName(namespacePath); - parentPageLocation = $"{safeNamespacePath}Namespace.md"; - parentContext = new MarkdownContext(context, null, parentPageLocation); - yield return CreateNamedText(parentPageLocation, context.PageLocation, group.Namespace, writer => + var relative = GetPermalink(visibleTypeRecord.Path); + var safeRelative = GetSafeName(relative); + if (PermalinkPretty) + safeRelative += "Type.md"; + var typePage = $"{GetNamespaceUriName(visibleTypeRecord.Namespace)}/{safeRelative}"; + yield return WriteMemberPage( + path: typePage, + title: GetFullMemberName(visibleTypeRecord.TypeInfo), + parent: parentPageLocation, + memberInfo: visibleTypeRecord.TypeInfo, + context: parentContext); + + var typeKind = GetTypeKind(visibleTypeRecord.TypeInfo); + if (typeKind is TypeKind.Record or TypeKind.Class or TypeKind.Struct or TypeKind.Interface) { - var front = GetFrontMatter(namespacePath, $"{safeNamespacePath}Namespace"); - if (!string.IsNullOrEmpty(front)) - writer.WriteLine(front!); - - writer.WriteLine($"## {group.Namespace} namespace"); - - foreach (var typeGroup in group.Types.GroupBy(x => x.Visibility)) - { - writer.WriteLine(); - writer.WriteLine($"| {(typeGroup.Key == XmlDocVisibilityLevel.Public ? "public" : "internal")} type | description |"); - writer.WriteLine("| --- | --- |"); - foreach (var typeInfo in typeGroup) + var memberGroups = visibleTypeRecord.TypeInfo + .DeclaredMembers + .Where(x => !(x is TypeInfo) && IsVisible(x)) + .GroupBy(GetMemberUriName) + .Select(tg => new { - var relative = GetPermalink(typeInfo.Path); - var safeRelative = GetSafeName(relative); - if (PermalinkPretty) - safeRelative += "Type"; - var rel = MakeRelative(context.PageLocation, $"{GetNamespaceUriName(group.Namespace)}/{safeRelative}"); - var typeText = GetShortSignatureMarkdown(typeInfo.ShortSignature, rel); - var summaryText = GetShortSummaryMarkdown(xmlDocAssembly, typeInfo.TypeInfo, context); - writer.WriteLine($"| {typeText} | {summaryText} |"); - } - } - - writer.WriteLine(); - writer.WriteLine(GetCodeGenComment(assemblyFileName)); - }); - } + MemberUriName = tg.Key, + Members = OrderMembers(tg, x => x).ToList(), + }) + .ToList(); - foreach (var visibleTypeRecord in visibleTypeRecords) - { - if (visibleTypeRecord.Namespace == group.Namespace) - { - var relative = GetPermalink(visibleTypeRecord.Path); - var safeRelative = GetSafeName(relative); - if (PermalinkPretty) - safeRelative += "Type.md"; - var typePage = $"{GetNamespaceUriName(visibleTypeRecord.Namespace)}/{safeRelative}"; - yield return WriteMemberPage( - path: typePage, - title: GetFullMemberName(visibleTypeRecord.TypeInfo), - parent: parentPageLocation, - memberInfo: visibleTypeRecord.TypeInfo, - context: parentContext); - - var typeKind = GetTypeKind(visibleTypeRecord.TypeInfo); - if (typeKind is TypeKind.Record or TypeKind.Class or TypeKind.Struct or TypeKind.Interface) + foreach (var memberGroup in memberGroups) { - var memberGroups = visibleTypeRecord.TypeInfo - .DeclaredMembers - .Where(x => !(x is TypeInfo) && IsVisible(x)) - .GroupBy(GetMemberUriName) - .Select(tg => new - { - MemberUriName = tg.Key, - Members = OrderMembers(tg, x => x).ToList(), - }) - .ToList(); - - foreach (var memberGroup in memberGroups) - { - yield return WriteMemberPage( - path: $"{GetNamespaceUriName(visibleTypeRecord.Namespace)}/{GetTypeUriName(visibleTypeRecord.TypeInfo)}/{memberGroup.MemberUriName}.md", - parent: typePage, - title: memberGroup.MemberUriName, - memberGroup: memberGroup.Members, - context: context); - } + yield return WriteMemberPage( + path: $"{GetNamespaceUriName(visibleTypeRecord.Namespace)}/{GetTypeUriName(visibleTypeRecord.TypeInfo)}/{memberGroup.MemberUriName}.md", + parent: typePage, + title: memberGroup.MemberUriName, + memberGroup: memberGroup.Members, + context: context); } } } } } + } - private string? GetFrontMatter(string title, string relativeLink) - { - if (string.IsNullOrEmpty(FrontMatter)) - return null; - - var contents = File.ReadAllText(FrontMatter); - return contents.Replace("$title", title).Replace("$ref", relativeLink); - } + private string? GetFrontMatter(string title, string relativeLink) + { + if (string.IsNullOrEmpty(FrontMatter)) + return null; - private NamedText CreateNamedText(string name, string? parent, string title, Action writeTo) - { - using var stringWriter = new StringWriter(); - if (NewLine != null) - stringWriter.NewLine = NewLine; + var contents = File.ReadAllText(FrontMatter); + return contents.Replace("$title", title, StringComparison.Ordinal).Replace("$ref", relativeLink, StringComparison.Ordinal); + } - var code = new MarkdownWriter(stringWriter); - writeTo(code); - return new NamedText(name, parent, title, stringWriter.ToString()); - } + private NamedText CreateNamedText(string name, Action writeTo) + { + using var stringWriter = new StringWriter(); + if (NewLine != null) + stringWriter.NewLine = NewLine; - private static Collection? GetSummary(XmlDocAssembly xmlDocAssembly, MemberInfo member) => - GetSummary(xmlDocAssembly.FindMember(XmlDocUtility.GetXmlDocRef(member)), member); + var code = new MarkdownWriter(stringWriter); + writeTo(code); + return new NamedText(name, stringWriter.ToString()); + } - private static Collection? GetSummary(XmlDocMember? xmlDocMember, MemberInfo member) - { - var summary = xmlDocMember?.Summary; + private static Collection? GetSummary(XmlDocAssembly xmlDocAssembly, MemberInfo member) => + GetSummary(xmlDocAssembly.FindMember(XmlDocUtility.GetXmlDocRef(member)), member); - if (summary == null || summary.Count == 0) - { - var constructorInfo = member as ConstructorInfo; - if (constructorInfo != null && !constructorInfo.IsStatic && constructorInfo.GetParameters().Length == 0) - summary = new Collection { new() { Inlines = { new XmlDocInline { Text = "The default constructor." } } } }; - } + private static Collection? GetSummary(XmlDocMember? xmlDocMember, MemberInfo member) + { + var summary = xmlDocMember?.Summary; - return summary; + if (summary == null || summary.Count == 0) + { + var constructorInfo = member as ConstructorInfo; + if (constructorInfo != null && !constructorInfo.IsStatic && constructorInfo.GetParameters().Length == 0) + summary = new Collection { new() { Inlines = { new XmlDocInline { Text = "The default constructor." } } } }; } - private string GetShortSummaryMarkdown(XmlDocAssembly xmlDocAssembly, MemberInfo member, MarkdownContext context) => - ToMarkdown(GetSummary(xmlDocAssembly, member)?.FirstOrDefault()?.Inlines, context) ?? ""; + return summary; + } + + private string GetShortSummaryMarkdown(XmlDocAssembly xmlDocAssembly, MemberInfo member, MarkdownContext context) => + ToMarkdown(GetSummary(xmlDocAssembly, member)?.FirstOrDefault()?.Inlines, context) ?? ""; - private static string GetAssemblyUriName(Assembly assembly) => $"{assembly.GetName().Name}"; + private static string GetAssemblyUriName(Assembly assembly) => $"{assembly.GetName().Name}"; - private static string GetNamespaceUriName(string? namespaceName) => namespaceName ?? "global"; + private static string GetNamespaceUriName(string? namespaceName) => namespaceName ?? "global"; - private static string GetTypeUriName(TypeInfo typeInfo) + private static string GetTypeUriName(TypeInfo typeInfo) + { + return GetFullTypeName(typeInfo, x => { - return GetFullTypeName(typeInfo, x => - { - var genericTypeCount = x.GenericTypeParameters.Length; - return GetShortName(x) + (genericTypeCount == 0 ? "" : $"-{genericTypeCount}"); - }); - } + var genericTypeCount = x.GenericTypeParameters.Length; + return GetShortName(x) + (genericTypeCount == 0 ? "" : $"-{genericTypeCount}"); + }); + } - private string GetSafeTypeUriName(TypeInfo typeInfo) + private string GetSafeTypeUriName(TypeInfo typeInfo) + { + var name = GetSafeName(GetTypeUriName(typeInfo)); + if (PermalinkPretty) { - var name = GetSafeName(GetTypeUriName(typeInfo)); - if (PermalinkPretty) - { - // Jekyll doesn't allow "Foo.md" and a folder named "Foo" containing the method markdown files. - // So we rename the type "FooType.md" to avoid this conflict. - name += "Type"; - } - return name; + // Jekyll doesn't allow "Foo.md" and a folder named "Foo" containing the method markdown files. + // So we rename the type "FooType.md" to avoid this conflict. + name += "Type"; } + return name; + } - private string GetSafeName(string name) - { - if (PermalinkPretty) - return name.Replace(".", ""); // Jekyll can't handle dots in file name. + private string GetSafeName(string name) + { + if (PermalinkPretty) + return name.Replace(".", "", StringComparison.Ordinal); // Jekyll can't handle dots in file name. - return name; - } + return name; + } - private string GetMemberUriName(MemberInfo memberInfo) - { - var typeInfo = memberInfo as TypeInfo; - return typeInfo != null ? $"{GetSafeTypeUriName(typeInfo)}" : GetShortName(memberInfo); - } + private string GetMemberUriName(MemberInfo memberInfo) + { + var typeInfo = memberInfo as TypeInfo; + return typeInfo != null ? $"{GetSafeTypeUriName(typeInfo)}" : GetShortName(memberInfo); + } - private static string GetShortSignatureMarkdown(ShortSignature shortSignature, string path) => - EscapeHtml($"{shortSignature.Prefix}[{shortSignature.Name}]({path}){shortSignature.Suffix}"); + private static string GetShortSignatureMarkdown(ShortSignature shortSignature, string path) => + EscapeHtml($"{shortSignature.Prefix}[{shortSignature.Name}]({path}){shortSignature.Suffix}"); - private static string EscapeHtml(string value) => - value.Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace("|", "|"); + private static string EscapeHtml(string value) => + value.Replace("&", "&", StringComparison.Ordinal).Replace("<", "<", StringComparison.Ordinal).Replace(">", ">", StringComparison.Ordinal).Replace("|", "|", StringComparison.Ordinal); - private static string SurroundCode(string value) - { - var backticks = new string('`', Regex.Matches(value, "`+").Cast().Select(x => x.Length).Concat([0]).Max() + 1); - return backticks + value + backticks; - } + private static string SurroundCode(string value) + { + var backticks = new string('`', Regex.Matches(value, "`+").Cast().Select(x => x.Length).Concat([0]).Max() + 1); + return backticks + value + backticks; + } - private NamedText WriteMemberPage(string path, string parent, string title, MemberInfo memberInfo, MarkdownContext context) => - WriteMemberPage(path, parent, title, [memberInfo], context); + private NamedText WriteMemberPage(string path, string parent, string title, MemberInfo memberInfo, MarkdownContext context) => + WriteMemberPage(path, parent, title, [memberInfo], context); - private string GetFileExtension() => PermalinkPretty ? "" : ".md"; + private string GetFileExtension() => PermalinkPretty ? "" : ".md"; - private string GetPermalink(string path) + private string GetPermalink(string path) + { + if (PermalinkPretty) { - if (PermalinkPretty) - { - // permalinks paths cannot end in .md - var pos = path.LastIndexOf('.'); - if (pos > 0) - path = path.Substring(0, pos); - } - return path.Replace("\\", "/"); + // permalinks paths cannot end in .md + var pos = path.LastIndexOf('.'); + if (pos > 0) + path = path.Substring(0, pos); } + return path.Replace("\\", "/", StringComparison.Ordinal); + } + + private NamedText WriteMemberPage(string path, string parent, string title, IReadOnlyList memberGroup, MarkdownContext context) + { + var extension = GetFileExtension(); - private NamedText WriteMemberPage(string path, string parent, string title, IReadOnlyList memberGroup, MarkdownContext context) + return CreateNamedText(path, writer => { - var extension = GetFileExtension(); + var relative = $"{GetPermalink(path)}"; + var front = GetFrontMatter(title, relative); + if (!string.IsNullOrEmpty(front)) + writer.WriteLine(front!); - return CreateNamedText(path, parent, title, writer => + for (var memberIndex = 0; memberIndex < memberGroup.Count; memberIndex++) { - var relative = $"{GetPermalink(path)}"; - var front = GetFrontMatter(title, relative); - if (!string.IsNullOrEmpty(front)) - writer.WriteLine(front!); - - for (var memberIndex = 0; memberIndex < memberGroup.Count; memberIndex++) - { - var memberInfo = memberGroup[memberIndex]; - var memberContext = new MarkdownContext(context, memberInfo, path); - var typeInfo = memberInfo as TypeInfo; - var typeKind = typeInfo == null ? default(TypeKind?) : GetTypeKind(typeInfo); + var memberInfo = memberGroup[memberIndex]; + var memberContext = new MarkdownContext(context, memberInfo, path); + var typeInfo = memberInfo as TypeInfo; + var typeKind = typeInfo == null ? default(TypeKind?) : GetTypeKind(typeInfo); - if (memberIndex != 0) - writer.WriteLine(); + if (memberIndex != 0) + writer.WriteLine(); - writer.WriteLine($"# {EscapeHtml(GetMemberHeading(memberGroup, memberIndex))}"); + writer.WriteLine($"# {EscapeHtml(GetMemberHeading(memberGroup, memberIndex))}"); - var xmlDocRef = XmlDocUtility.GetXmlDocRef(memberInfo); - var xmlDocMember = memberContext.XmlDocAssembly.FindMember(xmlDocRef); + var xmlDocRef = XmlDocUtility.GetXmlDocRef(memberInfo); + var xmlDocMember = memberContext.XmlDocAssembly.FindMember(xmlDocRef); - var summary = GetSummary(xmlDocMember, memberInfo); - if (summary != null && summary.Count != 0) - { - writer.WriteLine(); - writer.WriteLines(ToMarkdown(summary, memberContext)); - } + var summary = GetSummary(xmlDocMember, memberInfo); + if (summary != null && summary.Count != 0) + { + writer.WriteLine(); + writer.WriteLines(ToMarkdown(summary, memberContext)); + } - var seeAlsoMembers = new List(); - if (xmlDocMember != null) + var seeAlsoMembers = new List(); + if (xmlDocMember != null) + { + foreach (var seeAlsoInfo in xmlDocMember.SeeAlso) { - foreach (var seeAlsoInfo in xmlDocMember.SeeAlso) - { - var xmlDocName = seeAlsoInfo.Ref; - if (xmlDocName != null && memberContext.MembersByXmlDocName.TryGetValue(xmlDocName, out var seeAlsoMember)) - seeAlsoMembers.Add(seeAlsoMember); - } + var xmlDocName = seeAlsoInfo.Ref; + if (xmlDocName != null && memberContext.MembersByXmlDocName.TryGetValue(xmlDocName, out var seeAlsoMember)) + seeAlsoMembers.Add(seeAlsoMember); } + } - writer.WriteLine(); - writer.WriteLine("```csharp"); - writer.WriteLine(GetFullSignature(memberInfo, seeAlsoMembers)); - writer.WriteLine("```"); - - if (xmlDocMember != null) - { - var typeParameters = xmlDocMember.TypeParameters; - var parameters = xmlDocMember.Parameters; + writer.WriteLine(); + writer.WriteLine("```csharp"); + writer.WriteLine(GetFullSignature(memberInfo, seeAlsoMembers)); + writer.WriteLine("```"); - // record parameters are documented by the constructor, so don't repeat them here - if (typeKind == TypeKind.Record) - parameters = new Collection(); + if (xmlDocMember != null) + { + var typeParameters = xmlDocMember.TypeParameters; + var parameters = xmlDocMember.Parameters; - if (typeParameters.Count + parameters.Count > 0) - { - writer.WriteLine(); - writer.WriteLine("| parameter | description |"); - writer.WriteLine("| --- | --- |"); - foreach (var typeParameter in typeParameters) - { - var description = ToMarkdown(typeParameter.Description.FirstOrDefault()?.Inlines, memberContext) ?? ""; - writer.WriteLine($"| {typeParameter.Name} | {description} |"); - } - foreach (var parameter in parameters) - { - var description = ToMarkdown(parameter.Description.FirstOrDefault()?.Inlines, memberContext) ?? ""; - writer.WriteLine($"| {parameter.Name} | {description} |"); - } - } - } + // record parameters are documented by the constructor, so don't repeat them here + if (typeKind == TypeKind.Record) + parameters = new Collection(); - if (typeKind == TypeKind.Enum) + if (typeParameters.Count + parameters.Count > 0) { writer.WriteLine(); - writer.WriteLine("## Values"); - writer.WriteLine(); - writer.WriteLine("| name | value | description |"); - writer.WriteLine("| --- | --- | --- |"); - - var isFlags = IsFlagsEnum(typeInfo!); - foreach (var enumValue in typeInfo!.DeclaredMembers.OfType().Where(x => x.IsPublic && x.IsLiteral)) + writer.WriteLine("| parameter | description |"); + writer.WriteLine("| --- | --- |"); + foreach (var typeParameter in typeParameters) { - var valueObject = enumValue.GetValue(null); - var valueText = isFlags ? "0x" + Convert.ToString(Convert.ToInt64(valueObject, CultureInfo.InvariantCulture), 16).ToUpperInvariant() : - Enum.GetUnderlyingType(typeInfo.AsType()) == typeof(ulong) ? Convert.ToString(Convert.ToUInt64(valueObject, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture) : - Convert.ToString(Convert.ToInt64(valueObject, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); - var description = GetShortSummaryMarkdown(memberContext.XmlDocAssembly, enumValue, memberContext); - writer.WriteLine($"| {enumValue.Name} | {SurroundCode(valueText)} | {description} |"); + var description = ToMarkdown(typeParameter.Description.FirstOrDefault()?.Inlines, memberContext) ?? ""; + writer.WriteLine($"| {typeParameter.Name} | {description} |"); } - } - else if (typeKind is TypeKind.Record or TypeKind.Class or TypeKind.Struct or TypeKind.Interface) - { - var innerMemberVisibilityGroups = typeInfo! - .DeclaredMembers - .Where(IsVisible) - .GroupBy(GetVisibility) - .Select(tg => new - { - Visibility = tg.Key, - Members = tg.ToList(), - }) - .OrderByDescending(x => (int) x.Visibility); - foreach (var innerMemberVisibilityGroup in innerMemberVisibilityGroups) + foreach (var parameter in parameters) { - var innerMemberSignatureGroups = OrderMembers(innerMemberVisibilityGroup - .Members - .GroupBy(x => GetShortSignature(x)) - .Select(tg => new - { - ShortSignature = tg.Key, - Members = tg.OrderBy(x => (x as TypeInfo)?.GenericTypeParameters.Length ?? 0).ToList(), - }), x => x.Members[0]).ToList(); - - if (innerMemberSignatureGroups.Count != 0) - { - writer.WriteLine(); - switch (innerMemberVisibilityGroup.Visibility) - { - case XmlDocVisibilityLevel.Public: - writer.WriteLine(typeKind == TypeKind.Interface ? "## Members" : "## Public Members"); - break; - case XmlDocVisibilityLevel.Protected: - writer.WriteLine("## Protected Members"); - break; - case XmlDocVisibilityLevel.Internal: - writer.WriteLine("## Internal Members"); - break; - case XmlDocVisibilityLevel.Private: - writer.WriteLine("## Private Members"); - break; - default: - throw new InvalidOperationException(); - } - writer.WriteLine(); - writer.WriteLine("| name | description |"); - writer.WriteLine("| --- | --- |"); - - foreach (var innerMemberGroup in innerMemberSignatureGroups) - { - var innerMembers = innerMemberGroup.Members; - var firstInnerMember = innerMembers[0]; - var memberPath = firstInnerMember is TypeInfo ? - $"{GetMemberUriName(firstInnerMember)}{extension}" : - $"{GetTypeUriName(typeInfo)}/{GetMemberUriName(firstInnerMember)}{extension}"; - var memberText = GetShortSignatureMarkdown(innerMemberGroup.ShortSignature, memberPath); - var summaryText = GetShortSummaryMarkdown(memberContext.XmlDocAssembly, firstInnerMember, memberContext); - if (innerMembers.Count != 1) - summaryText += $" ({innerMembers.Count} {GetMemberGroupNoun(innerMembers)})"; - - writer.WriteLine($"| {memberText} | {summaryText} |"); - } - } + var description = ToMarkdown(parameter.Description.FirstOrDefault()?.Inlines, memberContext) ?? ""; + writer.WriteLine($"| {parameter.Name} | {description} |"); } } + } - var returnValue = xmlDocMember?.ReturnValue; - if (returnValue != null && returnValue.Count != 0) - { - writer.WriteLine(); - writer.WriteLine("## Return Value"); - writer.WriteLine(); - writer.WriteLines(ToMarkdown(returnValue, memberContext)); - } + if (typeKind == TypeKind.Enum) + { + writer.WriteLine(); + writer.WriteLine("## Values"); + writer.WriteLine(); + writer.WriteLine("| name | value | description |"); + writer.WriteLine("| --- | --- | --- |"); - var propertyValue = xmlDocMember?.PropertyValue; - if (propertyValue != null && propertyValue.Count != 0) + var isFlags = IsFlagsEnum(typeInfo!); + foreach (var enumValue in typeInfo!.DeclaredMembers.OfType().Where(x => x.IsPublic && x.IsLiteral)) { - writer.WriteLine(); - writer.WriteLine("## Property Value"); - writer.WriteLine(); - writer.WriteLines(ToMarkdown(propertyValue, memberContext)); + var valueObject = enumValue.GetValue(null); + var valueText = isFlags ? "0x" + Convert.ToString(Convert.ToInt64(valueObject, CultureInfo.InvariantCulture), 16).ToUpperInvariant() : + Enum.GetUnderlyingType(typeInfo.AsType()) == typeof(ulong) ? Convert.ToString(Convert.ToUInt64(valueObject, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture) : + Convert.ToString(Convert.ToInt64(valueObject, CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + var description = GetShortSummaryMarkdown(memberContext.XmlDocAssembly, enumValue, memberContext); + writer.WriteLine($"| {enumValue.Name} | {SurroundCode(valueText)} | {description} |"); } - - var exceptions = xmlDocMember?.Exceptions; - if (exceptions != null && exceptions.Count != 0) + } + else if (typeKind is TypeKind.Record or TypeKind.Class or TypeKind.Struct or TypeKind.Interface) + { + var innerMemberVisibilityGroups = typeInfo! + .DeclaredMembers + .Where(IsVisible) + .GroupBy(GetVisibility) + .Select(tg => new + { + Visibility = tg.Key, + Members = tg.ToList(), + }) + .OrderByDescending(x => (int) x.Visibility); + foreach (var innerMemberVisibilityGroup in innerMemberVisibilityGroups) { - writer.WriteLine(); - writer.WriteLine("## Exceptions"); - writer.WriteLine(); - writer.WriteLine("| exception | condition |"); - writer.WriteLine("| --- | --- |"); + var innerMemberSignatureGroups = OrderMembers(innerMemberVisibilityGroup + .Members + .GroupBy(x => GetShortSignature(x)) + .Select(tg => new + { + ShortSignature = tg.Key, + Members = tg.OrderBy(x => (x as TypeInfo)?.GenericTypeParameters.Length ?? 0).ToList(), + }), x => x.Members[0]).ToList(); - foreach (var exception in exceptions) + if (innerMemberSignatureGroups.Count != 0) { - MemberInfo? exceptionMemberInfo = null; - if (exception.ExceptionTypeRef != null) - memberContext.MembersByXmlDocName.TryGetValue(exception.ExceptionTypeRef, out exceptionMemberInfo); - var text = exceptionMemberInfo != null ? GetShortName(exceptionMemberInfo) : exception.ExceptionTypeRef != null ? XmlDocUtility.GetShortNameForXmlDocRef(exception.ExceptionTypeRef) : ""; - var link = WrapMarkdownRefLink(text, exceptionMemberInfo, memberContext); - writer.WriteLine($"| {link} | {ToMarkdown(exception.Condition.FirstOrDefault()?.Inlines, memberContext) ?? ""} |"); + writer.WriteLine(); + switch (innerMemberVisibilityGroup.Visibility) + { + case XmlDocVisibilityLevel.Public: + writer.WriteLine(typeKind == TypeKind.Interface ? "## Members" : "## Public Members"); + break; + case XmlDocVisibilityLevel.Protected: + writer.WriteLine("## Protected Members"); + break; + case XmlDocVisibilityLevel.Internal: + writer.WriteLine("## Internal Members"); + break; + case XmlDocVisibilityLevel.Private: + writer.WriteLine("## Private Members"); + break; + default: + throw new InvalidOperationException(); + } + writer.WriteLine(); + writer.WriteLine("| name | description |"); + writer.WriteLine("| --- | --- |"); + + foreach (var innerMemberGroup in innerMemberSignatureGroups) + { + var innerMembers = innerMemberGroup.Members; + var firstInnerMember = innerMembers[0]; + var memberPath = firstInnerMember is TypeInfo ? + $"{GetMemberUriName(firstInnerMember)}{extension}" : + $"{GetTypeUriName(typeInfo)}/{GetMemberUriName(firstInnerMember)}{extension}"; + var memberText = GetShortSignatureMarkdown(innerMemberGroup.ShortSignature, memberPath); + var summaryText = GetShortSummaryMarkdown(memberContext.XmlDocAssembly, firstInnerMember, memberContext); + if (innerMembers.Count != 1) + summaryText += $" ({innerMembers.Count} {GetMemberGroupNoun(innerMembers)})"; + + writer.WriteLine($"| {memberText} | {summaryText} |"); + } } } + } - var remarks = xmlDocMember?.Remarks; - if (remarks != null && remarks.Count != 0) - { - writer.WriteLine(); - writer.WriteLine("## Remarks"); - writer.WriteLine(); - writer.WriteLines(ToMarkdown(remarks, memberContext)); - } + var returnValue = xmlDocMember?.ReturnValue; + if (returnValue != null && returnValue.Count != 0) + { + writer.WriteLine(); + writer.WriteLine("## Return Value"); + writer.WriteLine(); + writer.WriteLines(ToMarkdown(returnValue, memberContext)); + } - var examples = xmlDocMember?.Examples; - if (examples != null && examples.Count != 0) - { - writer.WriteLine(); - writer.WriteLine("## Examples"); - writer.WriteLine(); - writer.WriteLines(ToMarkdown(examples, memberContext)); - } + var propertyValue = xmlDocMember?.PropertyValue; + if (propertyValue != null && propertyValue.Count != 0) + { + writer.WriteLine(); + writer.WriteLine("## Property Value"); + writer.WriteLine(); + writer.WriteLines(ToMarkdown(propertyValue, memberContext)); + } + var exceptions = xmlDocMember?.Exceptions; + if (exceptions != null && exceptions.Count != 0) + { writer.WriteLine(); - writer.WriteLine("## See Also"); + writer.WriteLine("## Exceptions"); writer.WriteLine(); + writer.WriteLine("| exception | condition |"); + writer.WriteLine("| --- | --- |"); - var declaringType = memberInfo.DeclaringType?.GetTypeInfo(); - var declaringTypeXmlDocName = declaringType == null ? null : XmlDocUtility.GetXmlDocRef(declaringType); - if (declaringType != null) - seeAlsoMembers.Add(declaringType); - - foreach (var seeAlso in seeAlsoMembers - .Where(x => XmlDocUtility.GetXmlDocRef(x) != xmlDocRef) - .Select(GetGenericDefinition) - .GroupBy(XmlDocUtility.GetXmlDocRef) - .Select(x => new { Member = x.First(), XmlDocName = x.Key! }) - .OrderBy(x => x.XmlDocName == declaringTypeXmlDocName)) + foreach (var exception in exceptions) { - if (memberContext.MembersByXmlDocName.ContainsKey(seeAlso.XmlDocName!) || - FindExternalDocumentation(seeAlso.Member) != null) - { - var shortSignature = GetShortSignature(seeAlso.Member, forSeeAlso: true); - writer.WriteLine("* " + shortSignature.Prefix + - WrapMarkdownRefLink(shortSignature.Name, seeAlso.Member, memberContext) + shortSignature.Suffix); - } + MemberInfo? exceptionMemberInfo = null; + if (exception.ExceptionTypeRef != null) + memberContext.MembersByXmlDocName.TryGetValue(exception.ExceptionTypeRef, out exceptionMemberInfo); + var text = exceptionMemberInfo != null ? GetShortName(exceptionMemberInfo) : exception.ExceptionTypeRef != null ? XmlDocUtility.GetShortNameForXmlDocRef(exception.ExceptionTypeRef) : ""; + var link = WrapMarkdownRefLink(text, exceptionMemberInfo, memberContext); + writer.WriteLine($"| {link} | {ToMarkdown(exception.Condition.FirstOrDefault()?.Inlines, memberContext) ?? ""} |"); } + } - if (NamespacePages) - { - var namespacePath = GetPermalink(MakeRelative(path, parent)); - writer.WriteLine("* " + $"namespace\u00A0[{GetNamespaceName(declaringType ?? typeInfo!)}]({namespacePath}{extension})"); + var remarks = xmlDocMember?.Remarks; + if (remarks != null && remarks.Count != 0) + { + writer.WriteLine(); + writer.WriteLine("## Remarks"); + writer.WriteLine(); + writer.WriteLines(ToMarkdown(remarks, memberContext)); + } - var assemblyName = (declaringType ?? typeInfo!).Assembly.GetName().Name; - var assemblyPath = GetPermalink(MakeRelative(path, RootPageLocation!)); - writer.WriteLine("* " + $"assembly\u00A0[{assemblyName}]({assemblyPath})"); - } - else - { - writer.WriteLine("* " + $"namespace\u00A0[{GetNamespaceName(declaringType ?? typeInfo!)}](../{(typeInfo != null ? "" : "../")}{GetAssemblyUriName((declaringType ?? typeInfo!).Assembly)}{extension})"); - } + var examples = xmlDocMember?.Examples; + if (examples != null && examples.Count != 0) + { + writer.WriteLine(); + writer.WriteLine("## Examples"); + writer.WriteLine(); + writer.WriteLines(ToMarkdown(examples, memberContext)); + } - if (typeInfo != null && declaringType == null && !string.IsNullOrEmpty(context.SourceCodePath) && !string.IsNullOrEmpty(context.RootNamespace)) - { - var namespaceName = GetNamespaceName(typeInfo); - if (namespaceName.StartsWith(context.RootNamespace, StringComparison.Ordinal)) - { - var directoryPath = context.SourceCodePath + namespaceName.Substring(context.RootNamespace.Length).Replace('.', '/'); - if (!Uri.TryCreate(directoryPath, UriKind.Absolute, out _)) - directoryPath = "../" + directoryPath; - var fileName = GetShortName(typeInfo) + ".cs"; - writer.WriteLine($"* [{fileName}]({directoryPath}/{fileName})"); - } - } + writer.WriteLine(); + writer.WriteLine("## See Also"); + writer.WriteLine(); - if (memberIndex < memberGroup.Count - 1) + var declaringType = memberInfo.DeclaringType?.GetTypeInfo(); + var declaringTypeXmlDocName = declaringType == null ? null : XmlDocUtility.GetXmlDocRef(declaringType); + if (declaringType != null) + seeAlsoMembers.Add(declaringType); + + foreach (var seeAlso in seeAlsoMembers + .Where(x => XmlDocUtility.GetXmlDocRef(x) != xmlDocRef) + .Select(GetGenericDefinition) + .GroupBy(XmlDocUtility.GetXmlDocRef) + .Select(x => new { Member = x.First(), XmlDocName = x.Key! }) + .OrderBy(x => x.XmlDocName == declaringTypeXmlDocName)) + { + if (memberContext.MembersByXmlDocName.ContainsKey(seeAlso.XmlDocName!) || + FindExternalDocumentation(seeAlso.Member) != null) { - writer.WriteLine(); - writer.WriteLine("---"); + var shortSignature = GetShortSignature(seeAlso.Member, forSeeAlso: true); + writer.WriteLine("* " + shortSignature.Prefix + + WrapMarkdownRefLink(shortSignature.Name, seeAlso.Member, memberContext) + shortSignature.Suffix); } } - writer.WriteLine(); - writer.WriteLine(GetCodeGenComment(context.AssemblyFileName)); - }); - } + if (NamespacePages) + { + var namespacePath = GetPermalink(MakeRelative(path, parent)); + writer.WriteLine("* " + $"namespace\u00A0[{GetNamespaceName(declaringType ?? typeInfo!)}]({namespacePath}{extension})"); - private ExternalDocumentation? FindExternalDocumentation(MemberInfo? memberInfo) - { - if (memberInfo == null) - return null; + var assemblyName = (declaringType ?? typeInfo!).Assembly.GetName().Name; + var assemblyPath = GetPermalink(MakeRelative(path, RootPageLocation!)); + writer.WriteLine("* " + $"assembly\u00A0[{assemblyName}]({assemblyPath})"); + } + else + { + writer.WriteLine("* " + $"namespace\u00A0[{GetNamespaceName(declaringType ?? typeInfo!)}](../{(typeInfo != null ? "" : "../")}{GetAssemblyUriName((declaringType ?? typeInfo!).Assembly)}{extension})"); + } - var namespaceName = (memberInfo as TypeInfo ?? memberInfo.DeclaringType.GetTypeInfo()).Namespace; - return ExternalDocs?.FirstOrDefault(x => x.Namespace == namespaceName); - } + if (memberIndex < memberGroup.Count - 1) + { + writer.WriteLine(); + writer.WriteLine("---"); + } + } - private MemberInfo GetGenericDefinition(MemberInfo memberInfo) - { - var typeInfo = memberInfo as TypeInfo; - if (typeInfo != null) - return typeInfo.IsGenericType ? typeInfo.GetGenericTypeDefinition().GetTypeInfo() : typeInfo; + writer.WriteLine(); + writer.WriteLine(GetCodeGenComment(context.AssemblyFileName)); + }); + } - var methodInfo = memberInfo as MethodInfo; - if (methodInfo != null) - return methodInfo.IsGenericMethod ? methodInfo.GetGenericMethodDefinition() : methodInfo; + private ExternalDocumentation? FindExternalDocumentation(MemberInfo? memberInfo) + { + if (memberInfo == null) + return null; - return memberInfo; - } + var namespaceName = (memberInfo as TypeInfo ?? memberInfo.DeclaringType!.GetTypeInfo()).Namespace; + return ExternalDocs?.FirstOrDefault(x => x.Namespace == namespaceName); + } - private bool IsVisible(MemberInfo memberInfo) - { - if (IsBuiltInRecordMember(memberInfo)) - return false; + private MemberInfo GetGenericDefinition(MemberInfo memberInfo) + { + var typeInfo = memberInfo as TypeInfo; + if (typeInfo != null) + return typeInfo.IsGenericType ? typeInfo.GetGenericTypeDefinition().GetTypeInfo() : typeInfo; - var visibility = GetVisibility(memberInfo); - if (IsMorePrivateThan(visibility, Visibility)) - return false; + var methodInfo = memberInfo as MethodInfo; + if (methodInfo != null) + return methodInfo.IsGenericMethod ? methodInfo.GetGenericMethodDefinition() : methodInfo; - if (!IncludeObsolete && memberInfo.GetCustomAttributes().Any()) - return false; + return memberInfo; + } - if (memberInfo.GetCustomAttributes().Any()) - return false; + private bool IsVisible(MemberInfo memberInfo) + { + if (IsBuiltInRecordMember(memberInfo)) + return false; - if (SkipUnbrowsable && memberInfo.GetCustomAttributes().Any(x => x.State == EditorBrowsableState.Never)) - return false; + var visibility = GetVisibility(memberInfo); + if (IsMorePrivateThan(visibility, Visibility)) + return false; - if (SkipCompilerGenerated && memberInfo.GetCustomAttributes().Any()) - return false; + if (!IncludeObsolete && memberInfo.GetCustomAttributes().Any()) + return false; - var name = memberInfo.Name; - if (name.Length == 0 || name[0] == '<') - return false; + if (memberInfo.GetCustomAttributes().Any()) + return false; - // nested types of invisible types are invisible - if (memberInfo.DeclaringType != null && !IsVisible(memberInfo.DeclaringType)) - return false; + if (SkipUnbrowsable && memberInfo.GetCustomAttributes().Any(x => x.State == EditorBrowsableState.Never)) + return false; - if (memberInfo is TypeInfo) - return true; + if (SkipCompilerGenerated && memberInfo.GetCustomAttributes().Any()) + return false; - var methodBase = memberInfo as MethodBase; - if (methodBase == null) - return true; + var name = memberInfo.Name; + if (name.Length == 0 || name[0] == '<') + return false; - if (memberInfo is ConstructorInfo) - return true; + // nested types of invisible types are invisible + if (memberInfo.DeclaringType != null && !IsVisible(memberInfo.DeclaringType)) + return false; - if (!methodBase.IsSpecialName) - return true; + if (memberInfo is TypeInfo) + return true; - if (methodBase.Name.StartsWith("op_", StringComparison.Ordinal)) - return true; + var methodBase = memberInfo as MethodBase; + if (methodBase == null) + return true; - return false; - } + if (memberInfo is ConstructorInfo) + return true; - private static bool IsMorePrivateThan(XmlDocVisibilityLevel visibility1, XmlDocVisibilityLevel visibility2) => (int) visibility1 < (int) visibility2; + if (!methodBase.IsSpecialName) + return true; - public static XmlDocVisibilityLevel GetMostPublic(params XmlDocVisibilityLevel[] visibilityLevels) => (XmlDocVisibilityLevel) visibilityLevels.Max(x => (int) x); + if (methodBase.Name.StartsWith("op_", StringComparison.Ordinal)) + return true; - public static XmlDocVisibilityLevel GetMostPrivate(params XmlDocVisibilityLevel[] visibilityLevels) => (XmlDocVisibilityLevel) visibilityLevels.Min(x => (int) x); + return false; + } - private static string GetMemberHeading(IReadOnlyList membersInfos, int index) - { - var heading = $"{GetFullMemberName(membersInfos[index])} {GetMemberGroupNoun([membersInfos[index]])}"; - if (membersInfos.Count > 1) - heading += $" ({index + 1} of {membersInfos.Count})"; - return heading; - } + private static bool IsMorePrivateThan(XmlDocVisibilityLevel visibility1, XmlDocVisibilityLevel visibility2) => (int) visibility1 < (int) visibility2; - private static string GetMemberGroupNoun(IReadOnlyList memberInfos) - { - var plural = memberInfos.Count != 1; - - if (memberInfos.All(x => x is ConstructorInfo)) - return plural ? "constructors" : "constructor"; - if (memberInfos.All(x => (x as PropertyInfo)?.GetIndexParameters().Length > 0)) - return plural ? "indexers" : "indexer"; - if (memberInfos.All(x => x is PropertyInfo)) - return plural ? "properties" : "property"; - if (memberInfos.All(x => (x as MethodInfo)?.Name.StartsWith("op_", StringComparison.Ordinal) == true)) - return plural ? "operators" : "operator"; - if (memberInfos.All(x => x is MethodInfo)) - return plural ? "methods" : "method"; - if (memberInfos.All(x => x is EventInfo)) - return plural ? "events" : "event"; - if (memberInfos.All(x => x is FieldInfo)) - return plural ? "fields" : "field"; - if (memberInfos.All(x => x is TypeInfo)) - return GetTypeGroupNoun(memberInfos.Cast().ToList()); - return plural ? "members" : "member"; - } + public static XmlDocVisibilityLevel GetMostPublic(params XmlDocVisibilityLevel[] visibilityLevels) => (XmlDocVisibilityLevel) visibilityLevels.Max(x => (int) x); - private static string GetTypeGroupNoun(IReadOnlyList typeInfos) - { - var plural = typeInfos.Count != 1; - - var typeKinds = typeInfos.Select(GetTypeKind).ToList(); - if (typeKinds.All(x => x == TypeKind.Record)) - return plural ? "records" : "record"; - if (typeKinds.All(x => x == TypeKind.Class)) - return plural ? "classes" : "class"; - if (typeKinds.All(x => x == TypeKind.Interface)) - return plural ? "interfaces" : "interface"; - if (typeKinds.All(x => x == TypeKind.Struct)) - return plural ? "structures" : "structure"; - if (typeKinds.All(x => x == TypeKind.Enum)) - return plural ? "enumerations" : "enumeration"; - if (typeKinds.All(x => x == TypeKind.Delegate)) - return plural ? "delegates" : "delegate"; - return plural ? "types" : "type"; - } + public static XmlDocVisibilityLevel GetMostPrivate(params XmlDocVisibilityLevel[] visibilityLevels) => (XmlDocVisibilityLevel) visibilityLevels.Min(x => (int) x); - private static string GetNamespaceName(TypeInfo typeInfo) => typeInfo.Namespace ?? "global"; + private static string GetMemberHeading(IReadOnlyList membersInfos, int index) + { + var heading = $"{GetFullMemberName(membersInfos[index])} {GetMemberGroupNoun([membersInfos[index]])}"; + if (membersInfos.Count > 1) + heading += $" ({index + 1} of {membersInfos.Count})"; + return heading; + } - private static string GetShortName(MemberInfo memberInfo) - { - var name = memberInfo.Name; + private static string GetMemberGroupNoun(IReadOnlyList memberInfos) + { + var plural = memberInfos.Count != 1; + + if (memberInfos.All(x => x is ConstructorInfo)) + return plural ? "constructors" : "constructor"; + if (memberInfos.All(x => (x as PropertyInfo)?.GetIndexParameters().Length > 0)) + return plural ? "indexers" : "indexer"; + if (memberInfos.All(x => x is PropertyInfo)) + return plural ? "properties" : "property"; + if (memberInfos.All(x => (x as MethodInfo)?.Name.StartsWith("op_", StringComparison.Ordinal) == true)) + return plural ? "operators" : "operator"; + if (memberInfos.All(x => x is MethodInfo)) + return plural ? "methods" : "method"; + if (memberInfos.All(x => x is EventInfo)) + return plural ? "events" : "event"; + if (memberInfos.All(x => x is FieldInfo)) + return plural ? "fields" : "field"; + if (memberInfos.All(x => x is TypeInfo)) + return GetTypeGroupNoun(memberInfos.Cast().ToList()); + return plural ? "members" : "member"; + } - var tickIndex = name.IndexOf('`'); - if (tickIndex != -1) - name = name.Substring(0, tickIndex); + private static string GetTypeGroupNoun(IReadOnlyList typeInfos) + { + var plural = typeInfos.Count != 1; + + var typeKinds = typeInfos.Select(GetTypeKind).ToList(); + if (typeKinds.All(x => x == TypeKind.Record)) + return plural ? "records" : "record"; + if (typeKinds.All(x => x == TypeKind.Class)) + return plural ? "classes" : "class"; + if (typeKinds.All(x => x == TypeKind.Interface)) + return plural ? "interfaces" : "interface"; + if (typeKinds.All(x => x == TypeKind.Struct)) + return plural ? "structures" : "structure"; + if (typeKinds.All(x => x == TypeKind.Enum)) + return plural ? "enumerations" : "enumeration"; + if (typeKinds.All(x => x == TypeKind.Delegate)) + return plural ? "delegates" : "delegate"; + return plural ? "types" : "type"; + } - if (name == ".ctor" || name == ".cctor") - name = GetShortName(memberInfo.DeclaringType.GetTypeInfo()); - else if (name == "op_UnaryPlus") - name = "op_Addition"; - else if (name == "op_UnaryNegation") - name = "op_Subtraction"; + private static string GetNamespaceName(TypeInfo typeInfo) => typeInfo.Namespace ?? "global"; - return name; - } + private static string GetShortName(MemberInfo memberInfo) + { + var name = memberInfo.Name; - private static string GetOperatorKeywordName(string name) => - name switch - { - "op_Addition" => "operator +", - "op_BitwiseAnd" => "operator &", - "op_BitwiseOr" => "operator |", - "op_Decrement" => "operator --", - "op_Division" => "operator /", - "op_Equality" => "operator ==", - "op_ExclusiveOr" => "operator ^", - "op_Explicit" => "explicit operator", - "op_False" => "operator false", - "op_GreaterThan" => "operator >", - "op_GreaterThanOrEqual" => "operator >=", - "op_Implicit" => "implicit operator", - "op_Increment" => "operator ++", - "op_Inequality" => "operator !=", - "op_LeftShift" => "operator <<", - "op_LessThan" => "operator <", - "op_LessThanOrEqual" => "operator <=", - "op_LogicalNot" => "operator !", - "op_Modulus" => "operator %", - "op_Multiply" => "operator *", - "op_OnesComplement" => "operator ~", - "op_RightShift" => "operator >>", - "op_Subtraction" => "operator -", - "op_True" => "operator true", - "op_UnaryNegation" => "operator -", - "op_UnaryPlus" => "operator +", - _ => name, - }; + var tickIndex = name.IndexOf('`', StringComparison.Ordinal); + if (tickIndex != -1) + name = name.Substring(0, tickIndex); - private static string GetFullMemberName(MemberInfo memberInfo) - { - var type = memberInfo as TypeInfo; - if (type != null) - return GetFullTypeName(type, t => GetShortName(t) + RenderShortGenericParameters(t.GenericTypeParameters)); + if (name == ".ctor" || name == ".cctor") + name = GetShortName(memberInfo.DeclaringType!.GetTypeInfo()); + else if (name == "op_UnaryPlus") + name = "op_Addition"; + else if (name == "op_UnaryNegation") + name = "op_Subtraction"; - if (memberInfo is ConstructorInfo || (memberInfo as PropertyInfo)?.GetIndexParameters().Length > 0) - return GetFullTypeName(memberInfo.DeclaringType.GetTypeInfo(), t => GetShortName(t) + RenderShortGenericParameters(t.GenericTypeParameters)); + return name; + } - var methodName = (memberInfo as MethodInfo)?.Name; - if (methodName?.StartsWith("op_", StringComparison.Ordinal) == true) - return GetFullTypeName(memberInfo.DeclaringType.GetTypeInfo(), t => GetShortName(t) + RenderShortGenericParameters(t.GenericTypeParameters)) + " " + methodName.Substring(3); + private static string GetOperatorKeywordName(string name) => + name switch + { + "op_Addition" => "operator +", + "op_BitwiseAnd" => "operator &", + "op_BitwiseOr" => "operator |", + "op_Decrement" => "operator --", + "op_Division" => "operator /", + "op_Equality" => "operator ==", + "op_ExclusiveOr" => "operator ^", + "op_Explicit" => "explicit operator", + "op_False" => "operator false", + "op_GreaterThan" => "operator >", + "op_GreaterThanOrEqual" => "operator >=", + "op_Implicit" => "implicit operator", + "op_Increment" => "operator ++", + "op_Inequality" => "operator !=", + "op_LeftShift" => "operator <<", + "op_LessThan" => "operator <", + "op_LessThanOrEqual" => "operator <=", + "op_LogicalNot" => "operator !", + "op_Modulus" => "operator %", + "op_Multiply" => "operator *", + "op_OnesComplement" => "operator ~", + "op_RightShift" => "operator >>", + "op_Subtraction" => "operator -", + "op_True" => "operator true", + "op_UnaryNegation" => "operator -", + "op_UnaryPlus" => "operator +", + _ => name, + }; - return GetFullTypeName(memberInfo.DeclaringType.GetTypeInfo(), t => GetShortName(t) + RenderShortGenericParameters(t.GenericTypeParameters)) + "." + - GetShortName(memberInfo) + RenderShortGenericParameters(GetGenericArguments(memberInfo)); - } + private static string GetFullMemberName(MemberInfo memberInfo) + { + var type = memberInfo as TypeInfo; + if (type != null) + return GetFullTypeName(type, t => GetShortName(t) + RenderShortGenericParameters(t.GenericTypeParameters)); + + if (memberInfo is ConstructorInfo || (memberInfo as PropertyInfo)?.GetIndexParameters().Length > 0) + return GetFullTypeName(memberInfo.DeclaringType!.GetTypeInfo(), t => GetShortName(t) + RenderShortGenericParameters(t.GenericTypeParameters)); + + var methodName = (memberInfo as MethodInfo)?.Name; + if (methodName?.StartsWith("op_", StringComparison.Ordinal) == true) + return string.Concat(GetFullTypeName(memberInfo.DeclaringType!.GetTypeInfo(), t => GetShortName(t) + RenderShortGenericParameters(t.GenericTypeParameters)), " ", methodName.AsSpan(3)); + + return GetFullTypeName(memberInfo.DeclaringType!.GetTypeInfo(), t => GetShortName(t) + RenderShortGenericParameters(t.GenericTypeParameters)) + "." + + GetShortName(memberInfo) + RenderShortGenericParameters(GetGenericArguments(memberInfo)); + } + + private static string GetFullTypeName(TypeInfo typeInfo, Func render) + { + var name = render(typeInfo); + if (typeInfo.DeclaringType != null) + name = $"{GetFullTypeName(typeInfo.DeclaringType.GetTypeInfo(), render)}.{name}"; + return name; + } - private static string GetFullTypeName(TypeInfo typeInfo, Func render) + private sealed class ShortSignature : IEquatable + { + public ShortSignature(string prefix, string name, string suffix) { - var name = render(typeInfo); - if (typeInfo.DeclaringType != null) - name = $"{GetFullTypeName(typeInfo.DeclaringType.GetTypeInfo(), render)}.{name}"; - return name; + Prefix = prefix; + Name = name; + Suffix = suffix; } - private sealed class ShortSignature : IEquatable - { - public ShortSignature(string prefix, string name, string suffix) - { - Prefix = prefix; - Name = name; - Suffix = suffix; - } + public string Prefix { get; } - public string Prefix { get; } + public string Name { get; } - public string Name { get; } + public string Suffix { get; } - public string Suffix { get; } + public bool Equals(ShortSignature? other) => other != null && other.ToString() == ToString(); - public bool Equals(ShortSignature? other) => other != null && other.ToString() == ToString(); + public override bool Equals(object? obj) => Equals(obj as ShortSignature); - public override bool Equals(object? obj) => Equals(obj as ShortSignature); + public override int GetHashCode() => ToString().GetHashCode(StringComparison.Ordinal); - public override int GetHashCode() => ToString().GetHashCode(); + public override string ToString() => Prefix + Name + Suffix; + } - public override string ToString() => Prefix + Name + Suffix; - } + private ShortSignature GetShortSignature(MemberInfo memberInfo, bool forSeeAlso = false) + { + var name = GetOperatorKeywordName(GetShortName(memberInfo)); + var prefix = ""; + var suffix = ""; - private ShortSignature GetShortSignature(MemberInfo memberInfo, bool forSeeAlso = false) + var typeInfo = memberInfo as TypeInfo; + if (typeInfo != null) { - var name = GetOperatorKeywordName(GetShortName(memberInfo)); - var prefix = ""; - var suffix = ""; + name += RenderShortGenericParameters(typeInfo.GenericTypeParameters); - var typeInfo = memberInfo as TypeInfo; - if (typeInfo != null) + prefix = GetTypeKind(typeInfo) switch { - name += RenderShortGenericParameters(typeInfo.GenericTypeParameters); + TypeKind.Record => "record ", + TypeKind.Class => "class ", + TypeKind.Interface => "interface ", + TypeKind.Struct => "struct ", + TypeKind.Enum => "enum ", + TypeKind.Delegate => "delegate ", + _ => prefix, + }; - prefix = GetTypeKind(typeInfo) switch - { - TypeKind.Record => "record ", - TypeKind.Class => "class ", - TypeKind.Interface => "interface ", - TypeKind.Struct => "struct ", - TypeKind.Enum => "enum ", - TypeKind.Delegate => "delegate ", - _ => prefix, - }; + if (!forSeeAlso) + { + if (IsFlagsEnum(typeInfo)) + prefix = "[Flags] " + prefix; + if (IsStatic(typeInfo)) + prefix = "static " + prefix; + else if (IsAbstract(typeInfo)) + prefix = "abstract " + prefix; + else if (IsVirtual(typeInfo)) + prefix = "virtual " + prefix; + else if (IsOverride(typeInfo)) + prefix = "override " + prefix; + } + } + else + { + var eventInfo = memberInfo as EventInfo; + var propertyInfo = memberInfo as PropertyInfo; + var fieldInfo = memberInfo as FieldInfo; + var methodBase = memberInfo as MethodBase; + if (eventInfo != null) + { + prefix = "event "; if (!forSeeAlso) { - if (IsFlagsEnum(typeInfo)) - prefix = "[Flags] " + prefix; - if (IsStatic(typeInfo)) + if (IsStatic(eventInfo)) prefix = "static " + prefix; - else if (IsAbstract(typeInfo)) + else if (IsAbstract(eventInfo)) prefix = "abstract " + prefix; - else if (IsVirtual(typeInfo)) + else if (IsVirtual(eventInfo)) prefix = "virtual " + prefix; - else if (IsOverride(typeInfo)) + else if (IsOverride(eventInfo)) prefix = "override " + prefix; } } - else + else if (propertyInfo != null) { - var eventInfo = memberInfo as EventInfo; - var propertyInfo = memberInfo as PropertyInfo; - var fieldInfo = memberInfo as FieldInfo; - var methodBase = memberInfo as MethodBase; - - if (eventInfo != null) + if (!forSeeAlso) + suffix = GetPropertyGetSet(propertyInfo); + + if (forSeeAlso) + prefix = "property "; + else if (IsStatic(propertyInfo)) + prefix = "static "; + else if (IsAbstract(propertyInfo)) + prefix = "abstract "; + else if (IsVirtual(propertyInfo)) + prefix = "virtual "; + else if (IsOverride(propertyInfo)) + prefix = "override "; + } + else if (fieldInfo != null) + { + if (forSeeAlso) { - prefix = "event "; - if (!forSeeAlso) - { - if (IsStatic(eventInfo)) - prefix = "static " + prefix; - else if (IsAbstract(eventInfo)) - prefix = "abstract " + prefix; - else if (IsVirtual(eventInfo)) - prefix = "virtual " + prefix; - else if (IsOverride(eventInfo)) - prefix = "override " + prefix; - } + prefix = "field "; } - else if (propertyInfo != null) + else { - if (!forSeeAlso) - suffix = GetPropertyGetSet(propertyInfo); - - if (forSeeAlso) - prefix = "property "; - else if (IsStatic(propertyInfo)) + if (IsConst(fieldInfo)) + prefix += "const "; + if (IsStatic(fieldInfo)) prefix = "static "; - else if (IsAbstract(propertyInfo)) - prefix = "abstract "; - else if (IsVirtual(propertyInfo)) - prefix = "virtual "; - else if (IsOverride(propertyInfo)) - prefix = "override "; - } - else if (fieldInfo != null) - { - if (forSeeAlso) - { - prefix = "field "; - } - else - { - if (IsConst(fieldInfo)) - prefix += "const "; - if (IsStatic(fieldInfo)) - prefix = "static "; - if (IsReadOnly(fieldInfo)) - prefix += "readonly "; - } - } - else if (methodBase != null) - { - var isOperator = methodBase.Name.StartsWith("op_", StringComparison.Ordinal); - - if (methodBase is MethodInfo) - name += RenderShortGenericParameters(methodBase.GetGenericArguments()); - - if (!forSeeAlso && !isOperator) - suffix += methodBase.GetParameters().Length == 0 ? "()" : "(…)"; - - if (forSeeAlso) - prefix = "method "; - else if (IsStatic(methodBase) && !isOperator) - prefix = "static " + prefix; - else if (IsAbstract(methodBase)) - prefix = "abstract " + prefix; - else if (IsVirtual(methodBase)) - prefix = "virtual " + prefix; - else if (IsOverride(methodBase)) - prefix = "override " + prefix; + if (IsReadOnly(fieldInfo)) + prefix += "readonly "; } } - - return new ShortSignature(prefix: prefix.Replace(' ', '\u00A0'), name: name, suffix: suffix.Replace(' ', '\u00A0')); + else if (methodBase != null) + { + var isOperator = methodBase.Name.StartsWith("op_", StringComparison.Ordinal); + + if (methodBase is MethodInfo) + name += RenderShortGenericParameters(methodBase.GetGenericArguments()); + + if (!forSeeAlso && !isOperator) + suffix += methodBase.GetParameters().Length == 0 ? "()" : "(…)"; + + if (forSeeAlso) + prefix = "method "; + else if (IsStatic(methodBase) && !isOperator) + prefix = "static " + prefix; + else if (IsAbstract(methodBase)) + prefix = "abstract " + prefix; + else if (IsVirtual(methodBase)) + prefix = "virtual " + prefix; + else if (IsOverride(methodBase)) + prefix = "override " + prefix; + } } - private string GetPropertyGetSet(PropertyInfo propertyInfo) - { - var getMethod = propertyInfo.GetMethod; - var setMethod = propertyInfo.SetMethod; - if (getMethod == null && setMethod == null) - throw new InvalidOperationException(); - - var getVisibility = getMethod == null ? XmlDocVisibilityLevel.Private : GetMethodVisibility(getMethod); - var setVisibility = setMethod == null ? XmlDocVisibilityLevel.Private : GetMethodVisibility(setMethod); - - if (getMethod != null && (setMethod == null || IsMorePrivateThan(setVisibility, Visibility))) - return " { get; }"; - if (getMethod == null || IsMorePrivateThan(getVisibility, Visibility)) - return " { set; }"; - - if (getVisibility == setVisibility) - return " { get; set; }"; - if (IsMorePrivateThan(getVisibility, setVisibility)) - return $" {{ {GetAccessModifier(getMethod)} get; set; }}"; - return $" {{ get; {GetAccessModifier(setMethod!)} set; }}"; - } + return new ShortSignature(prefix: prefix.Replace(' ', '\u00A0'), name: name, suffix: suffix.Replace(' ', '\u00A0')); + } + + private string GetPropertyGetSet(PropertyInfo propertyInfo) + { + var getMethod = propertyInfo.GetMethod; + var setMethod = propertyInfo.SetMethod; + if (getMethod == null && setMethod == null) + throw new InvalidOperationException(); + + var getVisibility = getMethod == null ? XmlDocVisibilityLevel.Private : GetMethodVisibility(getMethod); + var setVisibility = setMethod == null ? XmlDocVisibilityLevel.Private : GetMethodVisibility(setMethod); + + if (getMethod != null && (setMethod == null || IsMorePrivateThan(setVisibility, Visibility))) + return " { get; }"; + if (getMethod == null || IsMorePrivateThan(getVisibility, Visibility)) + return " { set; }"; + + if (getVisibility == setVisibility) + return " { get; set; }"; + if (IsMorePrivateThan(getVisibility, setVisibility)) + return $" {{ {GetAccessModifier(getMethod)} get; set; }}"; + return $" {{ get; {GetAccessModifier(setMethod!)} set; }}"; + } + + private string GetFullSignature(MemberInfo memberInfo, ICollection seeAlsoMembers) + { + var stringBuilder = new StringBuilder(); + var lineBuilder = new StringBuilder(); + var segmentBuilder = new StringBuilder(); + const int maxLineLength = 100; - private string GetFullSignature(MemberInfo memberInfo, ICollection seeAlsoMembers) + void WrapLineIfNecessary(bool hard) { - var stringBuilder = new StringBuilder(); - var lineBuilder = new StringBuilder(); - var segmentBuilder = new StringBuilder(); - const int maxLineLength = 100; + const string indent = " "; - void WrapLineIfNecessary(bool hard) + if (lineBuilder.Length > indent.Length && (hard || lineBuilder.Length + segmentBuilder.Length > maxLineLength)) { - const string indent = " "; - - if (lineBuilder.Length > indent.Length && (hard || lineBuilder.Length + segmentBuilder.Length > maxLineLength)) - { - lineBuilder.Append(ActualNewLine); - stringBuilder.Append(lineBuilder); - lineBuilder.Clear(); + lineBuilder.Append(ActualNewLine); + stringBuilder.Append(lineBuilder); + lineBuilder.Clear(); - if (!hard) - lineBuilder.Append(indent); - } + if (!hard) + lineBuilder.Append(indent); } + } - foreach (var part in GetFullSignatureParts(memberInfo, seeAlsoMembers)) + foreach (var part in GetFullSignatureParts(memberInfo, seeAlsoMembers)) + { + if (part.Length == 0 || part == ActualNewLine) { - if (part.Length == 0 || part == ActualNewLine) - { - WrapLineIfNecessary(false); + WrapLineIfNecessary(false); - lineBuilder.Append(segmentBuilder); - segmentBuilder.Clear(); + lineBuilder.Append(segmentBuilder); + segmentBuilder.Clear(); - WrapLineIfNecessary(part == ActualNewLine); - } - else - { - segmentBuilder.Append(part); - } + WrapLineIfNecessary(part == ActualNewLine); + } + else + { + segmentBuilder.Append(part); } + } - WrapLineIfNecessary(false); + WrapLineIfNecessary(false); - lineBuilder.Append(segmentBuilder); - stringBuilder.Append(lineBuilder); - return stringBuilder.ToString(); - } + lineBuilder.Append(segmentBuilder); + stringBuilder.Append(lineBuilder); + return stringBuilder.ToString(); + } - private IEnumerable GetFullSignatureParts(MemberInfo memberInfo, ICollection seeAlsoMembers) - { - var typeInfo = memberInfo as TypeInfo; - var typeKind = typeInfo == null ? default(TypeKind?) : GetTypeKind(typeInfo); + private IEnumerable GetFullSignatureParts(MemberInfo memberInfo, ICollection seeAlsoMembers) + { + var typeInfo = memberInfo as TypeInfo; + var typeKind = typeInfo == null ? default(TypeKind?) : GetTypeKind(typeInfo); - var obsoleteAttribute = memberInfo.GetCustomAttribute(); - if (obsoleteAttribute != null) + var obsoleteAttribute = memberInfo.GetCustomAttribute(); + if (obsoleteAttribute != null) + { + var message = obsoleteAttribute.Message; + if (string.IsNullOrWhiteSpace(message)) { - var message = obsoleteAttribute.Message; - if (string.IsNullOrWhiteSpace(message)) - { - yield return "[Obsolete]"; - } - else - { - yield return "[Obsolete("; - yield return RenderConstant(message); - yield return ")]"; - } - yield return ActualNewLine; + yield return "[Obsolete]"; } - - var browsableAttribute = memberInfo.GetCustomAttribute(); - if (browsableAttribute != null && browsableAttribute.State != EditorBrowsableState.Always) + else { - yield return "[EditorBrowsable("; - yield return RenderConstant(browsableAttribute.State); + yield return "[Obsolete("; + yield return RenderConstant(message); yield return ")]"; - yield return ActualNewLine; } + yield return ActualNewLine; + } - var attributeUsage = memberInfo.GetCustomAttribute(); - if (attributeUsage != null) - { - yield return "[AttributeUsage("; - - yield return RenderConstant(attributeUsage.ValidOn); + var browsableAttribute = memberInfo.GetCustomAttribute(); + if (browsableAttribute != null && browsableAttribute.State != EditorBrowsableState.Always) + { + yield return "[EditorBrowsable("; + yield return RenderConstant(browsableAttribute.State); + yield return ")]"; + yield return ActualNewLine; + } - if (!attributeUsage.Inherited) - { - yield return ", "; - yield return ""; - yield return "Inherited = false"; - } + var attributeUsage = memberInfo.GetCustomAttribute(); + if (attributeUsage != null) + { + yield return "[AttributeUsage("; - if (attributeUsage.AllowMultiple) - { - yield return ", "; - yield return ""; - yield return "AllowMultiple = true"; - } + yield return RenderConstant(attributeUsage.ValidOn); - yield return ")]"; - yield return ActualNewLine; + if (!attributeUsage.Inherited) + { + yield return ", "; + yield return ""; + yield return "Inherited = false"; } - if (IsFlagsEnum(memberInfo)) + if (attributeUsage.AllowMultiple) { - yield return "[Flags]"; - yield return ActualNewLine; + yield return ", "; + yield return ""; + yield return "AllowMultiple = true"; } - yield return $"{GetAccessModifier(memberInfo)} "; + yield return ")]"; + yield return ActualNewLine; + } + + if (IsFlagsEnum(memberInfo)) + { + yield return "[Flags]"; + yield return ActualNewLine; + } + + yield return $"{GetAccessModifier(memberInfo)} "; + + if (IsStatic(memberInfo)) + yield return "static "; + else if (typeKind == TypeKind.Class && typeInfo!.IsSealed) + yield return "sealed "; + else if (IsAbstract(memberInfo)) + yield return "abstract "; + else if (IsVirtual(memberInfo)) + yield return "virtual "; + else if (IsOverride(memberInfo)) + yield return "override "; - if (IsStatic(memberInfo)) - yield return "static "; - else if (typeKind == TypeKind.Class && typeInfo!.IsSealed) - yield return "sealed "; - else if (IsAbstract(memberInfo)) - yield return "abstract "; - else if (IsVirtual(memberInfo)) - yield return "virtual "; - else if (IsOverride(memberInfo)) - yield return "override "; + if (IsConst(memberInfo)) + yield return "const "; + if (IsReadOnly(memberInfo)) + yield return "readonly "; - if (IsConst(memberInfo)) - yield return "const "; - if (IsReadOnly(memberInfo)) - yield return "readonly "; + if (memberInfo is EventInfo) + yield return "event "; - if (memberInfo is EventInfo) - yield return "event "; + switch (typeKind) + { + case TypeKind.Record: + yield return "record "; + break; + case TypeKind.Class: + yield return "class "; + break; + case TypeKind.Interface: + yield return "interface "; + break; + case TypeKind.Struct: + yield return "struct "; + break; + case TypeKind.Enum: + yield return "enum "; + break; + case TypeKind.Delegate: + yield return "delegate "; + break; + } + + var shortName = GetOperatorKeywordName(GetShortName(memberInfo)); + if (shortName == "Item" && memberInfo is PropertyInfo) + shortName = "this"; + + var isConversion = shortName == "explicit operator" || shortName == "implicit operator"; - switch (typeKind) + var nullableContextFlags = GetNullableContextFlags(memberInfo.GetCustomAttributes()); + if (nullableContextFlags.Length == 0) + { + var ancestor = memberInfo; + while (true) { - case TypeKind.Record: - yield return "record "; - break; - case TypeKind.Class: - yield return "class "; - break; - case TypeKind.Interface: - yield return "interface "; - break; - case TypeKind.Struct: - yield return "struct "; + ancestor = ancestor.DeclaringType?.GetTypeInfo(); + if (ancestor is null) break; - case TypeKind.Enum: - yield return "enum "; - break; - case TypeKind.Delegate: - yield return "delegate "; + nullableContextFlags = GetNullableContextFlags(ancestor.GetCustomAttributes()); + if (nullableContextFlags.Length != 0) break; } + } - var shortName = GetOperatorKeywordName(GetShortName(memberInfo)); - if (shortName == "Item" && memberInfo is PropertyInfo) - shortName = "this"; - - var isConversion = shortName == "explicit operator" || shortName == "implicit operator"; + var (valueType, valueAttributes) = GetValueType(memberInfo); + if (valueType != null && !isConversion) + { + yield return RenderTypeName(valueType, seeAlsoMembers, valueAttributes, nullableContextFlags); + yield return " "; + } - var nullableContextFlags = GetNullableContextFlags(memberInfo.GetCustomAttributes()); - if (nullableContextFlags.Length == 0) - { - var ancestor = memberInfo; - while (true) - { - ancestor = ancestor.DeclaringType?.GetTypeInfo(); - if (ancestor is null) - break; - nullableContextFlags = GetNullableContextFlags(ancestor.GetCustomAttributes()); - if (nullableContextFlags.Length != 0) - break; - } - } + if (valueType != null && isConversion) + { + yield return shortName; + yield return " "; + yield return RenderTypeName(valueType, seeAlsoMembers, valueAttributes, nullableContextFlags); + } + else + { + yield return ""; + yield return shortName; + } - var (valueType, valueAttributes) = GetValueType(memberInfo); - if (valueType != null && !isConversion) - { - yield return RenderTypeName(valueType, seeAlsoMembers, valueAttributes, nullableContextFlags); - yield return " "; - } + var genericParameters = GetGenericArguments(memberInfo); + if (genericParameters.Length != 0) + yield return RenderGenericParameters(genericParameters); - if (valueType != null && isConversion) - { - yield return shortName; - yield return " "; - yield return RenderTypeName(valueType, seeAlsoMembers, valueAttributes, nullableContextFlags); - } - else + if (typeKind is TypeKind.Record or TypeKind.Class or TypeKind.Struct or TypeKind.Interface) + { + var isFirstBase = true; + if (typeKind == TypeKind.Class && typeInfo!.BaseType != typeof(object)) { + yield return " : "; yield return ""; - yield return shortName; + yield return RenderTypeName(typeInfo.BaseType!.GetTypeInfo(), seeAlsoMembers); + isFirstBase = false; } - var genericParameters = GetGenericArguments(memberInfo); - if (genericParameters.Length != 0) - yield return RenderGenericParameters(genericParameters); - - if (typeKind is TypeKind.Record or TypeKind.Class or TypeKind.Struct or TypeKind.Interface) + var baseInterfaces = typeInfo!.ImplementedInterfaces.Select(x => x.GetTypeInfo()) + .Where(x => x.IsPublic) + .OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase) + .ThenBy(x => x.IsGenericType ? x.GenericTypeArguments.Length : 0) + .ThenBy(x => x.IsGenericType ? RenderGenericArguments(x.GenericTypeArguments) : "", StringComparer.OrdinalIgnoreCase) + .ToList(); + var baseTypeInterfaces = typeInfo.BaseType?.GetTypeInfo().ImplementedInterfaces.Select(x => x.GetTypeInfo()).ToList(); + foreach (var baseInterface in baseInterfaces) { - var isFirstBase = true; - if (typeKind == TypeKind.Class && typeInfo!.BaseType != typeof(object)) + if (!(typeKind == TypeKind.Class && baseTypeInterfaces!.Contains(baseInterface)) && + !baseInterfaces.Any(x => XmlDocUtility.GetXmlDocRef(x) != XmlDocUtility.GetXmlDocRef(baseInterface) && IsLessDerived(baseInterface, x)) && + !(typeKind == TypeKind.Record && baseInterface.Name == "IEquatable`1" && baseInterface.GenericTypeArguments[0] == typeInfo.AsType())) { - yield return " : "; + yield return isFirstBase ? " : " : ", "; yield return ""; - yield return RenderTypeName(typeInfo.BaseType.GetTypeInfo(), seeAlsoMembers); + yield return RenderTypeName(baseInterface, seeAlsoMembers); isFirstBase = false; } - - var baseInterfaces = typeInfo!.ImplementedInterfaces.Select(x => x.GetTypeInfo()) - .Where(x => x.IsPublic) - .OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase) - .ThenBy(x => x.IsGenericType ? x.GenericTypeArguments.Length : 0) - .ThenBy(x => x.IsGenericType ? RenderGenericArguments(x.GenericTypeArguments) : "", StringComparer.OrdinalIgnoreCase) - .ToList(); - var baseTypeInterfaces = typeInfo.BaseType?.GetTypeInfo().ImplementedInterfaces.Select(x => x.GetTypeInfo()).ToList(); - foreach (var baseInterface in baseInterfaces) - { - if (!(typeKind == TypeKind.Class && baseTypeInterfaces!.Contains(baseInterface)) && - !baseInterfaces.Any(x => XmlDocUtility.GetXmlDocRef(x) != XmlDocUtility.GetXmlDocRef(baseInterface) && IsLessDerived(baseInterface, x)) && - !(typeKind == TypeKind.Record && baseInterface.Name == "IEquatable`1" && baseInterface.GenericTypeArguments[0] == typeInfo.AsType())) - { - yield return isFirstBase ? " : " : ", "; - yield return ""; - yield return RenderTypeName(baseInterface, seeAlsoMembers); - isFirstBase = false; - } - } } + } - if (typeKind == TypeKind.Enum && Enum.GetUnderlyingType(typeInfo!.AsType()) != typeof(int)) - { - yield return " : "; - yield return RenderTypeName(Enum.GetUnderlyingType(typeInfo.AsType()).GetTypeInfo(), seeAlsoMembers); - } + if (typeKind == TypeKind.Enum && Enum.GetUnderlyingType(typeInfo!.AsType()) != typeof(int)) + { + yield return " : "; + yield return RenderTypeName(Enum.GetUnderlyingType(typeInfo.AsType()).GetTypeInfo(), seeAlsoMembers); + } - ParameterInfo[]? parameterInfos = null; + ParameterInfo[]? parameterInfos = null; - var propertyInfo = memberInfo as PropertyInfo; - if (propertyInfo != null) - { - parameterInfos = GetParameters(propertyInfo); - if (parameterInfos.Length == 0) - parameterInfos = null; - } + var propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) + { + parameterInfos = GetParameters(propertyInfo); + if (parameterInfos.Length == 0) + parameterInfos = null; + } - var methodInfo = memberInfo as MethodBase ?? TryGetDelegateInvoke(memberInfo); - if (methodInfo != null) - parameterInfos = GetParameters(methodInfo); + var methodInfo = memberInfo as MethodBase ?? TryGetDelegateInvoke(memberInfo); + if (methodInfo != null) + parameterInfos = GetParameters(methodInfo); - if (parameterInfos != null) - { - yield return propertyInfo != null ? "[" : "("; - yield return ""; + if (parameterInfos != null) + { + yield return propertyInfo != null ? "[" : "("; + yield return ""; - var isFirstParameter = true; - foreach (var (parameterInfo, index) in parameterInfos.Select((x, i) => (x, i))) + var isFirstParameter = true; + foreach (var (parameterInfo, index) in parameterInfos.Select((x, i) => (x, i))) + { + if (!isFirstParameter) { - if (!isFirstParameter) - { - yield return ", "; - yield return ""; - } - - if (parameterInfo.GetCustomAttributes().Any()) - yield return "[CallerFilePath] "; - if (parameterInfo.GetCustomAttributes().Any()) - yield return "[CallerLineNumber] "; - if (parameterInfo.GetCustomAttributes().Any()) - yield return "[CallerMemberName] "; + yield return ", "; + yield return ""; + } - if (isFirstParameter && IsStatic(memberInfo) && memberInfo.GetCustomAttributes().Any()) - yield return "this "; + if (parameterInfo.GetCustomAttributes().Any()) + yield return "[CallerFilePath] "; + if (parameterInfo.GetCustomAttributes().Any()) + yield return "[CallerLineNumber] "; + if (parameterInfo.GetCustomAttributes().Any()) + yield return "[CallerMemberName] "; - if (parameterInfo.ParameterType.IsByRef) - yield return parameterInfo.IsOut ? "out " : "ref "; - if (parameterInfo.GetCustomAttributes().Any()) - yield return "params "; + if (isFirstParameter && IsStatic(memberInfo) && memberInfo.GetCustomAttributes().Any()) + yield return "this "; - yield return RenderTypeName(parameterInfo.ParameterType.GetTypeInfo(), - seeAlso: seeAlsoMembers, - attributes: parameterInfo.GetCustomAttributes().ToList(), - nullableContextFlags: nullableContextFlags); + if (parameterInfo.ParameterType.IsByRef) + yield return parameterInfo.IsOut ? "out " : "ref "; + if (parameterInfo.GetCustomAttributes().Any()) + yield return "params "; - yield return " "; - if (IsKeyword(parameterInfo.Name)) - yield return "@"; - yield return parameterInfo.Name ?? "P_" + index.ToString(CultureInfo.InvariantCulture); // default as per ILSpy if null + yield return RenderTypeName(parameterInfo.ParameterType.GetTypeInfo(), + seeAlso: seeAlsoMembers, + attributes: parameterInfo.GetCustomAttributes().ToList(), + nullableContextFlags: nullableContextFlags); - if (ParameterHasDefaultValue(parameterInfo)) - { - yield return " = "; - if (CanRenderParameterConstant(parameterInfo)) - yield return RenderConstant(parameterInfo.DefaultValue); - else if (parameterInfo.ParameterType.GetTypeInfo().IsValueType || parameterInfo.ParameterType.IsGenericParameter) - yield return "default"; - else - yield return "null"; - } + yield return " "; + if (IsKeyword(parameterInfo.Name!)) + yield return "@"; + yield return parameterInfo.Name ?? "P_" + index.ToString(CultureInfo.InvariantCulture); // default as per ILSpy if null - isFirstParameter = false; + if (ParameterHasDefaultValue(parameterInfo)) + { + yield return " = "; + if (CanRenderParameterConstant(parameterInfo)) + yield return RenderConstant(parameterInfo.DefaultValue!); + else if (parameterInfo.ParameterType.GetTypeInfo().IsValueType || parameterInfo.ParameterType.IsGenericParameter) + yield return "default"; + else + yield return "null"; } - yield return propertyInfo != null ? "]" : ")"; + isFirstParameter = false; } - if (propertyInfo != null) - yield return GetPropertyGetSet(propertyInfo); + yield return propertyInfo != null ? "]" : ")"; + } + + if (propertyInfo != null) + yield return GetPropertyGetSet(propertyInfo); - foreach (var genericParameter in genericParameters) + foreach (var genericParameter in genericParameters) + { + var isFirstPart = true; + + if (genericParameter.GetTypeInfo().GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint)) { - var isFirstPart = true; + yield return ActualNewLine; + yield return $" where {genericParameter.Name} : "; + + yield return "class"; + isFirstPart = false; + } - if (genericParameter.GetTypeInfo().GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint)) + var isStruct = genericParameter.GetTypeInfo().GenericParameterAttributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint); + if (isStruct) + { + if (isFirstPart) { yield return ActualNewLine; yield return $" where {genericParameter.Name} : "; - - yield return "class"; - isFirstPart = false; } - - var isStruct = genericParameter.GetTypeInfo().GenericParameterAttributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint); - if (isStruct) + else { - if (isFirstPart) - { - yield return ActualNewLine; - yield return $" where {genericParameter.Name} : "; - } - else - { - yield return ", "; - } - - yield return "struct"; - isFirstPart = false; + yield return ", "; } - var genericConstraints = genericParameter.GetTypeInfo().GetGenericParameterConstraints(); - foreach (var genericConstraint in genericConstraints.Where(x => x != typeof(ValueType))) - { - if (isFirstPart) - { - yield return ActualNewLine; - yield return $" where {genericParameter.Name} : "; - } - else - { - yield return ", "; - } + yield return "struct"; + isFirstPart = false; + } - yield return RenderTypeName(genericConstraint.GetTypeInfo(), seeAlsoMembers); - isFirstPart = false; + var genericConstraints = genericParameter.GetTypeInfo().GetGenericParameterConstraints(); + foreach (var genericConstraint in genericConstraints.Where(x => x != typeof(ValueType))) + { + if (isFirstPart) + { + yield return ActualNewLine; + yield return $" where {genericParameter.Name} : "; } - - if (!isStruct && genericParameter.GetTypeInfo().GenericParameterAttributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint)) + else { - if (isFirstPart) - { - yield return ActualNewLine; - yield return $" where {genericParameter.Name} : "; - } - else - { - yield return ", "; - } - - yield return "new()"; + yield return ", "; } + + yield return RenderTypeName(genericConstraint.GetTypeInfo(), seeAlsoMembers); + isFirstPart = false; } - if (typeKind == TypeKind.Delegate || memberInfo is EventInfo || memberInfo is FieldInfo) - yield return ";"; + if (!isStruct && genericParameter.GetTypeInfo().GenericParameterAttributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint)) + { + if (isFirstPart) + { + yield return ActualNewLine; + yield return $" where {genericParameter.Name} : "; + } + else + { + yield return ", "; + } + + yield return "new()"; + } } - private static (TypeInfo? Type, IReadOnlyList? Attributes) GetValueType(MemberInfo member) - { - var eventInfo = member as EventInfo; - if (eventInfo != null) - return (eventInfo.EventHandlerType.GetTypeInfo(), eventInfo.GetCustomAttributes().ToList()); + if (typeKind == TypeKind.Delegate || memberInfo is EventInfo || memberInfo is FieldInfo) + yield return ";"; + } - var propertyInfo = member as PropertyInfo; - if (propertyInfo != null) - return (propertyInfo.PropertyType.GetTypeInfo(), propertyInfo.GetCustomAttributes().ToList()); + private static (TypeInfo? Type, IReadOnlyList? Attributes) GetValueType(MemberInfo member) + { + var eventInfo = member as EventInfo; + if (eventInfo != null) + return (eventInfo.EventHandlerType!.GetTypeInfo(), eventInfo.GetCustomAttributes().ToList()); - var fieldInfo = member as FieldInfo; - if (fieldInfo != null) - return (fieldInfo.FieldType.GetTypeInfo(), fieldInfo.GetCustomAttributes().ToList()); + var propertyInfo = member as PropertyInfo; + if (propertyInfo != null) + return (propertyInfo.PropertyType.GetTypeInfo(), propertyInfo.GetCustomAttributes().ToList()); - var methodInfo = member as MethodInfo ?? TryGetDelegateInvoke(member); - if (methodInfo != null) - return (methodInfo.ReturnType.GetTypeInfo(), methodInfo.ReturnTypeCustomAttributes.GetCustomAttributes(inherit: false).OfType().ToList()); + var fieldInfo = member as FieldInfo; + if (fieldInfo != null) + return (fieldInfo.FieldType.GetTypeInfo(), fieldInfo.GetCustomAttributes().ToList()); - return default; - } + var methodInfo = member as MethodInfo ?? TryGetDelegateInvoke(member); + if (methodInfo != null) + return (methodInfo.ReturnType.GetTypeInfo(), methodInfo.ReturnTypeCustomAttributes.GetCustomAttributes(inherit: false).OfType().ToList()); - private static byte[] GetNullableContextFlags(IEnumerable attributes) - { - var attribute = attributes.FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableContextAttribute"); - return attribute is null ? [] : [(byte) attribute.GetType().GetField("Flag").GetValue(attribute)]; - } + return default; + } - private static byte[] GetNullableFlags(IEnumerable attributes) - { - var attribute = attributes.FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableAttribute"); - return ((byte[]?) attribute?.GetType().GetField("NullableFlags").GetValue(attribute)) ?? []; - } + private static byte[] GetNullableContextFlags(IEnumerable attributes) + { + var attribute = attributes.FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableContextAttribute"); + return attribute is null ? [] : [(byte) attribute.GetType().GetField("Flag")!.GetValue(attribute)!]; + } - private static bool ParameterHasDefaultValue(ParameterInfo parameterInfo) - { - if (parameterInfo.Attributes.HasFlag(ParameterAttributes.HasDefault)) - return true; + private static byte[] GetNullableFlags(IEnumerable attributes) + { + var attribute = attributes.FirstOrDefault(x => x.GetType().FullName == "System.Runtime.CompilerServices.NullableAttribute"); + return ((byte[]?) attribute?.GetType().GetField("NullableFlags")!.GetValue(attribute)!) ?? []; + } - if (parameterInfo.ParameterType == typeof(decimal) || parameterInfo.ParameterType == typeof(decimal?)) - return parameterInfo.HasDefaultValue; + private static bool ParameterHasDefaultValue(ParameterInfo parameterInfo) + { + if (parameterInfo.Attributes.HasFlag(ParameterAttributes.HasDefault)) + return true; - return false; - } + if (parameterInfo.ParameterType == typeof(decimal) || parameterInfo.ParameterType == typeof(decimal?)) + return parameterInfo.HasDefaultValue; - private static bool CanRenderParameterConstant(ParameterInfo parameterInfo) - { - return TryGetBuiltInTypeName(parameterInfo.ParameterType) != null || - TryGetBuiltInTypeName(Nullable.GetUnderlyingType(parameterInfo.ParameterType)) != null || - parameterInfo.ParameterType.GetTypeInfo().IsEnum; - } + return false; + } - private static string RenderConstant(object value) - { - if (value == null) - return "null"; + private static bool CanRenderParameterConstant(ParameterInfo parameterInfo) + { + return TryGetBuiltInTypeName(parameterInfo.ParameterType) != null || + TryGetBuiltInTypeName(Nullable.GetUnderlyingType(parameterInfo.ParameterType)) != null || + parameterInfo.ParameterType.GetTypeInfo().IsEnum; + } - if (value is bool valueAsBool) - return valueAsBool ? "true" : "false"; + private static string RenderConstant(object value) + { + if (value == null) + return "null"; - if (value is char valueAsChar) - return RenderChar(valueAsChar); + if (value is bool valueAsBool) + return valueAsBool ? "true" : "false"; - if (value is string valueAsString) - return RenderString(valueAsString); + if (value is char valueAsChar) + return RenderChar(valueAsChar); - var type = value.GetType(); - if (type.GetTypeInfo().IsEnum) - { - return string.Join(" | ", value.ToString() - .Split([", "], StringSplitOptions.None) - .Select(x => $"{type.Name}.{x}")); - } + if (value is string valueAsString) + return RenderString(valueAsString); + + var type = value.GetType(); + if (type.GetTypeInfo().IsEnum) + { + return string.Join(" | ", value.ToString()! + .Split([", "], StringSplitOptions.None) + .Select(x => $"{type.Name}.{x}")); + } - var rendered = Convert.ToString(value, CultureInfo.InvariantCulture); + var rendered = Convert.ToString(value, CultureInfo.InvariantCulture)!; - if (value is double) - rendered += "m"; + if (value is double) + rendered += "m"; - return rendered; - } + return rendered; + } - private static string RenderString(string value) - { - var builder = new StringBuilder("\""); - foreach (var ch in value) - builder.Append(ch == '\'' ? "'" : EscapeChar(ch)); - return builder.Append('"').ToString(); - } + private static string RenderString(string value) + { + var builder = new StringBuilder("\""); + foreach (var ch in value) + builder.Append(ch == '\'' ? "'" : EscapeChar(ch)); + return builder.Append('"').ToString(); + } - private static string RenderChar(char ch) => "'" + (ch == '\"' ? "\"" : EscapeChar(ch)) + "'"; + private static string RenderChar(char ch) => "'" + (ch == '\"' ? "\"" : EscapeChar(ch)) + "'"; + + private static string EscapeChar(char ch) => + ch switch + { + '\'' => @"\'", + '\"' => @"\""", + '\\' => @"\\", + '\a' => @"\a", + '\b' => @"\b", + '\f' => @"\f", + '\n' => @"\n", + '\r' => @"\r", + '\t' => @"\t", + '\v' => @"\v", + _ => char.IsControl(ch) ? $"\\u{(int) ch:x4}" : ch.ToString(), + }; - private static string EscapeChar(char ch) => - ch switch - { - '\'' => @"\'", - '\"' => @"\""", - '\\' => @"\\", - '\a' => @"\a", - '\b' => @"\b", - '\f' => @"\f", - '\n' => @"\n", - '\r' => @"\r", - '\t' => @"\t", - '\v' => @"\v", - _ => char.IsControl(ch) ? $"\\u{(int) ch:x4}" : ch.ToString(), - }; + private static string RenderGenericParameters(Type[] genericParameters) + { + var stringBuilder = new StringBuilder(); + for (var index = 0; index < genericParameters.Length; index++) + { + var genericParameter = genericParameters[index]; + stringBuilder.Append((index == 0 ? "<" : "") + + (genericParameter.GetTypeInfo().GenericParameterAttributes.HasFlag(GenericParameterAttributes.Covariant) ? "out " : "") + + (genericParameter.GetTypeInfo().GenericParameterAttributes.HasFlag(GenericParameterAttributes.Contravariant) ? "in " : "") + + genericParameter.Name + + (index < genericParameters.Length - 1 ? ", " : "") + + (index == genericParameters.Length - 1 ? ">" : "")); + } + return stringBuilder.ToString(); + } - private static string RenderGenericParameters(Type[] genericParameters) + private static string RenderShortGenericParameters(Type[] genericParameters) + { + if (genericParameters == null) + return ""; + + var stringBuilder = new StringBuilder(); + for (var index = 0; index < genericParameters.Length; index++) { - var stringBuilder = new StringBuilder(); - for (var index = 0; index < genericParameters.Length; index++) - { - var genericParameter = genericParameters[index]; - stringBuilder.Append((index == 0 ? "<" : "") + - (genericParameter.GetTypeInfo().GenericParameterAttributes.HasFlag(GenericParameterAttributes.Covariant) ? "out " : "") + - (genericParameter.GetTypeInfo().GenericParameterAttributes.HasFlag(GenericParameterAttributes.Contravariant) ? "in " : "") + - genericParameter.Name + - (index < genericParameters.Length - 1 ? ", " : "") + - (index == genericParameters.Length - 1 ? ">" : "")); - } - return stringBuilder.ToString(); + var genericParameter = genericParameters[index]; + stringBuilder.Append((index == 0 ? "<" : "") + + genericParameter.Name + + (index < genericParameters.Length - 1 ? "," : "") + + (index == genericParameters.Length - 1 ? ">" : "")); } + return stringBuilder.ToString(); + } - private static string RenderShortGenericParameters(Type[] genericParameters) - { - if (genericParameters == null) - return ""; + private static string RenderTypeName(TypeInfo typeInfo, ICollection? seeAlso = null, IReadOnlyList? attributes = null, byte[]? nullableContextFlags = null) + { + attributes ??= []; + var tupleNames = GetTupleNames(attributes); + var tupleNameIndex = 0; + var nullableFlags = GetNullableFlags(attributes); + if (nullableFlags.Length == 0) + nullableFlags = nullableContextFlags ?? []; + var nullableFlagIndex = 0; + return RenderTypeName(typeInfo, seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex); + } - var stringBuilder = new StringBuilder(); - for (var index = 0; index < genericParameters.Length; index++) - { - var genericParameter = genericParameters[index]; - stringBuilder.Append((index == 0 ? "<" : "") + - genericParameter.Name + - (index < genericParameters.Length - 1 ? "," : "") + - (index == genericParameters.Length - 1 ? ">" : "")); - } - return stringBuilder.ToString(); - } + private static string RenderTypeName(TypeInfo typeInfo, ICollection? seeAlso, IReadOnlyList tupleNames, ref int tupleNameIndex, byte[] nullableFlags, ref int nullableFlagIndex) + { + if (typeInfo.IsByRef) + return RenderTypeName(typeInfo.GetElementType()!.GetTypeInfo(), seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex); - private static string RenderTypeName(TypeInfo typeInfo, ICollection? seeAlso = null, IReadOnlyList? attributes = null, byte[]? nullableContextFlags = null) - { - attributes ??= []; - var tupleNames = GetTupleNames(attributes); - var tupleNameIndex = 0; - var nullableFlags = GetNullableFlags(attributes); - if (nullableFlags.Length == 0) - nullableFlags = nullableContextFlags ?? []; - var nullableFlagIndex = 0; - return RenderTypeName(typeInfo, seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex); - } + var nullableOfType = Nullable.GetUnderlyingType(typeInfo.AsType()); + if (nullableOfType != null) + return $"{RenderTypeName(nullableOfType.GetTypeInfo(), seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex)}?"; - private static string RenderTypeName(TypeInfo typeInfo, ICollection? seeAlso, IReadOnlyList tupleNames, ref int tupleNameIndex, byte[] nullableFlags, ref int nullableFlagIndex) + var nullableSuffix = ""; + if (!typeInfo.IsValueType || typeInfo.IsGenericType) { - if (typeInfo.IsByRef) - return RenderTypeName(typeInfo.GetElementType().GetTypeInfo(), seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex); + if (nullableFlagIndex < nullableFlags.Length && nullableFlags[nullableFlagIndex] == 2) + nullableSuffix = "?"; + if (nullableFlagIndex < nullableFlags.Length - 1) + nullableFlagIndex++; + } - var nullableOfType = Nullable.GetUnderlyingType(typeInfo.AsType()); - if (nullableOfType != null) - return $"{RenderTypeName(nullableOfType.GetTypeInfo(), seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex)}?"; + if (typeInfo.IsArray) + return $"{RenderTypeName(typeInfo.GetElementType()!.GetTypeInfo(), seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex)}[]" + nullableSuffix; - var nullableSuffix = ""; - if (!typeInfo.IsValueType || typeInfo.IsGenericType) - { - if (nullableFlagIndex < nullableFlags.Length && nullableFlags[nullableFlagIndex] == 2) - nullableSuffix = "?"; - if (nullableFlagIndex < nullableFlags.Length - 1) - nullableFlagIndex++; - } + var builtIn = TryGetBuiltInTypeName(typeInfo.AsType()); + if (builtIn != null) + return builtIn + nullableSuffix; - if (typeInfo.IsArray) - return $"{RenderTypeName(typeInfo.GetElementType().GetTypeInfo(), seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex)}[]" + nullableSuffix; + var renderedTupleTypes = RenderTupleTypes(typeInfo, seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex); + if (renderedTupleTypes.Count > 1) + return $"({string.Join(", ", renderedTupleTypes)})"; - var builtIn = TryGetBuiltInTypeName(typeInfo.AsType()); - if (builtIn != null) - return builtIn + nullableSuffix; + seeAlso?.Add(typeInfo); - var renderedTupleTypes = RenderTupleTypes(typeInfo, seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex); - if (renderedTupleTypes.Count > 1) - return $"({string.Join(", ", renderedTupleTypes)})"; - - seeAlso?.Add(typeInfo); + return GetShortName(typeInfo) + RenderGenericArguments(typeInfo.GenericTypeArguments, seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex) + nullableSuffix; + } - return GetShortName(typeInfo) + RenderGenericArguments(typeInfo.GenericTypeArguments, seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex) + nullableSuffix; - } + private static List RenderTupleTypes(TypeInfo typeInfo, ICollection? seeAlso, IReadOnlyList tupleNames, ref int tupleNameIndex, byte[] nullableFlags, ref int nullableFlagIndex) + { + var renderedTupleTypes = new List(); - private static List RenderTupleTypes(TypeInfo typeInfo, ICollection? seeAlso, IReadOnlyList tupleNames, ref int tupleNameIndex, byte[] nullableFlags, ref int nullableFlagIndex) + if (typeInfo.Namespace == "System" && typeInfo.Name.StartsWith("ValueTuple`", StringComparison.Ordinal)) { - var renderedTupleTypes = new List(); + var ourTupleNameIndex = tupleNameIndex; + tupleNameIndex += CountTupleItems(typeInfo); - if (typeInfo.Namespace == "System" && typeInfo.Name.StartsWith("ValueTuple`", StringComparison.Ordinal)) + var genericTypeArguments = typeInfo.GenericTypeArguments; + while (true) { - var ourTupleNameIndex = tupleNameIndex; - tupleNameIndex += CountTupleItems(typeInfo); - - var genericTypeArguments = typeInfo.GenericTypeArguments; - while (true) + foreach (var genericTypeArgument in genericTypeArguments.Take(7)) { - foreach (var genericTypeArgument in genericTypeArguments.Take(7)) - { - var itemType = genericTypeArgument.GetTypeInfo(); - var renderedTupleType = RenderTypeName(itemType, seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex); - var itemName = tupleNames.ElementAtOrDefault(ourTupleNameIndex++); - if (itemName is not null) - renderedTupleType += $" {itemName}"; - renderedTupleTypes.Add(renderedTupleType); - } + var itemType = genericTypeArgument.GetTypeInfo(); + var renderedTupleType = RenderTypeName(itemType, seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex); + var itemName = tupleNames.ElementAtOrDefault(ourTupleNameIndex++); + if (itemName is not null) + renderedTupleType += $" {itemName}"; + renderedTupleTypes.Add(renderedTupleType); + } - if (genericTypeArguments.Length < 8) - break; + if (genericTypeArguments.Length < 8) + break; - genericTypeArguments = genericTypeArguments[7].GenericTypeArguments; - tupleNameIndex++; - } + genericTypeArguments = genericTypeArguments[7].GenericTypeArguments; + tupleNameIndex++; } - - return renderedTupleTypes; } - private static int CountTupleItems(TypeInfo typeInfo) => - typeInfo.GenericTypeArguments.Length < 8 ? typeInfo.GenericTypeArguments.Length : 7 + CountTupleItems(typeInfo.GenericTypeArguments[7].GetTypeInfo()); + return renderedTupleTypes; + } - private static IReadOnlyList GetTupleNames(IEnumerable attributes) => - attributes.OfType().FirstOrDefault()?.TransformNames as IReadOnlyList ?? []; + private static int CountTupleItems(TypeInfo typeInfo) => + typeInfo.GenericTypeArguments.Length < 8 ? typeInfo.GenericTypeArguments.Length : 7 + CountTupleItems(typeInfo.GenericTypeArguments[7].GetTypeInfo()); - private static string RenderGenericArguments(Type[]? genericArguments, ICollection? seeAlso = null) - { - var tupleNameIndex = 0; - var nullableFlagIndex = 0; - return RenderGenericArguments(genericArguments, seeAlso, [], ref tupleNameIndex, [], ref nullableFlagIndex); - } + private static IReadOnlyList GetTupleNames(IEnumerable attributes) => + attributes.OfType().FirstOrDefault()?.TransformNames as IReadOnlyList ?? []; - private static string RenderGenericArguments(Type[]? genericArguments, ICollection? seeAlso, IReadOnlyList tupleNames, ref int tupleNameIndex, byte[] nullableFlags, ref int nullableFlagIndex) - { - if (genericArguments == null) - return ""; + private static string RenderGenericArguments(Type[]? genericArguments, ICollection? seeAlso = null) + { + var tupleNameIndex = 0; + var nullableFlagIndex = 0; + return RenderGenericArguments(genericArguments, seeAlso, [], ref tupleNameIndex, [], ref nullableFlagIndex); + } - var stringBuilder = new StringBuilder(); - for (var index = 0; index < genericArguments.Length; index++) - { - var genericArgument = genericArguments[index]; - stringBuilder.Append((index == 0 ? "<" : "") + - RenderTypeName(genericArgument.GetTypeInfo(), seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex) + - (index < genericArguments.Length - 1 ? ", " : "") + - (index == genericArguments.Length - 1 ? ">" : "")); - } - return stringBuilder.ToString(); - } + private static string RenderGenericArguments(Type[]? genericArguments, ICollection? seeAlso, IReadOnlyList tupleNames, ref int tupleNameIndex, byte[] nullableFlags, ref int nullableFlagIndex) + { + if (genericArguments == null) + return ""; - private static string? TryGetBuiltInTypeName(Type type) + var stringBuilder = new StringBuilder(); + for (var index = 0; index < genericArguments.Length; index++) { - if (type == typeof(void)) - return "void"; - if (type == typeof(bool)) - return "bool"; - if (type == typeof(byte)) - return "byte"; - if (type == typeof(sbyte)) - return "sbyte"; - if (type == typeof(char)) - return "char"; - if (type == typeof(decimal)) - return "decimal"; - if (type == typeof(double)) - return "double"; - if (type == typeof(float)) - return "float"; - if (type == typeof(int)) - return "int"; - if (type == typeof(uint)) - return "uint"; - if (type == typeof(long)) - return "long"; - if (type == typeof(ulong)) - return "ulong"; - if (type == typeof(object)) - return "object"; - if (type == typeof(short)) - return "short"; - if (type == typeof(ushort)) - return "ushort"; - if (type == typeof(string)) - return "string"; - return null; + var genericArgument = genericArguments[index]; + stringBuilder.Append((index == 0 ? "<" : "") + + RenderTypeName(genericArgument.GetTypeInfo(), seeAlso, tupleNames, ref tupleNameIndex, nullableFlags, ref nullableFlagIndex) + + (index < genericArguments.Length - 1 ? ", " : "") + + (index == genericArguments.Length - 1 ? ">" : "")); } + return stringBuilder.ToString(); + } - private static bool IsStatic(MemberInfo memberInfo) - { - var typeInfo = memberInfo as TypeInfo; - if (typeInfo != null) - return typeInfo.IsClass && typeInfo.IsAbstract && typeInfo.IsSealed; + private static string? TryGetBuiltInTypeName(Type? type) + { + if (type == typeof(void)) + return "void"; + if (type == typeof(bool)) + return "bool"; + if (type == typeof(byte)) + return "byte"; + if (type == typeof(sbyte)) + return "sbyte"; + if (type == typeof(char)) + return "char"; + if (type == typeof(decimal)) + return "decimal"; + if (type == typeof(double)) + return "double"; + if (type == typeof(float)) + return "float"; + if (type == typeof(int)) + return "int"; + if (type == typeof(uint)) + return "uint"; + if (type == typeof(long)) + return "long"; + if (type == typeof(ulong)) + return "ulong"; + if (type == typeof(object)) + return "object"; + if (type == typeof(short)) + return "short"; + if (type == typeof(ushort)) + return "ushort"; + if (type == typeof(string)) + return "string"; + return null; + } - var eventInfo = memberInfo as EventInfo; - if (eventInfo != null) - return eventInfo.AddMethod.IsStatic; + private static bool IsStatic(MemberInfo memberInfo) + { + var typeInfo = memberInfo as TypeInfo; + if (typeInfo != null) + return typeInfo.IsClass && typeInfo.IsAbstract && typeInfo.IsSealed; - var propertyInfo = memberInfo as PropertyInfo; - if (propertyInfo != null) - return (propertyInfo.GetMethod ?? propertyInfo.SetMethod)?.IsStatic ?? false; + var eventInfo = memberInfo as EventInfo; + if (eventInfo != null) + return eventInfo.AddMethod?.IsStatic is true; - var fieldInfo = memberInfo as FieldInfo; - if (fieldInfo != null) - return fieldInfo.IsStatic && !fieldInfo.IsLiteral; + var propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) + return (propertyInfo.GetMethod ?? propertyInfo.SetMethod)?.IsStatic ?? false; - var methodBase = memberInfo as MethodBase; - if (methodBase != null) - return methodBase.IsStatic; + var fieldInfo = memberInfo as FieldInfo; + if (fieldInfo != null) + return fieldInfo.IsStatic && !fieldInfo.IsLiteral; + var methodBase = memberInfo as MethodBase; + if (methodBase != null) + return methodBase.IsStatic; + + return false; + } + + private static bool IsAbstract(MemberInfo memberInfo) + { + var typeInfo = memberInfo as TypeInfo; + if (typeInfo != null && !typeInfo.IsInterface) + return typeInfo.IsAbstract; + + if (memberInfo.DeclaringType?.GetTypeInfo().IsInterface == true) return false; - } - private static bool IsAbstract(MemberInfo memberInfo) + var eventInfo = memberInfo as EventInfo; + if (eventInfo != null) + return eventInfo.AddMethod != null && IsAbstract(eventInfo.AddMethod); + + var propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) { - var typeInfo = memberInfo as TypeInfo; - if (typeInfo != null && !typeInfo.IsInterface) - return typeInfo.IsAbstract; + return (propertyInfo.GetMethod != null && IsAbstract(propertyInfo.GetMethod)) || + (propertyInfo.SetMethod != null && IsAbstract(propertyInfo.SetMethod)); + } - if (memberInfo.DeclaringType?.GetTypeInfo().IsInterface == true) - return false; + var methodBase = memberInfo as MethodBase; + if (methodBase != null) + return methodBase.IsAbstract; - var eventInfo = memberInfo as EventInfo; - if (eventInfo != null) - return eventInfo.AddMethod != null && IsAbstract(eventInfo.AddMethod); + return false; + } - var propertyInfo = memberInfo as PropertyInfo; - if (propertyInfo != null) - { - return (propertyInfo.GetMethod != null && IsAbstract(propertyInfo.GetMethod)) || - (propertyInfo.SetMethod != null && IsAbstract(propertyInfo.SetMethod)); - } + private static bool IsVirtual(MemberInfo memberInfo) + { + if (memberInfo.DeclaringType?.GetTypeInfo().IsInterface == true) + return false; - var methodBase = memberInfo as MethodBase; - if (methodBase != null) - return methodBase.IsAbstract; + var eventInfo = memberInfo as EventInfo; + if (eventInfo != null) + return eventInfo.AddMethod != null && IsVirtual(eventInfo.AddMethod); - return false; + var propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) + { + return (propertyInfo.GetMethod != null && IsVirtual(propertyInfo.GetMethod)) || + (propertyInfo.SetMethod != null && IsVirtual(propertyInfo.SetMethod)); } - private static bool IsVirtual(MemberInfo memberInfo) - { - if (memberInfo.DeclaringType?.GetTypeInfo().IsInterface == true) - return false; + var methodInfo = memberInfo as MethodInfo; + if (methodInfo != null) + return methodInfo.IsVirtual && !methodInfo.IsFinal && methodInfo.GetRuntimeBaseDefinition()!.DeclaringType == methodInfo.DeclaringType; - var eventInfo = memberInfo as EventInfo; - if (eventInfo != null) - return eventInfo.AddMethod != null && IsVirtual(eventInfo.AddMethod); + return false; + } - var propertyInfo = memberInfo as PropertyInfo; - if (propertyInfo != null) - { - return (propertyInfo.GetMethod != null && IsVirtual(propertyInfo.GetMethod)) || - (propertyInfo.SetMethod != null && IsVirtual(propertyInfo.SetMethod)); - } + private static bool IsOverride(MemberInfo memberInfo) + { + if (memberInfo.DeclaringType?.GetTypeInfo().IsInterface == true) + return false; - var methodInfo = memberInfo as MethodInfo; - if (methodInfo != null) - return methodInfo.IsVirtual && !methodInfo.IsFinal && methodInfo.GetRuntimeBaseDefinition().DeclaringType == methodInfo.DeclaringType; + var eventInfo = memberInfo as EventInfo; + if (eventInfo != null) + return eventInfo.AddMethod != null && IsOverride(eventInfo.AddMethod); - return false; + var propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) + { + return (propertyInfo.GetMethod != null && IsOverride(propertyInfo.GetMethod)) || + (propertyInfo.SetMethod != null && IsOverride(propertyInfo.SetMethod)); } - private static bool IsOverride(MemberInfo memberInfo) - { - if (memberInfo.DeclaringType?.GetTypeInfo().IsInterface == true) - return false; + var methodInfo = memberInfo as MethodInfo; + if (methodInfo != null) + return methodInfo.IsVirtual && !methodInfo.IsFinal && methodInfo.GetRuntimeBaseDefinition()!.DeclaringType != methodInfo.DeclaringType; - var eventInfo = memberInfo as EventInfo; - if (eventInfo != null) - return eventInfo.AddMethod != null && IsOverride(eventInfo.AddMethod); + return false; + } - var propertyInfo = memberInfo as PropertyInfo; - if (propertyInfo != null) - { - return (propertyInfo.GetMethod != null && IsOverride(propertyInfo.GetMethod)) || - (propertyInfo.SetMethod != null && IsOverride(propertyInfo.SetMethod)); - } + private static bool IsConst(MemberInfo memberInfo) => (memberInfo as FieldInfo)?.IsLiteral ?? false; - var methodInfo = memberInfo as MethodInfo; - if (methodInfo != null) - return methodInfo.IsVirtual && !methodInfo.IsFinal && methodInfo.GetRuntimeBaseDefinition().DeclaringType != methodInfo.DeclaringType; + private static bool IsReadOnly(MemberInfo memberInfo) => (memberInfo as FieldInfo)?.IsInitOnly ?? false; - return false; - } + private static bool IsFlagsEnum(MemberInfo memberInfo) + { + var type = memberInfo as TypeInfo; + return type != null && type.IsEnum && type.GetCustomAttributes().Any(); + } - private static bool IsConst(MemberInfo memberInfo) => (memberInfo as FieldInfo)?.IsLiteral ?? false; + private static bool IsRecord(Type type) => type.GetMethod("$") != null; - private static bool IsReadOnly(MemberInfo memberInfo) => (memberInfo as FieldInfo)?.IsInitOnly ?? false; + private static bool IsBuiltInRecordMember(MemberInfo memberInfo) + { + if (memberInfo.DeclaringType is not Type declaringType || !IsRecord(declaringType)) + return false; - private static bool IsFlagsEnum(MemberInfo memberInfo) + return memberInfo switch { - var type = memberInfo as TypeInfo; - return type != null && type.IsEnum && type.GetCustomAttributes().Any(); - } + MethodInfo method => method.Name is "$" or "Deconstruct" or "Equals" or "GetHashCode" or "op_Equality" or "op_Inequality" or "PrintMembers" or "ToString", + PropertyInfo property => property.Name is "EqualityContract", + ConstructorInfo constructor => !constructor.IsPublic && constructor.GetParameters().Select(x => x.ParameterType).SequenceEqual([declaringType]), + _ => false, + }; + } - private static bool IsRecord(Type type) => type.GetMethod("$") != null; + private static XmlDocVisibilityLevel GetVisibility(MemberInfo memberInfo) => GetVisibility(memberInfo, XmlDocVisibilityLevel.Protected); - private static bool IsBuiltInRecordMember(MemberInfo memberInfo) + private static XmlDocVisibilityLevel GetVisibility(MemberInfo memberInfo, XmlDocVisibilityLevel protectedInternal) + { + var typeInfo = memberInfo as TypeInfo; + if (typeInfo != null) { - if (memberInfo.DeclaringType is not Type declaringType || !IsRecord(declaringType)) - return false; - - return memberInfo switch - { - MethodInfo method => method.Name is "$" or "Deconstruct" or "Equals" or "GetHashCode" or "op_Equality" or "op_Inequality" or "PrintMembers" or "ToString", - PropertyInfo property => property.Name is "EqualityContract", - ConstructorInfo constructor => !constructor.IsPublic && constructor.GetParameters().Select(x => x.ParameterType).SequenceEqual([declaringType]), - _ => false, - }; + var visibility = GetTypeVisibility(typeInfo); + return typeInfo.IsNested ? GetMostPrivate(visibility, GetTypeVisibility(typeInfo.DeclaringType!.GetTypeInfo(), protectedInternal)) : visibility; } - private static XmlDocVisibilityLevel GetVisibility(MemberInfo memberInfo) => GetVisibility(memberInfo, XmlDocVisibilityLevel.Protected); + var eventInfo = memberInfo as EventInfo; + if (eventInfo != null) + return GetMethodVisibility(eventInfo.AddMethod!, protectedInternal); - private static XmlDocVisibilityLevel GetVisibility(MemberInfo memberInfo, XmlDocVisibilityLevel protectedInternal) - { - var typeInfo = memberInfo as TypeInfo; - if (typeInfo != null) - { - var visibility = GetTypeVisibility(typeInfo); - return typeInfo.IsNested ? GetMostPrivate(visibility, GetTypeVisibility(typeInfo.DeclaringType.GetTypeInfo(), protectedInternal)) : visibility; - } - - var eventInfo = memberInfo as EventInfo; - if (eventInfo != null) - return GetMethodVisibility(eventInfo.AddMethod, protectedInternal); + var propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) + return GetPropertyVisibility(propertyInfo, protectedInternal); - var propertyInfo = memberInfo as PropertyInfo; - if (propertyInfo != null) - return GetPropertyVisibility(propertyInfo, protectedInternal); + var fieldInfo = memberInfo as FieldInfo; + if (fieldInfo != null) + return GetFieldVisibility(fieldInfo, protectedInternal); - var fieldInfo = memberInfo as FieldInfo; - if (fieldInfo != null) - return GetFieldVisibility(fieldInfo, protectedInternal); + var methodBase = memberInfo as MethodBase; + if (methodBase != null) + return GetMethodVisibility(methodBase, protectedInternal); - var methodBase = memberInfo as MethodBase; - if (methodBase != null) - return GetMethodVisibility(methodBase, protectedInternal); + return XmlDocVisibilityLevel.Private; + } - return XmlDocVisibilityLevel.Private; - } + private static XmlDocVisibilityLevel GetTypeVisibility(TypeInfo typeInfo, XmlDocVisibilityLevel protectedInternal = XmlDocVisibilityLevel.Protected) + { + if (typeInfo.IsPublic || typeInfo.IsNestedPublic) + return XmlDocVisibilityLevel.Public; + if (typeInfo.IsNestedFamORAssem) + return protectedInternal; + if (typeInfo.IsNestedFamily) + return XmlDocVisibilityLevel.Protected; + if (typeInfo.IsNestedAssembly || typeInfo.IsNestedFamANDAssem) + return XmlDocVisibilityLevel.Internal; + return XmlDocVisibilityLevel.Private; + } - private static XmlDocVisibilityLevel GetTypeVisibility(TypeInfo typeInfo, XmlDocVisibilityLevel protectedInternal = XmlDocVisibilityLevel.Protected) - { - if (typeInfo.IsPublic || typeInfo.IsNestedPublic) - return XmlDocVisibilityLevel.Public; - if (typeInfo.IsNestedFamORAssem) - return protectedInternal; - if (typeInfo.IsNestedFamily) - return XmlDocVisibilityLevel.Protected; - if (typeInfo.IsNestedAssembly || typeInfo.IsNestedFamANDAssem) - return XmlDocVisibilityLevel.Internal; - return XmlDocVisibilityLevel.Private; - } + private static XmlDocVisibilityLevel GetMethodVisibility(MethodBase methodBase, XmlDocVisibilityLevel protectedInternal = XmlDocVisibilityLevel.Protected) + { + if (methodBase.IsPublic) + return XmlDocVisibilityLevel.Public; + if (methodBase.IsFamilyOrAssembly) + return protectedInternal; + if (methodBase.IsFamily) + return XmlDocVisibilityLevel.Protected; + if (methodBase.IsAssembly || methodBase.IsFamilyAndAssembly) + return XmlDocVisibilityLevel.Internal; + return XmlDocVisibilityLevel.Private; + } - private static XmlDocVisibilityLevel GetMethodVisibility(MethodBase methodBase, XmlDocVisibilityLevel protectedInternal = XmlDocVisibilityLevel.Protected) - { - if (methodBase.IsPublic) - return XmlDocVisibilityLevel.Public; - if (methodBase.IsFamilyOrAssembly) - return protectedInternal; - if (methodBase.IsFamily) - return XmlDocVisibilityLevel.Protected; - if (methodBase.IsAssembly || methodBase.IsFamilyAndAssembly) - return XmlDocVisibilityLevel.Internal; - return XmlDocVisibilityLevel.Private; - } + private static XmlDocVisibilityLevel GetPropertyVisibility(PropertyInfo propertyInfo, XmlDocVisibilityLevel protectedInternal = XmlDocVisibilityLevel.Protected) + { + var getMethod = propertyInfo.GetMethod; + var setMethod = propertyInfo.SetMethod; + if (getMethod == null && setMethod == null) + throw new InvalidOperationException(); + + if (getMethod != null && setMethod == null) + return GetMethodVisibility(getMethod); + if (getMethod == null) + return GetMethodVisibility(setMethod!); + + return GetMostPublic( + GetMethodVisibility(propertyInfo.GetMethod!, protectedInternal), + GetMethodVisibility(propertyInfo.SetMethod!, protectedInternal)); + } - private static XmlDocVisibilityLevel GetPropertyVisibility(PropertyInfo propertyInfo, XmlDocVisibilityLevel protectedInternal = XmlDocVisibilityLevel.Protected) - { - var getMethod = propertyInfo.GetMethod; - var setMethod = propertyInfo.SetMethod; - if (getMethod == null && setMethod == null) - throw new InvalidOperationException(); - - if (getMethod != null && setMethod == null) - return GetMethodVisibility(getMethod); - if (getMethod == null) - return GetMethodVisibility(setMethod); - - return GetMostPublic( - GetMethodVisibility(propertyInfo.GetMethod, protectedInternal), - GetMethodVisibility(propertyInfo.SetMethod, protectedInternal)); - } + private static XmlDocVisibilityLevel GetFieldVisibility(FieldInfo fieldInfo, XmlDocVisibilityLevel protectedInternal = XmlDocVisibilityLevel.Protected) + { + if (fieldInfo.IsPublic) + return XmlDocVisibilityLevel.Public; + if (fieldInfo.IsFamilyOrAssembly) + return protectedInternal; + if (fieldInfo.IsFamily) + return XmlDocVisibilityLevel.Protected; + if (fieldInfo.IsAssembly || fieldInfo.IsFamilyAndAssembly) + return XmlDocVisibilityLevel.Internal; + return XmlDocVisibilityLevel.Private; + } - private static XmlDocVisibilityLevel GetFieldVisibility(FieldInfo fieldInfo, XmlDocVisibilityLevel protectedInternal = XmlDocVisibilityLevel.Protected) - { - if (fieldInfo.IsPublic) - return XmlDocVisibilityLevel.Public; - if (fieldInfo.IsFamilyOrAssembly) - return protectedInternal; - if (fieldInfo.IsFamily) - return XmlDocVisibilityLevel.Protected; - if (fieldInfo.IsAssembly || fieldInfo.IsFamilyAndAssembly) - return XmlDocVisibilityLevel.Internal; - return XmlDocVisibilityLevel.Private; - } + private static string GetAccessModifier(MemberInfo memberInfo) + { + var visibility = GetVisibility(memberInfo, XmlDocVisibilityLevel.ProtectedInternal); + return visibility switch + { + XmlDocVisibilityLevel.Public => "public", + XmlDocVisibilityLevel.ProtectedInternal => "protected internal", + XmlDocVisibilityLevel.Protected => "protected", + XmlDocVisibilityLevel.Internal => "internal", + XmlDocVisibilityLevel.Private => "private", + _ => throw new InvalidOperationException(), + }; + } - private static string GetAccessModifier(MemberInfo memberInfo) - { - var visibility = GetVisibility(memberInfo, XmlDocVisibilityLevel.ProtectedInternal); - return visibility switch - { - XmlDocVisibilityLevel.Public => "public", - XmlDocVisibilityLevel.ProtectedInternal => "protected internal", - XmlDocVisibilityLevel.Protected => "protected", - XmlDocVisibilityLevel.Internal => "internal", - XmlDocVisibilityLevel.Private => "private", - _ => throw new InvalidOperationException(), - }; - } + private enum TypeKind + { + Unknown, + Class, + Interface, + Struct, + Enum, + Delegate, + Record, + } - private enum TypeKind - { - Unknown, - Class, - Interface, - Struct, - Enum, - Delegate, - Record, - } + private static TypeKind GetTypeKind(TypeInfo typeInfo) + { + if (typeof(Delegate).GetTypeInfo().IsAssignableFrom(typeInfo)) + return TypeKind.Delegate; + if (IsRecord(typeInfo)) + return TypeKind.Record; + if (typeInfo.IsClass) + return TypeKind.Class; + if (typeInfo.IsInterface) + return TypeKind.Interface; + if (typeInfo.IsEnum) + return TypeKind.Enum; + if (typeInfo.IsValueType) + return TypeKind.Struct; + return TypeKind.Unknown; + } - private static TypeKind GetTypeKind(TypeInfo typeInfo) - { - if (typeof(Delegate).GetTypeInfo().IsAssignableFrom(typeInfo)) - return TypeKind.Delegate; - if (IsRecord(typeInfo)) - return TypeKind.Record; - if (typeInfo.IsClass) - return TypeKind.Class; - if (typeInfo.IsInterface) - return TypeKind.Interface; - if (typeInfo.IsEnum) - return TypeKind.Enum; - if (typeInfo.IsValueType) - return TypeKind.Struct; - return TypeKind.Unknown; - } + private enum MemberOrder + { + Constructor, + LifetimeProperty, + LifetimeField, + LifetimeMethod, + InstanceProperty, + InstanceField, + InstanceEvent, + InstanceMethod, + StaticProperty, + StaticField, + StaticEvent, + StaticMethod, + Operator, + Type, + Unknown, + } - private enum MemberOrder + private static MemberOrder GetMemberOrder(MemberInfo memberInfo) => + memberInfo switch { - Constructor, - LifetimeProperty, - LifetimeField, - LifetimeMethod, - InstanceProperty, - InstanceField, - InstanceEvent, - InstanceMethod, - StaticProperty, - StaticField, - StaticEvent, - StaticMethod, - Operator, - Type, - Unknown, - } - - private static MemberOrder GetMemberOrder(MemberInfo memberInfo) => - memberInfo switch - { - TypeInfo => MemberOrder.Type, - ConstructorInfo => MemberOrder.Constructor, - PropertyInfo propertyInfo => GetPropertyOrder(propertyInfo), - EventInfo eventInfo => GetEventOrder(eventInfo), - MethodInfo methodInfo => GetMethodOrder(methodInfo), - FieldInfo fieldInfo => GetFieldOrder(fieldInfo), - _ => MemberOrder.Unknown, - }; + TypeInfo => MemberOrder.Type, + ConstructorInfo => MemberOrder.Constructor, + PropertyInfo propertyInfo => GetPropertyOrder(propertyInfo), + EventInfo eventInfo => GetEventOrder(eventInfo), + MethodInfo methodInfo => GetMethodOrder(methodInfo), + FieldInfo fieldInfo => GetFieldOrder(fieldInfo), + _ => MemberOrder.Unknown, + }; - private static MemberOrder GetPropertyOrder(PropertyInfo propertyInfo) - { - var method = propertyInfo.GetMethod ?? propertyInfo.SetMethod; - if (!method.IsStatic) - return MemberOrder.InstanceProperty; - if (propertyInfo.PropertyType == propertyInfo.DeclaringType) - return MemberOrder.LifetimeProperty; - return MemberOrder.StaticProperty; - } + private static MemberOrder GetPropertyOrder(PropertyInfo propertyInfo) + { + var method = propertyInfo.GetMethod ?? propertyInfo.SetMethod!; + if (!method.IsStatic) + return MemberOrder.InstanceProperty; + if (propertyInfo.PropertyType == propertyInfo.DeclaringType) + return MemberOrder.LifetimeProperty; + return MemberOrder.StaticProperty; + } - private static MemberOrder GetEventOrder(EventInfo eventInfo) - { - var method = eventInfo.AddMethod ?? eventInfo.RemoveMethod; - if (!method.IsStatic) - return MemberOrder.InstanceEvent; - return MemberOrder.StaticEvent; - } + private static MemberOrder GetEventOrder(EventInfo eventInfo) + { + var method = eventInfo.AddMethod ?? eventInfo.RemoveMethod!; + if (!method.IsStatic) + return MemberOrder.InstanceEvent; + return MemberOrder.StaticEvent; + } - private static MemberOrder GetMethodOrder(MethodInfo methodInfo) - { - if (methodInfo.Name.StartsWith("op_", StringComparison.Ordinal)) - return MemberOrder.Operator; - if (!methodInfo.IsStatic) - return MemberOrder.InstanceMethod; - if (methodInfo.ReturnType == methodInfo.DeclaringType) - return MemberOrder.LifetimeMethod; - return MemberOrder.StaticMethod; - } + private static MemberOrder GetMethodOrder(MethodInfo methodInfo) + { + if (methodInfo.Name.StartsWith("op_", StringComparison.Ordinal)) + return MemberOrder.Operator; + if (!methodInfo.IsStatic) + return MemberOrder.InstanceMethod; + if (methodInfo.ReturnType == methodInfo.DeclaringType) + return MemberOrder.LifetimeMethod; + return MemberOrder.StaticMethod; + } - private static MemberOrder GetFieldOrder(FieldInfo fieldInfo) - { - if (!fieldInfo.IsStatic) - return MemberOrder.InstanceField; - if (fieldInfo.FieldType == fieldInfo.DeclaringType) - return MemberOrder.LifetimeField; - return MemberOrder.StaticField; - } + private static MemberOrder GetFieldOrder(FieldInfo fieldInfo) + { + if (!fieldInfo.IsStatic) + return MemberOrder.InstanceField; + if (fieldInfo.FieldType == fieldInfo.DeclaringType) + return MemberOrder.LifetimeField; + return MemberOrder.StaticField; + } - private static IEnumerable OrderMembers(IEnumerable items, Func getMemberInfo) - { - return items.OrderBy(x => (int) GetMemberOrder(getMemberInfo(x))) - .ThenBy(x => GetShortName(getMemberInfo(x)).ToString(), StringComparer.OrdinalIgnoreCase) - .ThenBy(x => GetGenericArguments(getMemberInfo(x)).Length) - .ThenBy(x => GetParameters(getMemberInfo(x)).Length) - .ThenBy(x => GetParameterShortNames(getMemberInfo(x)), StringComparer.OrdinalIgnoreCase); - } + private static IEnumerable OrderMembers(IEnumerable items, Func getMemberInfo) + { + return items.OrderBy(x => (int) GetMemberOrder(getMemberInfo(x))) + .ThenBy(x => GetShortName(getMemberInfo(x)).ToString(), StringComparer.OrdinalIgnoreCase) + .ThenBy(x => GetGenericArguments(getMemberInfo(x)).Length) + .ThenBy(x => GetParameters(getMemberInfo(x)).Length) + .ThenBy(x => GetParameterShortNames(getMemberInfo(x)), StringComparer.OrdinalIgnoreCase); + } - private static MethodInfo? TryGetDelegateInvoke(MemberInfo memberInfo) - { - var typeInfo = memberInfo as TypeInfo; - return typeInfo != null && typeof(Delegate).GetTypeInfo().IsAssignableFrom(typeInfo) ? typeInfo.DeclaredMethods.FirstOrDefault(x => x.Name == "Invoke") : null; - } + private static MethodInfo? TryGetDelegateInvoke(MemberInfo memberInfo) + { + var typeInfo = memberInfo as TypeInfo; + return typeInfo != null && typeof(Delegate).GetTypeInfo().IsAssignableFrom(typeInfo) ? typeInfo.DeclaredMethods.FirstOrDefault(x => x.Name == "Invoke") : null; + } - private static Type[] GetGenericArguments(MemberInfo memberInfo) - { - var type = memberInfo as TypeInfo; - if (type != null) - return type.GenericTypeParameters; + private static Type[] GetGenericArguments(MemberInfo memberInfo) + { + var type = memberInfo as TypeInfo; + if (type != null) + return type.GenericTypeParameters; - var method = memberInfo as MethodInfo; - return method?.GetGenericArguments() ?? Array.Empty(); - } + var method = memberInfo as MethodInfo; + return method?.GetGenericArguments() ?? []; + } - private static ParameterInfo[] GetParameters(MemberInfo memberInfo) - { - var delegateInvoke = TryGetDelegateInvoke(memberInfo); - if (delegateInvoke != null) - return GetParameters(delegateInvoke); + private static ParameterInfo[] GetParameters(MemberInfo memberInfo) + { + var delegateInvoke = TryGetDelegateInvoke(memberInfo); + if (delegateInvoke != null) + return GetParameters(delegateInvoke); - var propertyInfo = memberInfo as PropertyInfo; - if (propertyInfo != null) - return propertyInfo.GetIndexParameters(); + var propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) + return propertyInfo.GetIndexParameters(); - var method = memberInfo as MethodBase; - return method?.GetParameters() ?? Array.Empty(); - } + var method = memberInfo as MethodBase; + return method?.GetParameters() ?? []; + } - private static string GetParameterShortNames(MemberInfo memberInfo) => - string.Join(", ", GetParameters(memberInfo).Select(x => RenderTypeName(x.ParameterType.GetTypeInfo()))); + private static string GetParameterShortNames(MemberInfo memberInfo) => + string.Join(", ", GetParameters(memberInfo).Select(x => RenderTypeName(x.ParameterType.GetTypeInfo()))); - private static bool IsLessDerived(TypeInfo a, TypeInfo b) + private static bool IsLessDerived(TypeInfo a, TypeInfo b) + { + if (!a.IsAssignableFrom(b)) + return false; + if (a.IsGenericType && b.IsGenericType && a.GetGenericTypeDefinition().GetTypeInfo() == b.GetGenericTypeDefinition().GetTypeInfo()) { - if (!a.IsAssignableFrom(b)) - return false; - if (a.IsGenericType && b.IsGenericType && a.GetGenericTypeDefinition().GetTypeInfo() == b.GetGenericTypeDefinition().GetTypeInfo()) + var parameters = a.GetGenericTypeDefinition().GetTypeInfo().GenericTypeParameters; + for (var i = 0; i < parameters.Length; i++) { - var parameters = a.GetGenericTypeDefinition().GetTypeInfo().GenericTypeParameters; - for (var i = 0; i < parameters.Length; i++) - { - var paramAttributes = parameters[i].GetTypeInfo().GenericParameterAttributes; - var aArgInfo = a.GenericTypeArguments[i].GetTypeInfo(); - var bArgInfo = b.GenericTypeArguments[i].GetTypeInfo(); - if (paramAttributes.HasFlag(GenericParameterAttributes.Contravariant) && !aArgInfo.IsAssignableFrom(bArgInfo)) - return false; - if (paramAttributes.HasFlag(GenericParameterAttributes.Covariant) && aArgInfo.IsAssignableFrom(bArgInfo)) - return false; - } + var paramAttributes = parameters[i].GetTypeInfo().GenericParameterAttributes; + var aArgInfo = a.GenericTypeArguments[i].GetTypeInfo(); + var bArgInfo = b.GenericTypeArguments[i].GetTypeInfo(); + if (paramAttributes.HasFlag(GenericParameterAttributes.Contravariant) && !aArgInfo.IsAssignableFrom(bArgInfo)) + return false; + if (paramAttributes.HasFlag(GenericParameterAttributes.Covariant) && aArgInfo.IsAssignableFrom(bArgInfo)) + return false; } - return true; } + return true; + } - private static bool IsKeyword(string value) => s_keywords.Contains(value); + private static bool IsKeyword(string value) => s_keywords.Contains(value); - private string ToMarkdown(XmlDocInline inline, MarkdownContext context) - { - var text = inline.Text ?? ""; + private string ToMarkdown(XmlDocInline inline, MarkdownContext context) + { + var text = inline.Text ?? ""; - MemberInfo? seeMemberInfo = null; - if (inline.SeeRef != null) - context.MembersByXmlDocName.TryGetValue(inline.SeeRef, out seeMemberInfo); + MemberInfo? seeMemberInfo = null; + if (inline.SeeRef != null) + context.MembersByXmlDocName.TryGetValue(inline.SeeRef, out seeMemberInfo); - if (text.Length == 0) - { - if (seeMemberInfo != null) - text = GetOperatorKeywordName(GetShortName(seeMemberInfo)); - else if (inline.SeeRef != null) - text = XmlDocUtility.GetShortNameForXmlDocRef(inline.SeeRef); - else if (inline.LinkUrl != null) - text = inline.LinkUrl; - else if (inline.LangWord != null) - text = SurroundCode(inline.LangWord); - } + if (text.Length == 0) + { + if (seeMemberInfo != null) + text = GetOperatorKeywordName(GetShortName(seeMemberInfo)); + else if (inline.SeeRef != null) + text = XmlDocUtility.GetShortNameForXmlDocRef(inline.SeeRef); + else if (inline.LinkUrl != null) + text = inline.LinkUrl; + else if (inline.LangWord != null) + text = SurroundCode(inline.LangWord); + } - if (text.Length != 0) - { - var isCode = inline.IsCode || seeMemberInfo != null; - if (isCode) - text = SurroundCode(text); + if (text.Length != 0) + { + var isCode = inline.IsCode || seeMemberInfo != null; + if (isCode) + text = SurroundCode(text); - if (inline.IsParamRef || inline.IsTypeParamRef) - text = $"*{text}*"; + if (inline.IsParamRef || inline.IsTypeParamRef) + text = $"*{text}*"; - text = WrapMarkdownRefLink(text, seeMemberInfo, context, isCode: inline.IsCode, linkUrl: inline.LinkUrl); - } + text = WrapMarkdownRefLink(text, seeMemberInfo, context, isCode: inline.IsCode, linkUrl: inline.LinkUrl); + } - text = Regex.Replace(text, @"\s+", " "); + text = Regex.Replace(text, @"\s+", " "); - return text; - } + return text; + } - private string WrapMarkdownRefLink(string text, MemberInfo? memberInfo, MarkdownContext context, bool isCode = false, string? linkUrl = null) + private string WrapMarkdownRefLink(string text, MemberInfo? memberInfo, MarkdownContext context, bool isCode = false, string? linkUrl = null) + { + var extension = GetFileExtension(); + var xmlDocRef = memberInfo == null ? null : XmlDocUtility.GetXmlDocRef(memberInfo); + var isLocal = xmlDocRef != null && context.MembersByXmlDocName.ContainsKey(xmlDocRef); + var externalDoc = isLocal || xmlDocRef == null ? null : FindExternalDocumentation(memberInfo); + if (memberInfo != null && xmlDocRef != XmlDocUtility.GetXmlDocRef(context.MemberInfo) && (isLocal || externalDoc != null)) { - var extension = GetFileExtension(); - var xmlDocRef = memberInfo == null ? null : XmlDocUtility.GetXmlDocRef(memberInfo); - var isLocal = xmlDocRef != null && context.MembersByXmlDocName.ContainsKey(xmlDocRef); - var externalDoc = isLocal || xmlDocRef == null ? null : FindExternalDocumentation(memberInfo); - if (memberInfo != null && xmlDocRef != XmlDocUtility.GetXmlDocRef(context.MemberInfo) && (isLocal || externalDoc != null)) - { - string path; + string path; - var typeInfo = memberInfo as TypeInfo; - if (context.MemberInfo != null) - { - if (typeInfo != null) - path = $"{GetNamespaceUriName(typeInfo.Namespace)}/{GetSafeTypeUriName(typeInfo)}{extension}"; - else - path = $"{GetNamespaceUriName(memberInfo.DeclaringType?.Namespace)}/{GetTypeUriName(memberInfo.DeclaringType.GetTypeInfo())}/{GetMemberUriName(memberInfo)}{extension}"; - } - else if (context.TypeInfo != null) - { - if (typeInfo != null) - path = $"{GetNamespaceUriName(typeInfo.Namespace)}/{GetSafeTypeUriName(typeInfo)}{extension}"; - else - path = $"{GetNamespaceUriName(memberInfo.DeclaringType?.Namespace)}/{GetTypeUriName(memberInfo.DeclaringType.GetTypeInfo())}/{GetMemberUriName(memberInfo)}{extension}"; - } + var typeInfo = memberInfo as TypeInfo; + if (context.MemberInfo != null) + { + if (typeInfo != null) + path = $"{GetNamespaceUriName(typeInfo.Namespace)}/{GetSafeTypeUriName(typeInfo)}{extension}"; else - { - if (typeInfo != null) - path = $"{GetNamespaceUriName(typeInfo.Namespace)}/{GetSafeTypeUriName(typeInfo)}{extension}"; - else - path = $"{GetNamespaceUriName(memberInfo.DeclaringType?.Namespace)}/{GetTypeUriName(memberInfo.DeclaringType.GetTypeInfo())}/{GetMemberUriName(memberInfo)}{extension}"; - } - - if (!string.IsNullOrEmpty(context.PageLocation)) - path = MakeRelative(context.PageLocation, path); - - text = $"[{text}]({path})"; + path = $"{GetNamespaceUriName(memberInfo.DeclaringType?.Namespace)}/{GetTypeUriName(memberInfo.DeclaringType!.GetTypeInfo())}/{GetMemberUriName(memberInfo)}{extension}"; } - else if (linkUrl != null) + else if (context.TypeInfo != null) { - text = $"[{text}]({linkUrl})"; + if (typeInfo != null) + path = $"{GetNamespaceUriName(typeInfo.Namespace)}/{GetSafeTypeUriName(typeInfo)}{extension}"; + else + path = $"{GetNamespaceUriName(memberInfo.DeclaringType?.Namespace)}/{GetTypeUriName(memberInfo.DeclaringType!.GetTypeInfo())}/{GetMemberUriName(memberInfo)}{extension}"; + } + else + { + if (typeInfo != null) + path = $"{GetNamespaceUriName(typeInfo.Namespace)}/{GetSafeTypeUriName(typeInfo)}{extension}"; + else + path = $"{GetNamespaceUriName(memberInfo.DeclaringType?.Namespace)}/{GetTypeUriName(memberInfo.DeclaringType!.GetTypeInfo())}/{GetMemberUriName(memberInfo)}{extension}"; } - return isCode ? text : EscapeHtml(text); - } + if (!string.IsNullOrEmpty(context.PageLocation)) + path = MakeRelative(context.PageLocation, path); - private string MakeRelative(string baseUri, string path) + text = $"[{text}]({path})"; + } + else if (linkUrl != null) { - var a = new Uri("file:///" + baseUri); - var b = new Uri("file:///" + path); - var rel = a.MakeRelativeUri(b); - var result = rel.ToString(); - if (string.IsNullOrEmpty(result)) - { - // then the file name is the link - result = b.Segments.Last(); - } + text = $"[{text}]({linkUrl})"; + } - // generate explicit relative link, e.g. for GitLab wiki - return result[0] == '.' ? result : $"./{result}"; + return isCode ? text : EscapeHtml(text); + } + + private string MakeRelative(string baseUri, string path) + { + var a = new Uri("file:///" + baseUri); + var b = new Uri("file:///" + path); + var rel = a.MakeRelativeUri(b); + var result = rel.ToString(); + if (string.IsNullOrEmpty(result)) + { + // then the file name is the link + result = b.Segments.Last(); } - private string? ToMarkdown(IEnumerable? inlines, MarkdownContext context) => - inlines == null ? null : string.Concat(inlines.Select(x => ToMarkdown(x, context))).Trim(); + // generate explicit relative link, e.g. for GitLab wiki + return result[0] == '.' ? result : $"./{result}"; + } + + private string? ToMarkdown(IEnumerable? inlines, MarkdownContext context) => + inlines == null ? null : string.Concat(inlines.Select(x => ToMarkdown(x, context))).Trim(); - private IEnumerable ToMarkdown(IReadOnlyList blocks, MarkdownContext context) + private IEnumerable ToMarkdown(IReadOnlyList blocks, MarkdownContext context) + { + for (var index = 0; index < blocks.Count; index++) { - for (var index = 0; index < blocks.Count; index++) - { - if (index != 0) - yield return ""; + if (index != 0) + yield return ""; - var block = blocks[index]; + var block = blocks[index]; - if (block.ListKind == XmlDocListKind.Bullet || block.ListKind == XmlDocListKind.Number) + if (block.ListKind == XmlDocListKind.Bullet || block.ListKind == XmlDocListKind.Number) + { + var number = 0; + while (true) { - var number = 0; - while (true) + var prefix = new string(' ', block.ListDepth * 2) + (block.ListKind == XmlDocListKind.Number ? $"{++number}. " : "* "); + var markdown = ToMarkdown(block.Inlines, context); + + if (block.IsListTerm) { - var prefix = new string(' ', block.ListDepth * 2) + (block.ListKind == XmlDocListKind.Number ? $"{++number}. " : "* "); - var markdown = ToMarkdown(block.Inlines, context); + markdown = $"**{markdown}**"; - if (block.IsListTerm) + var afterTermBlock = index + 1 < blocks.Count ? blocks[index + 1] : null; + if (afterTermBlock != null && afterTermBlock.ListKind == block.ListKind && afterTermBlock.ListDepth == block.ListDepth && !afterTermBlock.IsListTerm) { - markdown = $"**{markdown}**"; - - var afterTermBlock = index + 1 < blocks.Count ? blocks[index + 1] : null; - if (afterTermBlock != null && afterTermBlock.ListKind == block.ListKind && afterTermBlock.ListDepth == block.ListDepth && !afterTermBlock.IsListTerm) - { - markdown += " – " + ToMarkdown(afterTermBlock.Inlines, context); - index++; - } + markdown += " – " + ToMarkdown(afterTermBlock.Inlines, context); + index++; } + } - yield return prefix + markdown; + yield return prefix + markdown; - var nextBlock = index + 1 < blocks.Count ? blocks[index + 1] : null; - if (nextBlock == null || nextBlock.ListKind != block.ListKind) - break; + var nextBlock = index + 1 < blocks.Count ? blocks[index + 1] : null; + if (nextBlock == null || nextBlock.ListKind != block.ListKind) + break; - block = nextBlock; - index++; - } + block = nextBlock; + index++; + } + } + else + { + if (block.IsCode) + { + yield return "```csharp"; + foreach (var inline in block.Inlines) + yield return inline.Text!.Replace(Environment.NewLine, ActualNewLine, StringComparison.Ordinal); + yield return "```"; } else { - if (block.IsCode) - { - yield return "```csharp"; - foreach (var inline in block.Inlines) - yield return inline.Text!.Replace(Environment.NewLine, ActualNewLine); - yield return "```"; - } - else - { - var markdown = ToMarkdown(block.Inlines, context)!; - if (block.IsListHeader || block.IsListTerm) - markdown = $"**{markdown}**"; - yield return markdown; - } + var markdown = ToMarkdown(block.Inlines, context)!; + if (block.IsListHeader || block.IsListTerm) + markdown = $"**{markdown}**"; + yield return markdown; } } } + } + + private sealed class MarkdownContext + { + public MarkdownContext(XmlDocAssembly xmlDocAssembly, IReadOnlyDictionary membersByXmlDocName, string assemblyFileName, string pageLocation) + { + XmlDocAssembly = xmlDocAssembly; + MembersByXmlDocName = membersByXmlDocName; + AssemblyFileName = assemblyFileName; + PageLocation = pageLocation; + } - private sealed class MarkdownContext + public MarkdownContext(MarkdownContext context, MemberInfo? memberInfo, string pageLocation) { - public MarkdownContext(XmlDocAssembly xmlDocAssembly, IReadOnlyDictionary membersByXmlDocName, string assemblyFileName, string? sourceCodePath, string rootNamespace, string pageLocation) + XmlDocAssembly = context.XmlDocAssembly; + MembersByXmlDocName = context.MembersByXmlDocName; + AssemblyFileName = context.AssemblyFileName; + PageLocation = pageLocation; + + var typeInfo = memberInfo as TypeInfo; + if (typeInfo != null) { - XmlDocAssembly = xmlDocAssembly; - MembersByXmlDocName = membersByXmlDocName; - AssemblyFileName = assemblyFileName; - SourceCodePath = sourceCodePath; - RootNamespace = rootNamespace; - PageLocation = pageLocation; + TypeInfo = typeInfo; } - - public MarkdownContext(MarkdownContext context, MemberInfo? memberInfo, string pageLocation) + else if (memberInfo != null) { - XmlDocAssembly = context.XmlDocAssembly; - MembersByXmlDocName = context.MembersByXmlDocName; - AssemblyFileName = context.AssemblyFileName; - SourceCodePath = context.SourceCodePath; - RootNamespace = context.RootNamespace; - PageLocation = pageLocation; - - var typeInfo = memberInfo as TypeInfo; - if (typeInfo != null) - { - TypeInfo = typeInfo; - } - else if (memberInfo != null) - { - TypeInfo = memberInfo.DeclaringType.GetTypeInfo(); - MemberInfo = memberInfo; - } + TypeInfo = memberInfo.DeclaringType!.GetTypeInfo(); + MemberInfo = memberInfo; } + } - public TypeInfo? TypeInfo { get; } - - public MemberInfo? MemberInfo { get; } - - public XmlDocAssembly XmlDocAssembly { get; } - - public IReadOnlyDictionary MembersByXmlDocName { get; } + public TypeInfo? TypeInfo { get; } - public string AssemblyFileName { get; } + public MemberInfo? MemberInfo { get; } - public string? SourceCodePath { get; } + public XmlDocAssembly XmlDocAssembly { get; } - public string RootNamespace { get; } + public IReadOnlyDictionary MembersByXmlDocName { get; } - public string PageLocation { get; } - } + public string AssemblyFileName { get; } - private static readonly HashSet s_keywords = new() - { - "abstract", - "as", - "base", - "bool", - "break", - "byte", - "case", - "catch", - "char", - "checked", - "class", - "const", - "continue", - "decimal", - "default", - "delegate", - "do", - "double", - "else", - "enum", - "event", - "explicit", - "extern", - "false", - "finally", - "fixed", - "float", - "for", - "foreach", - "goto", - "if", - "implicit", - "in", - "int", - "interface", - "internal", - "is", - "lock", - "long", - "namespace", - "new", - "null", - "object", - "operator", - "out", - "override", - "params", - "private", - "protected", - "public", - "readonly", - "ref", - "return", - "sbyte", - "sealed", - "short", - "sizeof", - "stackalloc", - "static", - "string", - "struct", - "switch", - "this", - "throw", - "true", - "try", - "typeof", - "uint", - "ulong", - "unchecked", - "unsafe", - "ushort", - "using", - "virtual", - "void", - "volatile", - "while", - }; + public string PageLocation { get; } } + + private static readonly HashSet s_keywords = new() + { + "abstract", + "as", + "base", + "bool", + "break", + "byte", + "case", + "catch", + "char", + "checked", + "class", + "const", + "continue", + "decimal", + "default", + "delegate", + "do", + "double", + "else", + "enum", + "event", + "explicit", + "extern", + "false", + "finally", + "fixed", + "float", + "for", + "foreach", + "goto", + "if", + "implicit", + "in", + "int", + "interface", + "internal", + "is", + "lock", + "long", + "namespace", + "new", + "null", + "object", + "operator", + "out", + "override", + "params", + "private", + "protected", + "public", + "readonly", + "ref", + "return", + "sbyte", + "sealed", + "short", + "sizeof", + "stackalloc", + "static", + "string", + "struct", + "switch", + "this", + "throw", + "true", + "try", + "typeof", + "uint", + "ulong", + "unchecked", + "unsafe", + "ushort", + "using", + "virtual", + "void", + "volatile", + "while", + }; } diff --git a/src/XmlDocMarkdown.Core/MarkdownWriter.cs b/src/XmlDocMarkdown.Core/MarkdownWriter.cs index cd121da..95dc138 100644 --- a/src/XmlDocMarkdown.Core/MarkdownWriter.cs +++ b/src/XmlDocMarkdown.Core/MarkdownWriter.cs @@ -1,28 +1,27 @@ -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +internal sealed class MarkdownWriter { - internal sealed class MarkdownWriter + public MarkdownWriter(TextWriter textWriter) { - public MarkdownWriter(TextWriter textWriter) - { - TextWriter = textWriter; - } + TextWriter = textWriter; + } - public TextWriter TextWriter { get; } + public TextWriter TextWriter { get; } - public void Write(string text) => TextWriter.Write(text); + public void Write(string text) => TextWriter.Write(text); - public void WriteLine() => TextWriter.WriteLine(); + public void WriteLine() => TextWriter.WriteLine(); - public void WriteLine(string text) - { - Write(text); - WriteLine(); - } + public void WriteLine(string text) + { + Write(text); + WriteLine(); + } - public void WriteLines(IEnumerable lines) - { - foreach (var line in lines) - WriteLine(line); - } + public void WriteLines(IEnumerable lines) + { + foreach (var line in lines) + WriteLine(line); } } diff --git a/src/XmlDocMarkdown.Core/NamedText.cs b/src/XmlDocMarkdown.Core/NamedText.cs index b45b939..fbde2c7 100644 --- a/src/XmlDocMarkdown.Core/NamedText.cs +++ b/src/XmlDocMarkdown.Core/NamedText.cs @@ -1,21 +1,14 @@ -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +internal sealed class NamedText { - internal sealed class NamedText + public NamedText(string name, string text) { - public NamedText(string name, string? parent, string title, string text) - { - Name = name ?? throw new ArgumentNullException(nameof(name)); - Parent = parent; - Title = title ?? throw new ArgumentNullException(nameof(title)); - Text = text ?? throw new ArgumentNullException(nameof(text)); - } - - public string Name { get; } - - public string? Parent { get; } + Name = name ?? throw new ArgumentNullException(nameof(name)); + Text = text ?? throw new ArgumentNullException(nameof(text)); + } - public string Title { get; } + public string Name { get; } - public string Text { get; } - } + public string Text { get; } } diff --git a/src/XmlDocMarkdown.Core/XmlDocAssembly.cs b/src/XmlDocMarkdown.Core/XmlDocAssembly.cs index d0ee032..0ded39f 100644 --- a/src/XmlDocMarkdown.Core/XmlDocAssembly.cs +++ b/src/XmlDocMarkdown.Core/XmlDocAssembly.cs @@ -1,26 +1,25 @@ using System.Collections.ObjectModel; using System.Xml.Linq; -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +internal sealed class XmlDocAssembly { - internal sealed class XmlDocAssembly + public XmlDocAssembly() { - public XmlDocAssembly() - { - } + } - public XmlDocAssembly(XDocument xDocument) + public XmlDocAssembly(XDocument xDocument) + { + var xElement = xDocument?.Root; + if (xElement != null) { - var xElement = xDocument?.Root; - if (xElement != null) - { - foreach (var xMember in xElement.Elements("members").Elements("member").Where(x => x.Attribute("name") != null)) - Members.Add(new XmlDocMember(xMember)); - } + foreach (var xMember in xElement.Elements("members").Elements("member").Where(x => x.Attribute("name") != null)) + Members.Add(new XmlDocMember(xMember)); } + } - public Collection Members { get; } = new(); + public Collection Members { get; } = []; - public XmlDocMember? FindMember(string? xmlDocName) => Members.FirstOrDefault(x => x.XmlDocName == xmlDocName); - } + public XmlDocMember? FindMember(string? xmlDocName) => Members.FirstOrDefault(x => x.XmlDocName == xmlDocName); } diff --git a/src/XmlDocMarkdown.Core/XmlDocBlock.cs b/src/XmlDocMarkdown.Core/XmlDocBlock.cs index b7b9111..d53262c 100644 --- a/src/XmlDocMarkdown.Core/XmlDocBlock.cs +++ b/src/XmlDocMarkdown.Core/XmlDocBlock.cs @@ -1,19 +1,18 @@ using System.Collections.ObjectModel; -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +internal sealed class XmlDocBlock { - internal sealed class XmlDocBlock - { - public Collection Inlines { get; } = new(); + public Collection Inlines { get; } = []; - public bool IsCode { get; set; } + public bool IsCode { get; set; } - public XmlDocListKind? ListKind { get; set; } + public XmlDocListKind? ListKind { get; set; } - public int ListDepth { get; set; } + public int ListDepth { get; set; } - public bool IsListHeader { get; set; } + public bool IsListHeader { get; set; } - public bool IsListTerm { get; set; } - } + public bool IsListTerm { get; set; } } diff --git a/src/XmlDocMarkdown.Core/XmlDocException.cs b/src/XmlDocMarkdown.Core/XmlDocException.cs index a5650f3..12aae2b 100644 --- a/src/XmlDocMarkdown.Core/XmlDocException.cs +++ b/src/XmlDocMarkdown.Core/XmlDocException.cs @@ -1,11 +1,10 @@ using System.Collections.ObjectModel; -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +internal sealed class XmlDocException { - internal sealed class XmlDocException - { - public string? ExceptionTypeRef { get; set; } + public string? ExceptionTypeRef { get; set; } - public Collection Condition { get; } = new(); - } + public Collection Condition { get; } = []; } diff --git a/src/XmlDocMarkdown.Core/XmlDocInline.cs b/src/XmlDocMarkdown.Core/XmlDocInline.cs index efaf06d..8762404 100644 --- a/src/XmlDocMarkdown.Core/XmlDocInline.cs +++ b/src/XmlDocMarkdown.Core/XmlDocInline.cs @@ -1,19 +1,18 @@ -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +internal sealed class XmlDocInline { - internal sealed class XmlDocInline - { - public string? Text { get; set; } + public string? Text { get; set; } - public string? SeeRef { get; set; } + public string? SeeRef { get; set; } - public string? LangWord { get; set; } + public string? LangWord { get; set; } - public string? LinkUrl { get; set; } + public string? LinkUrl { get; set; } - public bool IsCode { get; set; } + public bool IsCode { get; set; } - public bool IsParamRef { get; set; } + public bool IsParamRef { get; set; } - public bool IsTypeParamRef { get; set; } - } + public bool IsTypeParamRef { get; set; } } diff --git a/src/XmlDocMarkdown.Core/XmlDocInput.cs b/src/XmlDocMarkdown.Core/XmlDocInput.cs deleted file mode 100644 index d741407..0000000 --- a/src/XmlDocMarkdown.Core/XmlDocInput.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Reflection; - -namespace XmlDocMarkdown.Core -{ - /// - /// The input for generating Markdown from .NET XML documentation comments. - /// - public sealed class XmlDocInput - { - /// - /// The path of the assembly to load. - /// - /// Optional; uses Assembly if omitted. - public string? AssemblyPath { get; set; } - - /// - /// An already-loaded assembly. - /// - /// Optional; uses AssemblyPath if omitted. - public Assembly? Assembly { get; set; } - - /// - /// The path of the XML documentation for the assembly. - /// - /// Optional; changes the extension of AssemblyPath or Assembly.Location if omitted. - public string? XmlDocPath { get; set; } - } -} diff --git a/src/XmlDocMarkdown.Core/XmlDocListKind.cs b/src/XmlDocMarkdown.Core/XmlDocListKind.cs index 4f4b1b2..6ca653a 100644 --- a/src/XmlDocMarkdown.Core/XmlDocListKind.cs +++ b/src/XmlDocMarkdown.Core/XmlDocListKind.cs @@ -1,10 +1,9 @@ -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +internal enum XmlDocListKind { - internal enum XmlDocListKind - { - Bullet, - Number, - Table, - Other, - } + Bullet, + Number, + Table, + Other, } diff --git a/src/XmlDocMarkdown.Core/XmlDocMarkdown.Core.csproj b/src/XmlDocMarkdown.Core/XmlDocMarkdown.Core.csproj index adbc523..5c1fae5 100644 --- a/src/XmlDocMarkdown.Core/XmlDocMarkdown.Core.csproj +++ b/src/XmlDocMarkdown.Core/XmlDocMarkdown.Core.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net8.0 A class library that generates Markdown from .NET XML documentation comments. .NET XML documentation comments Markdown true diff --git a/src/XmlDocMarkdown.Core/XmlDocMarkdownApp.cs b/src/XmlDocMarkdown.Core/XmlDocMarkdownApp.cs index 66591bc..026ae51 100644 --- a/src/XmlDocMarkdown.Core/XmlDocMarkdownApp.cs +++ b/src/XmlDocMarkdown.Core/XmlDocMarkdownApp.cs @@ -1,139 +1,92 @@ using System.Reflection; -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +/// +/// Implements the command-line application. +/// +public sealed class XmlDocMarkdownApp { /// - /// Implements the command-line application. + /// Run the command-line application. /// - public sealed class XmlDocMarkdownApp + /// The command-line arguments. + /// Called to configure the settings. + /// The exit code. + public static int Run(IReadOnlyList args, Action? configure = null) { - /// - /// Run the command-line application. - /// - /// The command-line arguments. - /// The exit code. - public static int Run(IReadOnlyList args) + try { - try + var argsReader = new ArgsReader(args); + if (argsReader.ReadHelpFlag()) { - var argsReader = new ArgsReader(args); - if (argsReader.ReadHelpFlag()) - { - WriteUsage(Console.Out); - return 0; - } - - var isVerify = argsReader.ReadVerifyFlag(); + WriteUsage(Console.Out); + return 0; + } - var settings = new XmlDocMarkdownSettings - { - NewLine = argsReader.ReadNewLineOption(), - SourceCodePath = argsReader.ReadSourceOption(), - RootNamespace = argsReader.ReadNamespaceOption(), - IncludeObsolete = argsReader.ReadObsoleteFlag(), - SkipUnbrowsable = argsReader.ReadSkipUnbrowsableFlag(), - SkipCompilerGenerated = argsReader.ReadSkipCompilerGeneratedFlag(), - VisibilityLevel = argsReader.ReadVisibilityOption(), - ShouldClean = argsReader.ReadCleanFlag(), - IsQuiet = argsReader.ReadQuietFlag(), - IsDryRun = isVerify || argsReader.ReadDryRunFlag(), - FrontMatter = argsReader.ReadFrontMatter(), - PermalinkStyle = argsReader.ReadPermalinkStyle(), - GenerateToc = argsReader.ReadTocFlag(), - TocPrefix = argsReader.ReadTocPrefix(), - NamespacePages = argsReader.ReadNamespacePagesFlag(), - }; + var isVerify = argsReader.ReadVerifyFlag(); - var externalDocs = new List(); - while (argsReader.ReadExternalOption() is { } externalOption) - externalDocs.Add(new ExternalDocumentation { Namespace = externalOption }); - if (externalDocs.Count != 0) - settings.ExternalDocs = externalDocs; + var settings = new XmlDocMarkdownSettings + { + ShouldClean = argsReader.ReadCleanFlag(), + IsQuiet = argsReader.ReadQuietFlag(), + IsDryRun = isVerify || argsReader.ReadDryRunFlag(), + }; - var inputPath = argsReader.ReadArgument() ?? throw new ArgsReaderException("Missing input path."); - var input = File.Exists(inputPath) - ? new XmlDocInput { AssemblyPath = inputPath } - : new XmlDocInput { Assembly = Assembly.Load(inputPath) }; + var assemblyName = argsReader.ReadArgument() ?? throw new ArgsReaderException("Missing assembly name."); + var outputPath = argsReader.ReadArgument() ?? throw new ArgsReaderException("Missing output path."); + argsReader.VerifyComplete(); - var outputPath = argsReader.ReadArgument() ?? throw new ArgsReaderException("Missing output path."); - argsReader.VerifyComplete(); + configure?.Invoke(assemblyName, settings); - var result = XmlDocMarkdownGenerator.Generate(input, outputPath, settings); + var assembly = Assembly.Load(assemblyName); + var result = XmlDocMarkdownGenerator.Generate(assembly, outputPath, settings); - foreach (var message in result.Messages) - Console.WriteLine(message); + foreach (var message in result.Messages) + Console.WriteLine(message); - return isVerify && result.Added.Count + result.Changed.Count + result.Removed.Count != 0 ? 1 : 0; + return isVerify && result.Added.Count + result.Changed.Count + result.Removed.Count != 0 ? 1 : 0; + } + catch (Exception exception) + { + if (exception is ArgsReaderException) + { + Console.Error.WriteLine(exception.Message); + Console.Error.WriteLine(); + WriteUsage(Console.Error); + return 2; } - catch (Exception exception) + else if (exception is ApplicationException or IOException or UnauthorizedAccessException) { - if (exception is ArgsReaderException) - { - Console.Error.WriteLine(exception.Message); - Console.Error.WriteLine(); - WriteUsage(Console.Error); - return 2; - } - else if (exception is ApplicationException || exception is IOException || exception is UnauthorizedAccessException) - { - Console.Error.WriteLine(exception.Message); - return 3; - } - else - { - Console.Error.WriteLine(exception.ToString()); - return 3; - } + Console.Error.WriteLine(exception.Message); + return 3; + } + else + { + Console.Error.WriteLine(exception.ToString()); + return 3; } } + } - private static void WriteUsage(TextWriter textWriter) - { - textWriter.WriteLine("Generates Markdown from .NET XML documentation comments."); - textWriter.WriteLine(); - textWriter.WriteLine($"Usage: {Assembly.GetEntryAssembly()?.GetName().Name} input output [options]"); - textWriter.WriteLine(); - textWriter.WriteLine(" input"); - textWriter.WriteLine(" The path or name of the input assembly."); - textWriter.WriteLine(" output"); - textWriter.WriteLine(" The path of the output directory."); - textWriter.WriteLine(); - textWriter.WriteLine(" --source "); - textWriter.WriteLine(" The URL (absolute or relative) of the folder containing the source"); - textWriter.WriteLine(" code of the assembly, e.g. at GitHub. (optional)"); - textWriter.WriteLine(" --namespace "); - textWriter.WriteLine(" The root namespace of the input assembly. (optional)"); - textWriter.WriteLine(" --visibility (public|protected|internal|private)"); - textWriter.WriteLine(" The minimum visibility of documented members. (default 'protected')"); - textWriter.WriteLine(" --obsolete"); - textWriter.WriteLine(" Generates documentation for obsolete types and members."); - textWriter.WriteLine(" --skip-unbrowsable"); - textWriter.WriteLine(" Skips documentation for types that are marked with System.ComponentModel.EditorBrowsable Never."); - textWriter.WriteLine(" --skip-compiler-generated"); - textWriter.WriteLine(" Skips documentation for types that are marked with System.Runtime.CompilerServices.CompilerGenerated."); - textWriter.WriteLine(" --clean"); - textWriter.WriteLine(" Deletes previously generated files that are no longer used."); - textWriter.WriteLine(" --verify"); - textWriter.WriteLine(" Exits with error code 1 if changes to the file system are needed."); - textWriter.WriteLine(" --dryrun"); - textWriter.WriteLine(" Executes the tool without making changes to the file system."); - textWriter.WriteLine(" --quiet"); - textWriter.WriteLine(" Suppresses normal console output."); - textWriter.WriteLine(" --front-matter"); - textWriter.WriteLine(" File containing the Jekyll front matter template you want in each generated page."); - textWriter.WriteLine(" The front matter can use $title argument and $rel for permalinks."); - textWriter.WriteLine(" When front matter is defined the .md extension is dropped in all generated links."); - textWriter.WriteLine(" --permalink"); - textWriter.WriteLine(" Specify permalink style, 'none' or 'pretty' (default 'none')."); - textWriter.WriteLine(" 'pretty' permalinks do not contain file extensions, and when you select this option."); - textWriter.WriteLine(" periods have to be removed from file names, for example, 'System.Console' would have to be 'SystemConsole'."); - textWriter.WriteLine(" since the removal of the '.md' extension would make Jekyll think .Console is a file extension which doesn't work."); - textWriter.WriteLine(" --namespace-pages"); - textWriter.WriteLine(" Generate separate pages for each namespace, listing types in each."); - textWriter.WriteLine(" --toc"); - textWriter.WriteLine(" File containing table of contents in .yml format."); - textWriter.WriteLine(" --newline (auto|lf|crlf)"); - textWriter.WriteLine(" The newline used in the output (default auto)."); - } + private static void WriteUsage(TextWriter textWriter) + { + textWriter.WriteLine("Generates Markdown from .NET XML documentation comments."); + textWriter.WriteLine(); + textWriter.WriteLine($"Usage: {Assembly.GetEntryAssembly()?.GetName().Name} input output [options]"); + textWriter.WriteLine(); + textWriter.WriteLine(" input"); + textWriter.WriteLine(" The name of the input assembly."); + textWriter.WriteLine(" output"); + textWriter.WriteLine(" The path of the output directory."); + textWriter.WriteLine(); + textWriter.WriteLine(" --clean"); + textWriter.WriteLine(" Deletes previously generated files that are no longer used."); + textWriter.WriteLine(" --dryrun"); + textWriter.WriteLine(" Executes the tool without making changes to the file system."); + textWriter.WriteLine(" --quiet"); + textWriter.WriteLine(" Suppresses normal console output."); + textWriter.WriteLine(" --verify"); + textWriter.WriteLine(" Exits with error code 1 if changes to the file system are needed."); } } diff --git a/src/XmlDocMarkdown.Core/XmlDocMarkdownGenerator.cs b/src/XmlDocMarkdown.Core/XmlDocMarkdownGenerator.cs index 55afb85..c63bdc2 100644 --- a/src/XmlDocMarkdown.Core/XmlDocMarkdownGenerator.cs +++ b/src/XmlDocMarkdown.Core/XmlDocMarkdownGenerator.cs @@ -1,208 +1,165 @@ using System.Reflection; using System.Xml.Linq; -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +/// +/// Generates Markdown from .NET XML documentation comments. +/// +public static class XmlDocMarkdownGenerator { /// /// Generates Markdown from .NET XML documentation comments. /// - public static class XmlDocMarkdownGenerator + /// The input assembly. + /// The output directory. + /// The settings. + /// The names of files that were added, changed, or removed. + public static XmlDocMarkdownResult Generate(Assembly assembly, string outputPath, XmlDocMarkdownSettings? settings) { - /// - /// Generates Markdown from .NET XML documentation comments. - /// - /// The input assembly. - /// The output directory. - /// The settings. - /// The names of files that were added, changed, or removed. - public static XmlDocMarkdownResult Generate(string inputPath, string outputPath, XmlDocMarkdownSettings? settings) - { - if (inputPath == null) - throw new ArgumentNullException(nameof(inputPath)); - - return Generate(new XmlDocInput { AssemblyPath = inputPath }, outputPath, settings); - } + ArgumentNullException.ThrowIfNull(assembly); + ArgumentNullException.ThrowIfNull(outputPath); - /// - /// Generates Markdown from .NET XML documentation comments. - /// - /// The input. - /// The output directory. - /// The settings. - /// The names of files that were added, changed, or removed. - public static XmlDocMarkdownResult Generate(XmlDocInput input, string outputPath, XmlDocMarkdownSettings? settings) - { - if (input == null) - throw new ArgumentNullException(nameof(input)); - if (outputPath == null) - throw new ArgumentNullException(nameof(outputPath)); - - var result = new XmlDocMarkdownResult(); + var result = new XmlDocMarkdownResult(); - settings ??= new XmlDocMarkdownSettings(); + settings ??= new XmlDocMarkdownSettings(); - var generator = new MarkdownGenerator - { - SourceCodePath = settings.SourceCodePath, - RootNamespace = settings.RootNamespace, - IncludeObsolete = settings.IncludeObsolete, - SkipUnbrowsable = settings.SkipUnbrowsable, - SkipCompilerGenerated = settings.SkipCompilerGenerated, - Visibility = settings.VisibilityLevel ?? XmlDocVisibilityLevel.Protected, - ExternalDocs = settings.ExternalDocs, - NamespacePages = settings.NamespacePages, - FrontMatter = settings.FrontMatter, - }; - if (settings.NewLine != null) - generator.NewLine = settings.NewLine; - - if (string.Equals(settings.PermalinkStyle, "pretty", StringComparison.OrdinalIgnoreCase)) - generator.PermalinkPretty = true; - - XmlDocAssembly xmlDocAssembly; - - var assembly = input.Assembly ?? Assembly.LoadFrom(input.AssemblyPath); - var xmlDocPath = input.XmlDocPath; - if (xmlDocPath == null) - { - var assemblyPath = input.AssemblyPath ?? assembly.Location; - - xmlDocPath = Path.ChangeExtension(assemblyPath, ".xml"); - if (!File.Exists(xmlDocPath)) - xmlDocPath = Path.ChangeExtension(assemblyPath, ".XML"); - } - - if (xmlDocPath != null && File.Exists(xmlDocPath)) - { - var xDocument = XDocument.Load(xmlDocPath); - xmlDocAssembly = new XmlDocAssembly(xDocument); - } - else - { - xmlDocAssembly = new XmlDocAssembly(); - } + var generator = new MarkdownGenerator + { + NewLine = settings.NewLine, + IncludeObsolete = settings.IncludeObsolete, + SkipUnbrowsable = settings.SkipUnbrowsable, + SkipCompilerGenerated = settings.SkipCompilerGenerated, + NamespacePages = settings.NamespacePages, + Visibility = settings.VisibilityLevel ?? XmlDocVisibilityLevel.Protected, + ExternalDocs = settings.ExternalDocs, + FrontMatter = settings.FrontMatter, + }; + + if (string.Equals(settings.PermalinkStyle, "pretty", StringComparison.OrdinalIgnoreCase)) + generator.PermalinkPretty = true; + + var assemblyPath = assembly.Location; + + var xmlDocPath = Path.ChangeExtension(assemblyPath, ".xml"); + if (!File.Exists(xmlDocPath)) + xmlDocPath = Path.ChangeExtension(assemblyPath, ".XML"); + + XmlDocAssembly xmlDocAssembly; + if (File.Exists(xmlDocPath)) + { + var xDocument = XDocument.Load(xmlDocPath); + xmlDocAssembly = new XmlDocAssembly(xDocument); + } + else + { + xmlDocAssembly = new XmlDocAssembly(); + } - var namedTexts = generator.GenerateOutput(assembly, xmlDocAssembly); + var namedTexts = generator.GenerateOutput(assembly, xmlDocAssembly); - var namedTextsToWrite = new List(); - foreach (var namedText in namedTexts) + var namedTextsToWrite = new List(); + foreach (var namedText in namedTexts) + { + var existingFilePath = Path.Combine(outputPath, namedText.Name); + if (File.Exists(existingFilePath)) { - var existingFilePath = Path.Combine(outputPath, namedText.Name); - if (File.Exists(existingFilePath)) - { - // ignore CR when comparing files - if (namedText.Text.Replace("\r", "") != File.ReadAllText(existingFilePath).Replace("\r", "")) - { - namedTextsToWrite.Add(namedText); - result.Changed.Add(namedText.Name); - if (!settings.IsQuiet) - result.Messages.Add("changed " + namedText.Name); - } - } - else + // ignore CR when comparing files + if (namedText.Text.Replace("\r", "", StringComparison.Ordinal) != File.ReadAllText(existingFilePath).Replace("\r", "", StringComparison.Ordinal)) { namedTextsToWrite.Add(namedText); - result.Added.Add(namedText.Name); + result.Changed.Add(namedText.Name); if (!settings.IsQuiet) - result.Messages.Add("added " + namedText.Name); + result.Messages.Add("changed " + namedText.Name); } } - - if (settings.GenerateToc) + else { - var tocPath = Path.Combine(outputPath, "toc.yml"); - - var root = namedTexts.FirstOrDefault(); - if (root != null) - { - var toc = new XmlDocToc { Path = root.Name, Title = root.Title, Prefix = settings.TocPrefix }; - - foreach (var namedText in namedTexts.Skip(1)) - toc.AddChild(namedText.Name, namedText.Parent, namedText.Title); - - toc.Save(tocPath); - } + namedTextsToWrite.Add(namedText); + result.Added.Add(namedText.Name); + if (!settings.IsQuiet) + result.Messages.Add("added " + namedText.Name); } + } - var namesToDelete = new List(); - if (settings.ShouldClean) + var namesToDelete = new List(); + if (settings.ShouldClean) + { + var directoryInfo = new DirectoryInfo(outputPath); + if (directoryInfo.Exists) { - var directoryInfo = new DirectoryInfo(outputPath); - if (directoryInfo.Exists) + var assemblyName = assembly.GetName().Name; + var assemblyFilePath = assembly.Modules.FirstOrDefault()?.FullyQualifiedName; + var assemblyFileName = assemblyFilePath is not null ? Path.GetFileName(assemblyFilePath) : assemblyName; + var assemblyFolder = Path.GetFileNameWithoutExtension(assemblyFileName); + var patterns = new[] { $"{assemblyFolder}/*.md", $"{assemblyFolder}/*/*.md" }; + var codeGenComment = MarkdownGenerator.GetCodeGenComment(assemblyFileName ?? ""); + + foreach (var nameMatchingPattern in FindNamesMatchingPatterns(directoryInfo, patterns, codeGenComment)) { - var assemblyName = assembly.GetName().Name; - var assemblyFilePath = assembly.Modules.FirstOrDefault()?.FullyQualifiedName; - var assemblyFileName = assemblyFilePath != null ? Path.GetFileName(assemblyFilePath) : assemblyName; - var assemblyFolder = Path.GetFileNameWithoutExtension(assemblyFileName); - var patterns = new[] { $"{assemblyFolder}/*.md", $"{assemblyFolder}/*/*.md" }; - var codeGenComment = MarkdownGenerator.GetCodeGenComment(assemblyFileName); - - foreach (var nameMatchingPattern in FindNamesMatchingPatterns(directoryInfo, patterns, codeGenComment)) + if (namedTexts.All(x => x.Name != nameMatchingPattern)) { - if (namedTexts.All(x => x.Name != nameMatchingPattern)) - { - namesToDelete.Add(nameMatchingPattern); - result.Removed.Add(nameMatchingPattern); - if (!settings.IsQuiet) - result.Messages.Add("removed " + nameMatchingPattern); - } + namesToDelete.Add(nameMatchingPattern); + result.Removed.Add(nameMatchingPattern); + if (!settings.IsQuiet) + result.Messages.Add("removed " + nameMatchingPattern); } } } + } - if (!settings.IsDryRun) - { - if (!Directory.Exists(outputPath)) - Directory.CreateDirectory(outputPath); - - foreach (var namedText in namedTextsToWrite) - { - var outputFilePath = Path.Combine(outputPath, namedText.Name); + if (!settings.IsDryRun) + { + if (!Directory.Exists(outputPath)) + Directory.CreateDirectory(outputPath); - var outputFileDirectoryPath = Path.GetDirectoryName(outputFilePath); - if (outputFileDirectoryPath != null && outputFileDirectoryPath != outputPath && !Directory.Exists(outputFileDirectoryPath)) - Directory.CreateDirectory(outputFileDirectoryPath); + foreach (var namedText in namedTextsToWrite) + { + var outputFilePath = Path.Combine(outputPath, namedText.Name); - File.WriteAllText(outputFilePath, namedText.Text); - } + var outputFileDirectoryPath = Path.GetDirectoryName(outputFilePath); + if (outputFileDirectoryPath is not null && outputFileDirectoryPath != outputPath && !Directory.Exists(outputFileDirectoryPath)) + Directory.CreateDirectory(outputFileDirectoryPath); - foreach (var nameToDelete in namesToDelete) - File.Delete(Path.Combine(outputPath, nameToDelete)); + File.WriteAllText(outputFilePath, namedText.Text); } - return result; + foreach (var nameToDelete in namesToDelete) + File.Delete(Path.Combine(outputPath, nameToDelete)); } - private static IEnumerable FindNamesMatchingPatterns(DirectoryInfo directoryInfo, IReadOnlyList namePatterns, string requiredSubstring) + return result; + } + + private static IEnumerable FindNamesMatchingPatterns(DirectoryInfo directoryInfo, IReadOnlyList namePatterns, string requiredSubstring) + { + foreach (var namePattern in namePatterns) { - foreach (var namePattern in namePatterns) - { - foreach (var name in FindNamesMatchingPattern(directoryInfo, namePattern, requiredSubstring)) - yield return name; - } + foreach (var name in FindNamesMatchingPattern(directoryInfo, namePattern, requiredSubstring)) + yield return name; } + } - private static IEnumerable FindNamesMatchingPattern(DirectoryInfo directoryInfo, string namePattern, string requiredSubstring) - { - var parts = namePattern.Split(['/'], 2); - if (parts[0].Length == 0) - throw new InvalidOperationException("Invalid name pattern."); + private static IEnumerable FindNamesMatchingPattern(DirectoryInfo directoryInfo, string namePattern, string requiredSubstring) + { + var parts = namePattern.Split(['/'], 2); + if (parts[0].Length == 0) + throw new InvalidOperationException("Invalid name pattern."); - if (parts.Length == 1) + if (parts.Length == 1) + { + foreach (var fileInfo in directoryInfo.GetFiles(parts[0])) { - foreach (var fileInfo in directoryInfo.GetFiles(parts[0])) - { - if (File.ReadAllText(fileInfo.FullName).Contains(requiredSubstring)) - yield return fileInfo.Name; - } + if (File.ReadAllText(fileInfo.FullName).Contains(requiredSubstring, StringComparison.Ordinal)) + yield return fileInfo.Name; } - else + } + else + { + foreach (var subdirectoryInfo in directoryInfo.GetDirectories(parts[0])) { - foreach (var subdirectoryInfo in directoryInfo.GetDirectories(parts[0])) - { - foreach (var name in FindNamesMatchingPattern(subdirectoryInfo, parts[1], requiredSubstring)) - yield return subdirectoryInfo.Name + '/' + name; - } + foreach (var name in FindNamesMatchingPattern(subdirectoryInfo, parts[1], requiredSubstring)) + yield return subdirectoryInfo.Name + '/' + name; } } } diff --git a/src/XmlDocMarkdown.Core/XmlDocMarkdownResult.cs b/src/XmlDocMarkdown.Core/XmlDocMarkdownResult.cs index aae6afc..626cdee 100644 --- a/src/XmlDocMarkdown.Core/XmlDocMarkdownResult.cs +++ b/src/XmlDocMarkdown.Core/XmlDocMarkdownResult.cs @@ -1,30 +1,29 @@ using System.Collections.ObjectModel; -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +/// +/// The names of files that were added, changed, or removed. +/// +public sealed class XmlDocMarkdownResult { /// - /// The names of files that were added, changed, or removed. + /// The names of files that were added. /// - public sealed class XmlDocMarkdownResult - { - /// - /// The names of files that were added. - /// - public Collection Added { get; } = new(); + public Collection Added { get; } = []; - /// - /// The names of files that were changed. - /// - public Collection Changed { get; } = new(); + /// + /// The names of files that were changed. + /// + public Collection Changed { get; } = []; - /// - /// The names of files that were removed. - /// - public Collection Removed { get; } = new(); + /// + /// The names of files that were removed. + /// + public Collection Removed { get; } = []; - /// - /// Messages that should be displayed (unless quiet). - /// - public Collection Messages { get; } = new(); - } + /// + /// Messages that should be displayed (unless quiet). + /// + public Collection Messages { get; } = []; } diff --git a/src/XmlDocMarkdown.Core/XmlDocMarkdownSettings.cs b/src/XmlDocMarkdown.Core/XmlDocMarkdownSettings.cs index 10c1f8f..395967f 100644 --- a/src/XmlDocMarkdown.Core/XmlDocMarkdownSettings.cs +++ b/src/XmlDocMarkdown.Core/XmlDocMarkdownSettings.cs @@ -1,97 +1,79 @@ -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +/// +/// Settings for markdown generation. +/// +public class XmlDocMarkdownSettings { /// - /// Settings for markdown generation. + /// The URL of the folder containing the source code of the assembly, e.g. at GitHub. /// - public class XmlDocMarkdownSettings - { - /// - /// The URL of the folder containing the source code of the assembly, e.g. at GitHub. - /// - /// The URL may be absolute or relative. Required to generate source code - /// links in the See Also sections for types. - public string? SourceCodePath { get; set; } - - /// - /// The root namespace of the input assembly. - /// - /// Used to generate source code links in the See Also sections for types. - /// If omitted, the tool guesses the root namespace from the exported types. - public string? RootNamespace { get; set; } - - /// - /// If true, generates documentation for obsolete types and members. (Default false.) - /// - public bool IncludeObsolete { get; set; } - - /// - /// If true, skips documentation for types and members with [EditorBrowsable(EditorBrowsableState.Never)]. - /// - public bool SkipUnbrowsable { get; set; } + /// The URL may be absolute or relative. Required to generate source code + /// links in the See Also sections for types. + public string? SourceCodePath { get; set; } - /// - /// If true, skips documentation for types and members with [CompilerGenerated]. - /// - public bool SkipCompilerGenerated { get; set; } + /// + /// If true, generates documentation for obsolete types and members. (Default false.) + /// + public bool IncludeObsolete { get; set; } - /// - /// The minimum visibility for documented types and members. - /// - /// Defaults to Protected. - public XmlDocVisibilityLevel? VisibilityLevel { get; set; } + /// + /// If true, skips documentation for types and members with [EditorBrowsable(EditorBrowsableState.Never)]. + /// + public bool SkipUnbrowsable { get; set; } - /// - /// Indicates the newline used in the output. - /// - /// Defaults to "\r\n" or "\n", depending on the platform. - public string? NewLine { get; set; } + /// + /// If true, skips documentation for types and members with [CompilerGenerated]. + /// + public bool SkipCompilerGenerated { get; set; } - /// - /// If true, deletes previously generated files that are no longer used. - /// - public bool ShouldClean { get; set; } + /// + /// The minimum visibility for documented types and members. + /// + /// Defaults to Protected. + public XmlDocVisibilityLevel? VisibilityLevel { get; set; } - /// - /// If true, generates a .yml file that can be used in a Jekyll based site. - /// - public bool GenerateToc { get; set; } + /// + /// Indicates the newline used in the output. + /// + /// Defaults to "\r\n" or "\n", depending on the platform. + public string? NewLine { get; set; } - /// - /// Generate separate pages for each namespace containing list of types in each. - /// - public bool NamespacePages { get; set; } + /// + /// If true, deletes previously generated files that are no longer used. + /// + public bool ShouldClean { get; set; } - /// - /// A path prefix to add to all links in the table of contents .yml file. - /// - public string? TocPrefix { get; set; } + /// + /// Generate separate pages for each namespace containing list of types in each. + /// + public bool NamespacePages { get; set; } - /// - /// If true, suppresses normal console output. - /// - public bool IsQuiet { get; set; } + /// + /// If true, suppresses normal console output. + /// + public bool IsQuiet { get; set; } - /// - /// If true, executes without making changes to the file system. - /// - public bool IsDryRun { get; set; } + /// + /// If true, executes without making changes to the file system. + /// + public bool IsDryRun { get; set; } - /// - /// If non-null, contains the path to a file that contains the Jekyll front matter template. - /// - public string? FrontMatter { get; set; } + /// + /// If non-null, contains the path to a file that contains the Jekyll front matter template. + /// + public string? FrontMatter { get; set; } - /// - /// Specify permalink style, 'none' or 'pretty' (default 'none'). - /// 'pretty' permalinks do not contain file extensions, and when you select this option - /// periods have to be removed from file names, for example, 'System.Console' would have to be 'SystemConsole'. - /// since the removal of the '.md' extension would make Jekyll think '.Console' is a file extension which doesn't work. - /// - public string? PermalinkStyle { get; set; } + /// + /// Specify permalink style, 'none' or 'pretty' (default 'none'). + /// 'pretty' permalinks do not contain file extensions, and when you select this option + /// periods have to be removed from file names, for example, 'System.Console' would have to be 'SystemConsole'. + /// since the removal of the '.md' extension would make Jekyll think '.Console' is a file extension which doesn't work. + /// + public string? PermalinkStyle { get; set; } - /// - /// Configures external documentation. - /// - public IReadOnlyList? ExternalDocs { get; set; } - } + /// + /// Configures external documentation. + /// + public IReadOnlyList? ExternalDocs { get; set; } } diff --git a/src/XmlDocMarkdown.Core/XmlDocMember.cs b/src/XmlDocMarkdown.Core/XmlDocMember.cs index ab44c81..beb7e07 100644 --- a/src/XmlDocMarkdown.Core/XmlDocMember.cs +++ b/src/XmlDocMarkdown.Core/XmlDocMember.cs @@ -1,270 +1,269 @@ using System.Collections.ObjectModel; using System.Xml.Linq; -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +internal sealed class XmlDocMember { - internal sealed class XmlDocMember + public XmlDocMember(XElement xMember) { - public XmlDocMember(XElement xMember) - { - XmlDocName = xMember.Attribute("name")?.Value; + XmlDocName = xMember.Attribute("name")?.Value; - foreach (var xElement in xMember.Elements()) + foreach (var xElement in xMember.Elements()) + { + switch (xElement.Name.LocalName) { - switch (xElement.Name.LocalName) - { - case "summary": - AddBlocks(xElement, Summary); - break; - - case "typeparam": - AddParameter(xElement, TypeParameters); - break; - - case "param": - AddParameter(xElement, Parameters); - break; - - case "returns": - AddBlocks(xElement, ReturnValue); - break; - - case "value": - AddBlocks(xElement, PropertyValue); - break; - - case "exception": - AddException(xElement, Exceptions); - break; - - case "remarks": - AddBlocks(xElement, Remarks); - break; - - case "example": - AddBlocks(xElement, Examples); - break; - - case "seealso": - AddSeeAlso(xElement, SeeAlso); - break; - } + case "summary": + AddBlocks(xElement, Summary); + break; + + case "typeparam": + AddParameter(xElement, TypeParameters); + break; + + case "param": + AddParameter(xElement, Parameters); + break; + + case "returns": + AddBlocks(xElement, ReturnValue); + break; + + case "value": + AddBlocks(xElement, PropertyValue); + break; + + case "exception": + AddException(xElement, Exceptions); + break; + + case "remarks": + AddBlocks(xElement, Remarks); + break; + + case "example": + AddBlocks(xElement, Examples); + break; + + case "seealso": + AddSeeAlso(xElement, SeeAlso); + break; } } + } - public string? XmlDocName { get; set; } + public string? XmlDocName { get; set; } - public Collection Summary { get; } = new(); + public Collection Summary { get; } = []; - public Collection TypeParameters { get; } = new(); + public Collection TypeParameters { get; } = []; - public Collection Parameters { get; } = new(); + public Collection Parameters { get; } = []; - public Collection ReturnValue { get; } = new(); + public Collection ReturnValue { get; } = []; - public Collection PropertyValue { get; } = new(); + public Collection PropertyValue { get; } = []; - public Collection Exceptions { get; } = new(); + public Collection Exceptions { get; } = []; - public Collection Remarks { get; } = new(); + public Collection Remarks { get; } = []; - public Collection Examples { get; } = new(); + public Collection Examples { get; } = []; - public Collection SeeAlso { get; } = new(); + public Collection SeeAlso { get; } = []; - public override string ToString() => XmlDocName ?? ""; + public override string ToString() => XmlDocName ?? ""; - private static void AddBlocks(XElement xElement, Collection blocks) - { - var generator = new BlockGenerator(); - generator.AddNodes(xElement.Nodes()); - foreach (var block in generator.GetBlocks()) - blocks.Add(block); - } + private static void AddBlocks(XElement xElement, Collection blocks) + { + var generator = new BlockGenerator(); + generator.AddNodes(xElement.Nodes()); + foreach (var block in generator.GetBlocks()) + blocks.Add(block); + } - private static void AddParameter(XElement xElement, Collection parameters) - { - var parameter = new XmlDocParameter { Name = xElement.Attribute("name")?.Value }; - AddBlocks(xElement, parameter.Description); - parameters.Add(parameter); - } + private static void AddParameter(XElement xElement, Collection parameters) + { + var parameter = new XmlDocParameter { Name = xElement.Attribute("name")?.Value }; + AddBlocks(xElement, parameter.Description); + parameters.Add(parameter); + } + + private static void AddException(XElement xElement, Collection exceptions) + { + var exception = new XmlDocException { ExceptionTypeRef = xElement.Attribute("cref")?.Value }; + AddBlocks(xElement, exception.Condition); + exceptions.Add(exception); + } - private static void AddException(XElement xElement, Collection exceptions) + private void AddSeeAlso(XElement xElement, Collection seeAlso) + { + seeAlso.Add(new XmlDocSeeAlso { Ref = xElement.Attribute("cref")?.Value }); + } + + private sealed class BlockGenerator + { + public BlockGenerator() { - var exception = new XmlDocException { ExceptionTypeRef = xElement.Attribute("cref")?.Value }; - AddBlocks(xElement, exception.Condition); - exceptions.Add(exception); + m_blocks = new List(); + m_listKinds = new Stack(); + + NextBlock(); } - private void AddSeeAlso(XElement xElement, Collection seeAlso) + public void AddNodes(IEnumerable xNodes) { - seeAlso.Add(new XmlDocSeeAlso { Ref = xElement.Attribute("cref")?.Value }); + foreach (var xNode in xNodes) + AddNode(xNode); } - private sealed class BlockGenerator + public List GetBlocks() { - public BlockGenerator() - { - m_blocks = new List(); - m_listKinds = new Stack(); - - NextBlock(); - } + NextBlock(); - public void AddNodes(IEnumerable xNodes) - { - foreach (var xNode in xNodes) - AddNode(xNode); - } + var blocks = m_blocks; + m_blocks = null; + return blocks ?? throw new InvalidOperationException(); + } - public List GetBlocks() - { - NextBlock(); + private void AddNode(XNode xNode) + { + if (xNode is XElement xElement) + AddElement(xElement); - var blocks = m_blocks; - m_blocks = null; - return blocks ?? throw new InvalidOperationException(); - } + if (xNode is XText xText) + m_block?.Inlines.Add(new XmlDocInline { Text = xText.Value }); + } - private void AddNode(XNode xNode) + private void AddElement(XElement xElement) + { + switch (xElement.Name.LocalName) { - if (xNode is XElement xElement) - AddElement(xElement); - - if (xNode is XText xText) - m_block?.Inlines.Add(new XmlDocInline { Text = xText.Value }); + case "para": + NextBlock(); + AddNodes(xElement.Nodes()); + NextBlock(); + break; + + case "code": + NextBlock(); + m_block!.IsCode = true; + m_block.Inlines.Add(new XmlDocInline { Text = TrimCode(xElement.Value) }); + NextBlock(); + break; + + case "list": + m_listKinds.Push(GetListKind(xElement)); + NextBlock(); + AddNodes(xElement.Nodes()); + m_listKinds.Pop(); + NextBlock(); + break; + + case "listheader": + m_isListHeader = true; + NextBlock(); + AddNodes(xElement.Nodes()); + NextBlock(); + m_isListHeader = false; + break; + + case "item": + NextBlock(); + AddNodes(xElement.Nodes()); + NextBlock(); + break; + + case "term": + m_isListTerm = true; + NextBlock(); + AddNodes(xElement.Nodes()); + m_isListTerm = false; + NextBlock(); + break; + + case "description": + NextBlock(); + AddNodes(xElement.Nodes()); + NextBlock(); + break; + + case "c": + m_block?.Inlines.Add(new XmlDocInline { Text = xElement.Value, IsCode = true }); + break; + + case "see": + m_block?.Inlines.Add(new XmlDocInline { Text = xElement.Value, SeeRef = xElement.Attribute("cref")?.Value, LinkUrl = xElement.Attribute("href")?.Value, LangWord = xElement.Attribute("langword")?.Value }); + break; + + case "a": + m_block?.Inlines.Add(new XmlDocInline { Text = xElement.Value, LinkUrl = xElement.Attribute("href")?.Value }); + break; + + case "paramref": + m_block?.Inlines.Add(new XmlDocInline { Text = (string?) xElement.Attribute("name"), IsParamRef = true }); + break; + + case "typeparamref": + m_block?.Inlines.Add(new XmlDocInline { Text = (string?) xElement.Attribute("name"), IsTypeParamRef = true }); + break; + + default: + AddNodes(xElement.Nodes()); + break; } + } - private void AddElement(XElement xElement) - { - switch (xElement.Name.LocalName) - { - case "para": - NextBlock(); - AddNodes(xElement.Nodes()); - NextBlock(); - break; - - case "code": - NextBlock(); - m_block!.IsCode = true; - m_block.Inlines.Add(new XmlDocInline { Text = TrimCode(xElement.Value) }); - NextBlock(); - break; - - case "list": - m_listKinds.Push(GetListKind(xElement)); - NextBlock(); - AddNodes(xElement.Nodes()); - m_listKinds.Pop(); - NextBlock(); - break; - - case "listheader": - m_isListHeader = true; - NextBlock(); - AddNodes(xElement.Nodes()); - NextBlock(); - m_isListHeader = false; - break; - - case "item": - NextBlock(); - AddNodes(xElement.Nodes()); - NextBlock(); - break; - - case "term": - m_isListTerm = true; - NextBlock(); - AddNodes(xElement.Nodes()); - m_isListTerm = false; - NextBlock(); - break; - - case "description": - NextBlock(); - AddNodes(xElement.Nodes()); - NextBlock(); - break; - - case "c": - m_block?.Inlines.Add(new XmlDocInline { Text = xElement.Value, IsCode = true }); - break; - - case "see": - m_block?.Inlines.Add(new XmlDocInline { Text = xElement.Value, SeeRef = xElement.Attribute("cref")?.Value, LinkUrl = xElement.Attribute("href")?.Value, LangWord = xElement.Attribute("langword")?.Value }); - break; - - case "a": - m_block?.Inlines.Add(new XmlDocInline { Text = xElement.Value, LinkUrl = xElement.Attribute("href")?.Value }); - break; - - case "paramref": - m_block?.Inlines.Add(new XmlDocInline { Text = (string) xElement.Attribute("name"), IsParamRef = true }); - break; - - case "typeparamref": - m_block?.Inlines.Add(new XmlDocInline { Text = (string) xElement.Attribute("name"), IsTypeParamRef = true }); - break; - - default: - AddNodes(xElement.Nodes()); - break; - } - } + private void NextBlock() + { + if (m_blocks != null && m_block != null && m_block.Inlines.Count != 0) + m_blocks.Add(m_block); + m_block = new XmlDocBlock(); - private void NextBlock() + if (m_listKinds.Count != 0) { - if (m_blocks != null && m_block != null && m_block.Inlines.Count != 0) - m_blocks.Add(m_block); - m_block = new XmlDocBlock(); - - if (m_listKinds.Count != 0) - { - m_block.ListKind = m_listKinds.Peek(); - m_block.ListDepth = m_listKinds.Count - 1; - m_block.IsListHeader = m_isListHeader; - m_block.IsListTerm = m_isListTerm; - } + m_block.ListKind = m_listKinds.Peek(); + m_block.ListDepth = m_listKinds.Count - 1; + m_block.IsListHeader = m_isListHeader; + m_block.IsListTerm = m_isListTerm; } + } - private static XmlDocListKind GetListKind(XElement xElement) => - xElement.Attribute("type")?.Value.ToLowerInvariant() switch - { - "bullet" => XmlDocListKind.Bullet, - "number" => XmlDocListKind.Number, - "table" => XmlDocListKind.Table, - _ => XmlDocListKind.Other, - }; - - private static string TrimCode(string text) + private static XmlDocListKind GetListKind(XElement xElement) => + xElement.Attribute("type")?.Value.ToLowerInvariant() switch { - // trimming logic adapted from https://github.com/kzu/NuDoq - var lines = text.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.None).ToList(); + "bullet" => XmlDocListKind.Bullet, + "number" => XmlDocListKind.Number, + "table" => XmlDocListKind.Table, + _ => XmlDocListKind.Other, + }; - if (lines.Count != 0 && lines[0].Trim().Length == 0) - lines.RemoveAt(0); - if (lines.Count != 0 && lines[lines.Count - 1].Trim().Length == 0) - lines.RemoveAt(lines.Count - 1); + private static string TrimCode(string text) + { + // trimming logic adapted from https://github.com/kzu/NuDoq + var lines = text.Split([Environment.NewLine, "\n"], StringSplitOptions.None).ToList(); - if (lines.Count == 0) - return ""; + if (lines.Count != 0 && lines[0].Trim().Length == 0) + lines.RemoveAt(0); + if (lines.Count != 0 && lines[^1].Trim().Length == 0) + lines.RemoveAt(lines.Count - 1); - var indentLength = lines[0].Length - lines[0].TrimStart().Length; - if (indentLength <= 4 && lines[0].Length != 0 && lines[0][0] != '\t') - indentLength = 0; + if (lines.Count == 0) + return ""; - text = string.Join(Environment.NewLine, lines.Select(x => x.Length <= indentLength ? "" : x.Substring(indentLength))); + var indentLength = lines[0].Length - lines[0].TrimStart().Length; + if (indentLength <= 4 && lines[0].Length != 0 && lines[0][0] != '\t') + indentLength = 0; - return text; - } + text = string.Join(Environment.NewLine, lines.Select(x => x.Length <= indentLength ? "" : x.Substring(indentLength))); - private List? m_blocks; - private XmlDocBlock? m_block; - private readonly Stack m_listKinds; - private bool m_isListHeader; - private bool m_isListTerm; + return text; } + + private List? m_blocks; + private XmlDocBlock? m_block; + private readonly Stack m_listKinds; + private bool m_isListHeader; + private bool m_isListTerm; } } diff --git a/src/XmlDocMarkdown.Core/XmlDocParameter.cs b/src/XmlDocMarkdown.Core/XmlDocParameter.cs index 625584d..9856b2a 100644 --- a/src/XmlDocMarkdown.Core/XmlDocParameter.cs +++ b/src/XmlDocMarkdown.Core/XmlDocParameter.cs @@ -1,11 +1,10 @@ using System.Collections.ObjectModel; -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +internal sealed class XmlDocParameter { - internal sealed class XmlDocParameter - { - public string? Name { get; set; } + public string? Name { get; set; } - public Collection Description { get; } = new(); - } + public Collection Description { get; } = []; } diff --git a/src/XmlDocMarkdown.Core/XmlDocSeeAlso.cs b/src/XmlDocMarkdown.Core/XmlDocSeeAlso.cs index 93a27eb..03ebb32 100644 --- a/src/XmlDocMarkdown.Core/XmlDocSeeAlso.cs +++ b/src/XmlDocMarkdown.Core/XmlDocSeeAlso.cs @@ -1,7 +1,6 @@ -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +internal sealed class XmlDocSeeAlso { - internal sealed class XmlDocSeeAlso - { - public string? Ref { get; set; } - } + public string? Ref { get; set; } } diff --git a/src/XmlDocMarkdown.Core/XmlDocToc.cs b/src/XmlDocMarkdown.Core/XmlDocToc.cs deleted file mode 100644 index 4fdc153..0000000 --- a/src/XmlDocMarkdown.Core/XmlDocToc.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System.Text; - -namespace XmlDocMarkdown.Core -{ - /// - /// This class builds a .yml table of contents along the lines of what you see here: - /// https://jekyllrb.com/tutorials/navigation/#scenario-4-three-level-navigation-list - /// - internal sealed class XmlDocToc - { - public string? Prefix { get; set; } - - public string? Path { get; set; } - - public string? Title { get; set; } - - public List? Children { get; set; } - - public void AddChild(string path, string? parent, string title) - { - if (parent == null || parent == Path) - { - GetOrCreate(path, title); - return; - } - - var parentItem = FindParent(parent) ?? throw new InvalidOperationException($"Parent '{parent}' not found?"); - parentItem.GetOrCreate(path, title); - } - - private XmlDocToc? FindParent(string parent) - { - if (Children == null) - { - return null; - } - - foreach (var item in Children) - { - if (item.Path == parent) - { - return item; - } - var result = item.FindParent(parent); - if (result != null) - { - return result; - } - } - return null; - } - - private XmlDocToc GetOrCreate(string path, string title) - { - if (Children == null) - { - Children = new List(); - } - var item = (from i in Children where i.Path == path select i).FirstOrDefault(); - if (item == null) - { - item = new XmlDocToc() { Path = path, Title = title, Prefix = Prefix }; - Children.Add(item); - } - return item; - } - - internal void Save(string tocPath) - { - Directory.CreateDirectory(System.IO.Path.GetDirectoryName(tocPath)!); - using (var writer = new StreamWriter(tocPath, false, Encoding.UTF8)) - { - writer.WriteLine("toc:"); - Save(writer, " "); - } - } - - internal void Save(StreamWriter writer, string indent) - { - /* - toc: - - title: ... - link: relative link to the permalink - subfolderitems: - - name: ... - link: ... - */ - - var p = Path!; - if (p.EndsWith(".md", StringComparison.Ordinal)) - { - var pos = p.LastIndexOf('.'); - if (pos > 0) - { - p = p.Substring(0, pos); - } - } - - writer.Write(indent); - writer.WriteLine("- name: {0}", Title); - writer.Write(indent); - if (!string.IsNullOrEmpty(Prefix)) - { - writer.WriteLine(" link: {0}/{1}", Prefix, p); - } - else - { - writer.WriteLine(" link: {0}", p); - } - if (Children != null && Children.Count > 0) - { - writer.Write(indent); - writer.WriteLine(" subfolderitems:"); - foreach (var item in Children) - { - item.Save(writer, indent + " "); - } - } - } - } -} diff --git a/src/XmlDocMarkdown.Core/XmlDocUtility.cs b/src/XmlDocMarkdown.Core/XmlDocUtility.cs index 3ae288d..3a5b56b 100644 --- a/src/XmlDocMarkdown.Core/XmlDocUtility.cs +++ b/src/XmlDocMarkdown.Core/XmlDocUtility.cs @@ -2,120 +2,122 @@ using System.Text; using System.Text.RegularExpressions; -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +internal static partial class XmlDocUtility { - internal static class XmlDocUtility + public static string? GetXmlDocRef(MemberInfo? memberInfo) { - public static string? GetXmlDocRef(MemberInfo? memberInfo) + switch (memberInfo) { - switch (memberInfo) - { - case null: - return null; + case null: + return null; - case TypeInfo typeInfo: - return "T:" + GetXmlDocTypePart(typeInfo); + case TypeInfo typeInfo: + return "T:" + GetXmlDocTypePart(typeInfo); - case MethodBase methodBase: - var methodInfo = methodBase as MethodInfo; - return "M:" + - GetXmlDocMemberPart(methodBase) + - (methodBase.IsGenericMethodDefinition ? $"``{methodBase.GetGenericArguments().Length}" : "") + - GetXmlDocParameters(methodBase.GetParameters()) + - (methodInfo?.Name is "op_Implicit" or "op_Explicit" ? $"~{GetXmlDocTypePart(methodInfo.ReturnType.GetTypeInfo())}" : ""); + case MethodBase methodBase: + var methodInfo = methodBase as MethodInfo; + return "M:" + + GetXmlDocMemberPart(methodBase) + + (methodBase.IsGenericMethodDefinition ? $"``{methodBase.GetGenericArguments().Length}" : "") + + GetXmlDocParameters(methodBase.GetParameters()) + + (methodInfo?.Name is "op_Implicit" or "op_Explicit" ? $"~{GetXmlDocTypePart(methodInfo.ReturnType.GetTypeInfo())}" : ""); - case PropertyInfo propertyInfo: - return "P:" + - GetXmlDocMemberPart(propertyInfo) + - (propertyInfo.GetIndexParameters().Length == 0 ? "" : GetXmlDocParameters(propertyInfo.GetIndexParameters())); + case PropertyInfo propertyInfo: + return "P:" + + GetXmlDocMemberPart(propertyInfo) + + (propertyInfo.GetIndexParameters().Length == 0 ? "" : GetXmlDocParameters(propertyInfo.GetIndexParameters())); - case EventInfo eventInfo: - return "E:" + GetXmlDocMemberPart(eventInfo); + case EventInfo eventInfo: + return "E:" + GetXmlDocMemberPart(eventInfo); - case FieldInfo fieldInfo: - return "F:" + GetXmlDocMemberPart(fieldInfo); + case FieldInfo fieldInfo: + return "F:" + GetXmlDocMemberPart(fieldInfo); - default: - throw new InvalidOperationException("Unexpected member: " + memberInfo); - } + default: + throw new InvalidOperationException("Unexpected member: " + memberInfo); } + } + + public static string GetShortNameForXmlDocRef(string xmlDocRef) + { + var match = XmlDocNameRegex().Match(xmlDocRef); + return match.Success ? match.Groups["name"].Value : xmlDocRef; + } + + private static string GetXmlDocTypePart(TypeInfo typeInfo) + { + var stringBuilder = new StringBuilder(); - public static string GetShortNameForXmlDocRef(string xmlDocRef) + if (typeInfo.IsArray) { - var match = Regex.Match(xmlDocRef, @"^[A-Z]:([^\.]+\.)*(?'name'[^\.\(\{`]+)", RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture); - return match.Success ? match.Groups["name"].Value : xmlDocRef; + stringBuilder.Append(GetXmlDocTypePart(typeInfo.GetElementType()!.GetTypeInfo())); + stringBuilder.Append("[]"); } - - private static string GetXmlDocTypePart(TypeInfo typeInfo) + else if (typeInfo.IsByRef) + { + stringBuilder.Append(GetXmlDocTypePart(typeInfo.GetElementType()!.GetTypeInfo())); + stringBuilder.Append('@'); + } + else if (!typeInfo.IsGenericParameter) { - var stringBuilder = new StringBuilder(); + if (typeInfo.DeclaringType is not null) + stringBuilder.Append(GetXmlDocTypePart(typeInfo.DeclaringType.GetTypeInfo()) + "."); + else if (!string.IsNullOrEmpty(typeInfo.Namespace)) + stringBuilder.Append(typeInfo.Namespace + "."); - if (typeInfo.IsArray) - { - stringBuilder.Append(GetXmlDocTypePart(typeInfo.GetElementType().GetTypeInfo())); - stringBuilder.Append("[]"); - } - else if (typeInfo.IsByRef) - { - stringBuilder.Append(GetXmlDocTypePart(typeInfo.GetElementType().GetTypeInfo())); - stringBuilder.Append('@'); - } - else if (!typeInfo.IsGenericParameter) + var tickIndex = typeInfo.Name.IndexOf('`', StringComparison.Ordinal); + if (typeInfo is { IsGenericType: true, IsGenericTypeDefinition: false } && tickIndex != -1) { - if (typeInfo.DeclaringType is not null) - stringBuilder.Append(GetXmlDocTypePart(typeInfo.DeclaringType.GetTypeInfo()) + "."); - else if (!string.IsNullOrEmpty(typeInfo.Namespace)) - stringBuilder.Append(typeInfo.Namespace + "."); - - var tickIndex = typeInfo.Name.IndexOf('`'); - if (typeInfo is { IsGenericType: true, IsGenericTypeDefinition: false } && tickIndex != -1) - { - stringBuilder.Append(typeInfo.Name.Substring(0, tickIndex)); - stringBuilder.Append('{'); - stringBuilder.Append(string.Join(",", typeInfo.GenericTypeArguments.Select(x => GetXmlDocTypePart(x.GetTypeInfo())))); - stringBuilder.Append('}'); - } - else - { - stringBuilder.Append(typeInfo.Name); - } - } - else if (typeInfo.DeclaringMethod is { } declaringMethod) - { - var genericTypeIndex = declaringMethod.GetGenericArguments().ToList().IndexOf(typeInfo.AsType()); - if (genericTypeIndex == -1) - throw new InvalidOperationException("Unexpected type: " + typeInfo); - - stringBuilder.Append("``" + genericTypeIndex); + stringBuilder.Append(typeInfo.Name.AsSpan(0, tickIndex)); + stringBuilder.Append('{'); + stringBuilder.Append(string.Join(",", typeInfo.GenericTypeArguments.Select(x => GetXmlDocTypePart(x.GetTypeInfo())))); + stringBuilder.Append('}'); } else { - var genericTypeIndex = typeInfo.DeclaringType.GetTypeInfo().GenericTypeParameters.ToList().IndexOf(typeInfo.AsType()); - if (genericTypeIndex == -1) - throw new InvalidOperationException("Unexpected type: " + typeInfo); - - stringBuilder.Append("`" + genericTypeIndex); + stringBuilder.Append(typeInfo.Name); } + } + else if (typeInfo.DeclaringMethod is { } declaringMethod) + { + var genericTypeIndex = declaringMethod.GetGenericArguments().ToList().IndexOf(typeInfo.AsType()); + if (genericTypeIndex == -1) + throw new InvalidOperationException("Unexpected type: " + typeInfo); - if (stringBuilder.Length == 0) + stringBuilder.Append("``" + genericTypeIndex); + } + else + { + var genericTypeIndex = typeInfo.DeclaringType!.GetTypeInfo().GenericTypeParameters.ToList().IndexOf(typeInfo.AsType()); + if (genericTypeIndex == -1) throw new InvalidOperationException("Unexpected type: " + typeInfo); - return stringBuilder.ToString(); + stringBuilder.Append("`" + genericTypeIndex); } - private static string GetXmlDocMemberPart(MemberInfo memberInfo) => - GetXmlDocTypePart(memberInfo.DeclaringType.GetTypeInfo()) + - "." + - memberInfo.Name.Replace('.', '#'); + if (stringBuilder.Length == 0) + throw new InvalidOperationException("Unexpected type: " + typeInfo); - private static string GetXmlDocParameters(ParameterInfo[] parameters) - { - if (parameters.Length == 0) - return ""; + return stringBuilder.ToString(); + } - return "(" + - string.Join(",", parameters.Select(x => GetXmlDocTypePart(x.ParameterType.GetTypeInfo()))) + - ")"; - } + private static string GetXmlDocMemberPart(MemberInfo memberInfo) => + GetXmlDocTypePart(memberInfo.DeclaringType!.GetTypeInfo()) + + "." + + memberInfo.Name.Replace('.', '#'); + + private static string GetXmlDocParameters(ParameterInfo[] parameters) + { + if (parameters.Length == 0) + return ""; + + return "(" + + string.Join(",", parameters.Select(x => GetXmlDocTypePart(x.ParameterType.GetTypeInfo()))) + + ")"; } + + [GeneratedRegex(@"^[A-Z]:([^\.]+\.)*(?'name'[^\.\(\{`]+)", RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant)] + private static partial Regex XmlDocNameRegex(); } diff --git a/src/XmlDocMarkdown.Core/XmlDocVisibilityLevel.cs b/src/XmlDocMarkdown.Core/XmlDocVisibilityLevel.cs index 90472ab..0e3e8bc 100644 --- a/src/XmlDocMarkdown.Core/XmlDocVisibilityLevel.cs +++ b/src/XmlDocMarkdown.Core/XmlDocVisibilityLevel.cs @@ -1,33 +1,32 @@ -namespace XmlDocMarkdown.Core +namespace XmlDocMarkdown.Core; + +/// +/// The minimum visibility for documented types and members. +/// +public enum XmlDocVisibilityLevel { /// - /// The minimum visibility for documented types and members. + /// All types and members are documented. /// - public enum XmlDocVisibilityLevel - { - /// - /// All types and members are documented. - /// - Private, + Private, - /// - /// Only public, protected, and internal types and members are documented. - /// - Internal, + /// + /// Only public, protected, and internal types and members are documented. + /// + Internal, - /// - /// Only public and protected types and members are documented. - /// - Protected, + /// + /// Only public and protected types and members are documented. + /// + Protected, - /// - /// Reserved for internal use. - /// - ProtectedInternal, + /// + /// Reserved for internal use. + /// + ProtectedInternal, - /// - /// Only public types and members are documented. - /// - Public, - } + /// + /// Only public types and members are documented. + /// + Public, } diff --git a/src/XmlDocMarkdown/Program.cs b/src/XmlDocMarkdown/Program.cs deleted file mode 100644 index 893a7df..0000000 --- a/src/XmlDocMarkdown/Program.cs +++ /dev/null @@ -1,3 +0,0 @@ -using XmlDocMarkdown.Core; - -return XmlDocMarkdownApp.Run(args); diff --git a/src/XmlDocMarkdown/XmlDocMarkdown.csproj b/src/XmlDocMarkdown/XmlDocMarkdown.csproj deleted file mode 100644 index 8f9200f..0000000 --- a/src/XmlDocMarkdown/XmlDocMarkdown.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - Exe - net472 - $(MONO_ROOT)/lib/mono/4.7.2-api/ - A console app that generates Markdown from .NET XML documentation comments. - .NET XML documentation comments Markdown - true - true - False - true - README.md - - - - - - - - - - - - - tools/ - true - - - - diff --git a/src/xmldocmd/Program.cs b/src/xmldocmd/Program.cs deleted file mode 100644 index 893a7df..0000000 --- a/src/xmldocmd/Program.cs +++ /dev/null @@ -1,3 +0,0 @@ -using XmlDocMarkdown.Core; - -return XmlDocMarkdownApp.Run(args); diff --git a/src/xmldocmd/xmldocmd.csproj b/src/xmldocmd/xmldocmd.csproj deleted file mode 100644 index da9af86..0000000 --- a/src/xmldocmd/xmldocmd.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - Exe - net6.0;net7.0;net8.0 - A .NET tool that generates Markdown from .NET XML documentation comments. - .NET XML documentation comments Markdown - true - true - xmldocmd - XmlDocMarkdown - Major - README.md - - - - - - - - - - - diff --git a/tests/XmlDocMarkdown.Tests/MarkdownGeneratorTests.cs b/tests/XmlDocMarkdown.Tests/MarkdownGeneratorTests.cs index ee384d5..42840f1 100644 --- a/tests/XmlDocMarkdown.Tests/MarkdownGeneratorTests.cs +++ b/tests/XmlDocMarkdown.Tests/MarkdownGeneratorTests.cs @@ -3,17 +3,16 @@ using NUnit.Framework; using XmlDocMarkdown.Core; -namespace XmlDocMarkdown.Tests +namespace XmlDocMarkdown.Tests; + +public class MarkdownGeneratorTests { - public class MarkdownGeneratorTests + [Test] + public void ExampleAssembly() { - [Test] - public void ExampleAssembly() - { - XmlDocMarkdownGenerator.Generate( - typeof(ExampleClass).GetTypeInfo().Assembly.Location, - Path.Combine(Path.GetTempPath(), "MarkdownGeneratorTests"), - new XmlDocMarkdownSettings { IsDryRun = true }); - } + XmlDocMarkdownGenerator.Generate( + typeof(ExampleClass).GetTypeInfo().Assembly, + Path.Combine(Path.GetTempPath(), "MarkdownGeneratorTests"), + new XmlDocMarkdownSettings { IsDryRun = true }); } } diff --git a/tests/XmlDocMarkdown.Tests/XmlDocMarkdown.Tests.csproj b/tests/XmlDocMarkdown.Tests/XmlDocMarkdown.Tests.csproj index b993669..c3ac793 100644 --- a/tests/XmlDocMarkdown.Tests/XmlDocMarkdown.Tests.csproj +++ b/tests/XmlDocMarkdown.Tests/XmlDocMarkdown.Tests.csproj @@ -5,7 +5,6 @@ - diff --git a/tools/Build/Build.cs b/tools/Build/Build.cs index dcc2f5f..3de35bf 100644 --- a/tools/Build/Build.cs +++ b/tools/Build/Build.cs @@ -30,13 +30,8 @@ void GenerateDocs(bool verify) { var configuration = dotNetBuildSettings.GetConfiguration(); - var projects = new[] - { - ("XmlDocMarkdown.Core", "../src/XmlDocMarkdown.Core"), - ("ExampleAssembly", "../tests/ExampleAssembly"), - }; var xmlDocGenPath = FindFiles($"tools/XmlDocGen/bin/{configuration}/net8.0/XmlDocGen.dll").First(); - foreach (var (assembly, sourcePath) in projects) - RunDotNet(xmlDocGenPath, assembly, "docs", verify ? "--verify" : null, "--source", sourcePath, "--newline", "lf", "--clean"); + RunDotNet(xmlDocGenPath, "ExampleAssembly", "docs", verify ? "--verify" : null); + RunDotNet(xmlDocGenPath, "XmlDocMarkdown.Core", "docs", verify ? "--verify" : null); } }); diff --git a/tools/ExampleAssembly/ExampleAbstractClass.cs b/tools/ExampleAssembly/ExampleAbstractClass.cs index 65b116e..66f8c85 100644 --- a/tools/ExampleAssembly/ExampleAbstractClass.cs +++ b/tools/ExampleAssembly/ExampleAbstractClass.cs @@ -1,48 +1,47 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// An abstract class. +/// +public abstract class ExampleAbstractClass { /// - /// An abstract class. - /// - public abstract class ExampleAbstractClass - { - /// - /// A public abstract read-only property. - /// - public abstract int PublicProperty { get; } - - /// - /// A public and protected virtual property. - /// - public virtual int PublicProtectedProperty { get; protected set; } - - /// - /// A protected abstract property. - /// - protected abstract int ProtectedPropertyCore { get; set; } - - /// - /// A protected and private virtual property. - /// - protected virtual int ProtectedPrivateProperty { private get; set; } - - /// - /// A public abstract method. - /// - public abstract void PublicMethod(); - - /// - /// A protected abstract method. - /// - protected abstract void ProtectedMethodCore(); - - /// - /// An internal abstract method. - /// - internal abstract void InternalMethodCore(); - - /// - /// A protected internal abstract method. - /// - protected internal abstract void ProtectedInternalMethodCore(); - } + /// A public abstract read-only property. + /// + public abstract int PublicProperty { get; } + + /// + /// A public and protected virtual property. + /// + public virtual int PublicProtectedProperty { get; protected set; } + + /// + /// A protected abstract property. + /// + protected abstract int ProtectedPropertyCore { get; set; } + + /// + /// A protected and private virtual property. + /// + protected virtual int ProtectedPrivateProperty { private get; set; } + + /// + /// A public abstract method. + /// + public abstract void PublicMethod(); + + /// + /// A protected abstract method. + /// + protected abstract void ProtectedMethodCore(); + + /// + /// An internal abstract method. + /// + internal abstract void InternalMethodCore(); + + /// + /// A protected internal abstract method. + /// + protected internal abstract void ProtectedInternalMethodCore(); } diff --git a/tools/ExampleAssembly/ExampleAssembly.csproj b/tools/ExampleAssembly/ExampleAssembly.csproj index a4b6e9e..3a9c4fc 100644 --- a/tools/ExampleAssembly/ExampleAssembly.csproj +++ b/tools/ExampleAssembly/ExampleAssembly.csproj @@ -1,7 +1,7 @@ - netstandard2.1 + net8.0 true $(NoWarn);CA1024;CA1711;CA1852;CA2217 diff --git a/tools/ExampleAssembly/ExampleAttribute.cs b/tools/ExampleAssembly/ExampleAttribute.cs index 3b070d9..3162910 100644 --- a/tools/ExampleAssembly/ExampleAttribute.cs +++ b/tools/ExampleAssembly/ExampleAttribute.cs @@ -1,10 +1,9 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// An attribute. +/// +[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = true, Inherited = false)] +public sealed class ExampleAttribute : Attribute { - /// - /// An attribute. - /// - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = true, Inherited = false)] - public sealed class ExampleAttribute : Attribute - { - } } diff --git a/tools/ExampleAssembly/ExampleClass.cs b/tools/ExampleAssembly/ExampleClass.cs index 744a2c5..b82b385 100644 --- a/tools/ExampleAssembly/ExampleClass.cs +++ b/tools/ExampleAssembly/ExampleClass.cs @@ -3,332 +3,331 @@ #pragma warning disable 67 -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// A class. +/// +public class ExampleClass : IExampleContravariantInterface, IExampleCovariantInterface { /// - /// A class. + /// A no-arg constructor. + /// + public ExampleClass() + { + } + + /// + /// A one-arg constructor. + /// + /// The ID. + /// These remarks reference parameter + /// at the end of a line.These remarks reference parameter + /// at the start of a line. + public ExampleClass(string id) + { + } + + /// + /// A static lifetime field. + /// + public static readonly ExampleClass Default = new ExampleClass(); + + /// + /// A static lifetime property. + /// + public static ExampleClass Instance { get; } = new ExampleClass(); + + /// + /// A static lifetime method. + /// + public static ExampleClass Create() + { + throw new NotImplementedException(); + } + + /// + /// Another static lifetime method. + /// + public static ExampleClass Create(string id) + { + throw new NotImplementedException(); + } + + /// + /// A read-only property. + /// + /// The ID. + public string Id { get; } = BlankId; + + /// + /// A read-write property with a much-longer than expected summary to see + /// if there is any word wrapping in the member name. + /// + public double Weight { get; set; } + + /// + /// A static read-only field. + /// + public static readonly double DefaultWeight = 1.0; + + /// + /// A static field. + /// + public static int GlobalVariable; + + /// + /// A constant field. + /// + public const string BlankId = ""; + + /// + /// A static read-only property. + /// + public static double MinWeight { get; } = 0.0; + + /// + /// A static read-write property. + /// + public static double MaxWeight { get; set; } + + /// + /// An event. + /// + public event EventHandler? WeightChanged; + + /// + /// A static event. + /// + public static event EventHandler? MaxWeightChanged; + + /// + /// A virtual method. + /// + public virtual void Jump() + { + } + + /// + /// A boring static method. It has a really long summary because things get interesting when + /// table cells have to wrap. Also, we should probably cut the summary off at some point, since + /// some documenters tend to put way more in the summary than would generally be expected. + /// + public static void JumpAll() + { + } + + /// + /// A public field. + /// + public bool IsBadIdea; + + /// + /// An overloaded method. + /// + /// The type parameter. + /// The parameter. + /// We can put special characters in <c> elements, + /// like | and ` and &#x21;. + public T Overloaded(string x) + { + return default!; + } + + /// + /// An overloaded method. + /// + /// The type parameter. + /// The second type parameter. + /// The parameter. + /// The second parameter. + public void Overloaded(T x, U y) + where T : class + where U : struct + { + } + + /// + /// An overloaded method. + /// + /// The type parameter. + /// The parameter. + public void Overloaded(T x) + { + } + + /// + /// An overloaded method. + /// + /// The type parameter. + public T Overloaded() + { + return default!; + } + + /// + /// An overloaded method. + /// + /// The parameter. + public void Overloaded(string x) + { + } + + /// + /// An overloaded method. + /// + public void Overloaded() + { + } + + /// + /// An overloaded method. + /// + /// The parameter. + public void Overloaded(int x) + { + } + + /// + /// A method with default parameters. + /// + public void DefaultParameters(bool @bool = true, bool? no = false, bool? maybe = null, + byte @byte = byte.MaxValue, sbyte @sbyte = sbyte.MaxValue, + char @char = '\u1234', decimal @decimal = 3.14m, double @double = double.NaN, float @float = float.NegativeInfinity, + int @int = -42, uint @uint = 42, long @long = long.MinValue, ulong @ulong = long.MaxValue, + object? @object = null, short @short = short.MinValue, ushort @ushort = ushort.MaxValue, + string @string = "hi\0'\"\\\a\b\f\n\r\t\v\u0001\uABCD", T? t = default, + DateTime @virtual = default(DateTime), + ExampleEnum @enum = ExampleEnum.One, + ExampleFlagsEnum flags = ExampleFlagsEnum.Second | ExampleFlagsEnum.Third) + { + } + + /// + /// A method with a really long name. + /// + public void LongMethodNameWithTemplateParametersAndMethodParameters(T t) + { + } + + /// + /// A method whose summary references . + /// + /// The value + public void ParameterReference(int value) + { + } + + /// + /// A method whose summary references . + /// + /// The value of type . + public void TypeParameterReference(T value) + { + } + + /// + /// A method that tries to get a value. + /// + /// The value. + public bool TryGetValue(out object? value) + { + value = default; + return false; + } + + /// + /// A method that tries to get a value. + /// + /// The value of type . + public bool TryGetValue(out T value) + { + value = default!; + return false; + } + + /// + /// A method that edits a value. + /// + /// The value to edit. + public void EditValue(ref object value) + { + } + + /// + /// A method that edits a value. + /// + /// The value to edit of type . + public void EditValue(ref T value) + { + } + + /// + /// A method with parameters. + /// + /// The parameters. + public void HasParams(params string[] parameters) + { + } + + /// + /// A method that uses caller info. + /// + public void UsesCallerInfo([CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) + { + } + + /// + /// A method whose docs have hyperlinks. + /// + /// Visit for more info. + public void HasHyperlinks() + { + } + + /// + /// An obsolete method. + /// + [Obsolete("This method is old and busted.")] + public void OldAndBusted() + { + } + + /// + /// An unbrowsable method. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public void UnbrowsableMethod() + { + } + + /// + /// A method with tuples. + /// + /// The tuples. + public void Tuples(params (string Key, object? Value)[] tuples) + { + } + + /// + /// A method with a long tuple. + /// + public ((int A1, int A2) A, int B, int C, int, int, int D, int E, (int F1, int F2) F)? EightTuple() + { + return null; + } + + /// + /// A method with a nested tuple. + /// + public void NestedTuple(Dictionary<((int A, int B) C, IList<(int D, int E)> F), (int G, int H)> dictionary) + { + } + + /// + /// A method with nullable references. /// - public class ExampleClass : IExampleContravariantInterface, IExampleCovariantInterface + public void NullableReferences(string? a, (string B1, string? B2, string B3) b, Dictionary c, string[]? d, string?[] e, (T F1, T? F2) f) { - /// - /// A no-arg constructor. - /// - public ExampleClass() - { - } - - /// - /// A one-arg constructor. - /// - /// The ID. - /// These remarks reference parameter - /// at the end of a line.These remarks reference parameter - /// at the start of a line. - public ExampleClass(string id) - { - } - - /// - /// A static lifetime field. - /// - public static readonly ExampleClass Default = new ExampleClass(); - - /// - /// A static lifetime property. - /// - public static ExampleClass Instance { get; } = new ExampleClass(); - - /// - /// A static lifetime method. - /// - public static ExampleClass Create() - { - throw new NotImplementedException(); - } - - /// - /// Another static lifetime method. - /// - public static ExampleClass Create(string id) - { - throw new NotImplementedException(); - } - - /// - /// A read-only property. - /// - /// The ID. - public string Id { get; } = BlankId; - - /// - /// A read-write property with a much-longer than expected summary to see - /// if there is any word wrapping in the member name. - /// - public double Weight { get; set; } - - /// - /// A static read-only field. - /// - public static readonly double DefaultWeight = 1.0; - - /// - /// A static field. - /// - public static int GlobalVariable; - - /// - /// A constant field. - /// - public const string BlankId = ""; - - /// - /// A static read-only property. - /// - public static double MinWeight { get; } = 0.0; - - /// - /// A static read-write property. - /// - public static double MaxWeight { get; set; } - - /// - /// An event. - /// - public event EventHandler? WeightChanged; - - /// - /// A static event. - /// - public static event EventHandler? MaxWeightChanged; - - /// - /// A virtual method. - /// - public virtual void Jump() - { - } - - /// - /// A boring static method. It has a really long summary because things get interesting when - /// table cells have to wrap. Also, we should probably cut the summary off at some point, since - /// some documenters tend to put way more in the summary than would generally be expected. - /// - public static void JumpAll() - { - } - - /// - /// A public field. - /// - public bool IsBadIdea; - - /// - /// An overloaded method. - /// - /// The type parameter. - /// The parameter. - /// We can put special characters in <c> elements, - /// like | and ` and &#x21;. - public T Overloaded(string x) - { - return default!; - } - - /// - /// An overloaded method. - /// - /// The type parameter. - /// The second type parameter. - /// The parameter. - /// The second parameter. - public void Overloaded(T x, U y) - where T : class - where U : struct - { - } - - /// - /// An overloaded method. - /// - /// The type parameter. - /// The parameter. - public void Overloaded(T x) - { - } - - /// - /// An overloaded method. - /// - /// The type parameter. - public T Overloaded() - { - return default!; - } - - /// - /// An overloaded method. - /// - /// The parameter. - public void Overloaded(string x) - { - } - - /// - /// An overloaded method. - /// - public void Overloaded() - { - } - - /// - /// An overloaded method. - /// - /// The parameter. - public void Overloaded(int x) - { - } - - /// - /// A method with default parameters. - /// - public void DefaultParameters(bool @bool = true, bool? no = false, bool? maybe = null, - byte @byte = byte.MaxValue, sbyte @sbyte = sbyte.MaxValue, - char @char = '\u1234', decimal @decimal = 3.14m, double @double = double.NaN, float @float = float.NegativeInfinity, - int @int = -42, uint @uint = 42, long @long = long.MinValue, ulong @ulong = long.MaxValue, - object? @object = null, short @short = short.MinValue, ushort @ushort = ushort.MaxValue, - string @string = "hi\0'\"\\\a\b\f\n\r\t\v\u0001\uABCD", T? t = default, - DateTime @virtual = default(DateTime), - ExampleEnum @enum = ExampleEnum.One, - ExampleFlagsEnum flags = ExampleFlagsEnum.Second | ExampleFlagsEnum.Third) - { - } - - /// - /// A method with a really long name. - /// - public void LongMethodNameWithTemplateParametersAndMethodParameters(T t) - { - } - - /// - /// A method whose summary references . - /// - /// The value - public void ParameterReference(int value) - { - } - - /// - /// A method whose summary references . - /// - /// The value of type . - public void TypeParameterReference(T value) - { - } - - /// - /// A method that tries to get a value. - /// - /// The value. - public bool TryGetValue(out object? value) - { - value = default; - return false; - } - - /// - /// A method that tries to get a value. - /// - /// The value of type . - public bool TryGetValue(out T value) - { - value = default!; - return false; - } - - /// - /// A method that edits a value. - /// - /// The value to edit. - public void EditValue(ref object value) - { - } - - /// - /// A method that edits a value. - /// - /// The value to edit of type . - public void EditValue(ref T value) - { - } - - /// - /// A method with parameters. - /// - /// The parameters. - public void HasParams(params string[] parameters) - { - } - - /// - /// A method that uses caller info. - /// - public void UsesCallerInfo([CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) - { - } - - /// - /// A method whose docs have hyperlinks. - /// - /// Visit for more info. - public void HasHyperlinks() - { - } - - /// - /// An obsolete method. - /// - [Obsolete("This method is old and busted.")] - public void OldAndBusted() - { - } - - /// - /// An unbrowsable method. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public void UnbrowsableMethod() - { - } - - /// - /// A method with tuples. - /// - /// The tuples. - public void Tuples(params (string Key, object? Value)[] tuples) - { - } - - /// - /// A method with a long tuple. - /// - public ((int A1, int A2) A, int B, int C, int, int, int D, int E, (int F1, int F2) F)? EightTuple() - { - return null; - } - - /// - /// A method with a nested tuple. - /// - public void NestedTuple(Dictionary<((int A, int B) C, IList<(int D, int E)> F), (int G, int H)> dictionary) - { - } - - /// - /// A method with nullable references. - /// - public void NullableReferences(string? a, (string B1, string? B2, string B3) b, Dictionary c, string[]? d, string?[] e, (T F1, T? F2) f) - { - } } } diff --git a/tools/ExampleAssembly/ExampleDeepClass.cs b/tools/ExampleAssembly/ExampleDeepClass.cs index 7c2cc52..73e7121 100644 --- a/tools/ExampleAssembly/ExampleDeepClass.cs +++ b/tools/ExampleAssembly/ExampleDeepClass.cs @@ -1,91 +1,90 @@ using ExampleAssembly.InnerNamespace; -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// A class with nested types. +/// +/// The class has a and a +/// with a and a . +/// Another type in this namespace is with method . A type +/// in an inner namespace is , which has a constructor . +public class ExampleDeepClass { /// - /// A class with nested types. + /// A nested delegate. /// /// The class has a and a /// with a and a . /// Another type in this namespace is with method . A type /// in an inner namespace is , which has a constructor . - public class ExampleDeepClass - { - /// - /// A nested delegate. - /// - /// The class has a and a - /// with a and a . - /// Another type in this namespace is with method . A type - /// in an inner namespace is , which has a constructor . - public delegate void NestedDelegate(); + public delegate void NestedDelegate(); + /// + /// A nested class. + /// + /// The class has a and a + /// with a and a . + /// Another type in this namespace is with method . A type + /// in an inner namespace is , which has a constructor . + public class NestedClass + { /// - /// A nested class. + /// A very nested structure. /// /// The class has a and a - /// with a and a . + /// with a and a . /// Another type in this namespace is with method . A type /// in an inner namespace is , which has a constructor . - public class NestedClass + public struct VeryNestedStruct { /// - /// A very nested structure. + /// A very nested property. /// /// The class has a and a /// with a and a . /// Another type in this namespace is with method . A type /// in an inner namespace is , which has a constructor . - public struct VeryNestedStruct - { - /// - /// A very nested property. - /// - /// The class has a and a - /// with a and a . - /// Another type in this namespace is with method . A type - /// in an inner namespace is , which has a constructor . - public bool IsDeep { get; set; } + public bool IsDeep { get; set; } - /// - /// A very, very nested interface. - /// - /// The class has a and a - /// with a and a . - /// Another type in this namespace is with method . A type - /// in an inner namespace is , which has a constructor . - public interface VeryVeryNestedInterface - { - } + /// + /// A very, very nested interface. + /// + /// The class has a and a + /// with a and a . + /// Another type in this namespace is with method . A type + /// in an inner namespace is , which has a constructor . + public interface VeryVeryNestedInterface + { } } + } - /// - /// A protected nested class. - /// - protected class ProtectedNestedClass - { - } + /// + /// A protected nested class. + /// + protected class ProtectedNestedClass + { + } - /// - /// An internal nested class. - /// - internal class InternalNestedClass - { - } + /// + /// An internal nested class. + /// + internal class InternalNestedClass + { + } - /// - /// A protected internal nested class. - /// - protected internal class ProtectedInternalNestedClass - { - } + /// + /// A protected internal nested class. + /// + protected internal class ProtectedInternalNestedClass + { + } - /// - /// A private nested class. - /// - private class PrivateNestedClass - { - } + /// + /// A private nested class. + /// + private class PrivateNestedClass + { } } diff --git a/tools/ExampleAssembly/ExampleDelegate.cs b/tools/ExampleAssembly/ExampleDelegate.cs index a14200b..eef0ee5 100644 --- a/tools/ExampleAssembly/ExampleDelegate.cs +++ b/tools/ExampleAssembly/ExampleDelegate.cs @@ -1,7 +1,6 @@ -namespace ExampleAssembly -{ - /// - /// A delegate. - /// - public delegate void ExampleDelegate(); -} +namespace ExampleAssembly; + +/// +/// A delegate. +/// +public delegate void ExampleDelegate(); diff --git a/tools/ExampleAssembly/ExampleDerivedClass.cs b/tools/ExampleAssembly/ExampleDerivedClass.cs index 377483c..bae4630 100644 --- a/tools/ExampleAssembly/ExampleDerivedClass.cs +++ b/tools/ExampleAssembly/ExampleDerivedClass.cs @@ -1,82 +1,81 @@ using System.Collections; using ExampleAssembly.InnerNamespace; -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// A class that derives from . +/// +public class ExampleDerivedClass : ExampleClass, + IExampleInterface, + IEnumerable, + IEnumerable, + IExampleInternalInterface, + IExampleContravariantInterface, + IExampleCovariantInterface { /// - /// A class that derives from . + /// A method with lots of see alsos. /// - public class ExampleDerivedClass : ExampleClass, - IExampleInterface, - IEnumerable, - IEnumerable, - IExampleInternalInterface, - IExampleContravariantInterface, - IExampleCovariantInterface + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void SeeAlso() { - /// - /// A method with lots of see alsos. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - public void SeeAlso() - { - } + } - /// - /// An implicitly implemented interface method. - /// - public int ExampleMethod(string value) - { - throw new NotImplementedException(); - } + /// + /// An implicitly implemented interface method. + /// + public int ExampleMethod(string value) + { + throw new NotImplementedException(); + } - /// - /// An overridden method. - /// - /// Some exception is thrown. - public override void Jump() - { - } + /// + /// An overridden method. + /// + /// Some exception is thrown. + public override void Jump() + { + } - /// - /// The enumerator. - /// - /// The strings. - /// An example error occurred. - /// It isn't implemented. - public IEnumerator GetEnumerator() - { - throw new NotImplementedException(); - } + /// + /// The enumerator. + /// + /// The strings. + /// An example error occurred. + /// It isn't implemented. + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } - /// - /// The enumerator for a less derived type. - /// - /// The strings. - /// An example error occurred. - /// It isn't implemented. - IEnumerator IEnumerable.GetEnumerator() - { - throw new NotImplementedException(); - } + /// + /// The enumerator for a less derived type. + /// + /// The strings. + /// An example error occurred. + /// It isn't implemented. + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } - /// - /// The old-school enumerator. - /// - /// The strings. - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + /// + /// The old-school enumerator. + /// + /// The strings. + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); } } diff --git a/tools/ExampleAssembly/ExampleEnum.cs b/tools/ExampleAssembly/ExampleEnum.cs index a63aeeb..5048ce0 100644 --- a/tools/ExampleAssembly/ExampleEnum.cs +++ b/tools/ExampleAssembly/ExampleEnum.cs @@ -1,33 +1,32 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// An enumeration. +/// +public enum ExampleEnum { /// - /// An enumeration. + /// Zero! /// - public enum ExampleEnum - { - /// - /// Zero! - /// - Zero, + Zero, - /// - /// One! - /// - One, + /// + /// One! + /// + One, - /// - /// Two! - /// - Two, + /// + /// Two! + /// + Two, - /// - /// Very negative! - /// - Min = int.MinValue, + /// + /// Very negative! + /// + Min = int.MinValue, - /// - /// Very positive! - /// - Max = int.MaxValue, - } + /// + /// Very positive! + /// + Max = int.MaxValue, } diff --git a/tools/ExampleAssembly/ExampleException.cs b/tools/ExampleAssembly/ExampleException.cs index 41c82ef..b490bb5 100644 --- a/tools/ExampleAssembly/ExampleException.cs +++ b/tools/ExampleAssembly/ExampleException.cs @@ -1,18 +1,17 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// An example exception. +/// +public class ExampleException : Exception { /// - /// An example exception. + /// Creates an instance. /// - public class ExampleException : Exception + /// The message. + /// The inner exception. + public ExampleException(string message, Exception? innerException = null) + : base(message, innerException) { - /// - /// Creates an instance. - /// - /// The message. - /// The inner exception. - public ExampleException(string message, Exception? innerException = null) - : base(message, innerException) - { - } } } diff --git a/tools/ExampleAssembly/ExampleFlagsEnum.cs b/tools/ExampleAssembly/ExampleFlagsEnum.cs index c692c3b..f56c9d5 100644 --- a/tools/ExampleAssembly/ExampleFlagsEnum.cs +++ b/tools/ExampleAssembly/ExampleFlagsEnum.cs @@ -1,44 +1,43 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// A flags enumeration. +/// +[Flags] +public enum ExampleFlagsEnum { /// - /// A flags enumeration. + /// No bits. /// - [Flags] - public enum ExampleFlagsEnum - { - /// - /// No bits. - /// - None = 0, + None = 0, - /// - /// First bit. - /// - First = 1, + /// + /// First bit. + /// + First = 1, - /// - /// Second bit. - /// - Second = 2, + /// + /// Second bit. + /// + Second = 2, - /// - /// Third bit. - /// - Third = 4, + /// + /// Third bit. + /// + Third = 4, - /// - /// Fourth bit. - /// - Fourth = 8, + /// + /// Fourth bit. + /// + Fourth = 8, - /// - /// All bits. - /// - All = First | Second | Third | Fourth, + /// + /// All bits. + /// + All = First | Second | Third | Fourth, - /// - /// Insanity. - /// - Insanity = -1, - } + /// + /// Insanity. + /// + Insanity = -1, } diff --git a/tools/ExampleAssembly/ExampleGenericClass.cs b/tools/ExampleAssembly/ExampleGenericClass.cs index 81a0856..20999f7 100644 --- a/tools/ExampleAssembly/ExampleGenericClass.cs +++ b/tools/ExampleAssembly/ExampleGenericClass.cs @@ -1,45 +1,44 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// A generic class. +/// +/// The generic type. +public class ExampleGenericClass { /// - /// A generic class. + /// Creates an instance. /// - /// The generic type. - public class ExampleGenericClass + public ExampleGenericClass(T value) { - /// - /// Creates an instance. - /// - public ExampleGenericClass(T value) - { - } + } - /// - /// The value. - /// - public IEnumerable? Value { get; set; } + /// + /// The value. + /// + public IEnumerable? Value { get; set; } - /// - /// Gets an example tuple. - /// - public ExampleTuple GetTuple() - { - throw new NotImplementedException(); - } + /// + /// Gets an example tuple. + /// + public ExampleTuple GetTuple() + { + throw new NotImplementedException(); + } - /// - /// Adds values. - /// - public void AddValues(IEnumerable values) - { - throw new NotImplementedException(); - } + /// + /// Adds values. + /// + public void AddValues(IEnumerable values) + { + throw new NotImplementedException(); + } - /// - /// Adds values. - /// - public T AddTuples(IEnumerable<(T Key, object? Value)> values) - { - throw new NotImplementedException(); - } + /// + /// Adds values. + /// + public T AddTuples(IEnumerable<(T Key, object? Value)> values) + { + throw new NotImplementedException(); } } diff --git a/tools/ExampleAssembly/ExampleGenericDelegate.cs b/tools/ExampleAssembly/ExampleGenericDelegate.cs index 299f9a8..5a1f019 100644 --- a/tools/ExampleAssembly/ExampleGenericDelegate.cs +++ b/tools/ExampleAssembly/ExampleGenericDelegate.cs @@ -1,15 +1,14 @@ -namespace ExampleAssembly -{ - /// - /// A generic delegate. - /// - /// The first generic type. - /// The second generic type. - /// The result type. - /// The first argument. - /// The second argument. - /// The result. - public delegate TResult ExampleGenericDelegate(T1 arg1, T2 arg2) - where T1 : class, new() - where T2 : struct; -} +namespace ExampleAssembly; + +/// +/// A generic delegate. +/// +/// The first generic type. +/// The second generic type. +/// The result type. +/// The first argument. +/// The second argument. +/// The result. +public delegate TResult ExampleGenericDelegate(T1 arg1, T2 arg2) + where T1 : class, new() + where T2 : struct; diff --git a/tools/ExampleAssembly/ExampleInternalClass.cs b/tools/ExampleAssembly/ExampleInternalClass.cs index 1db5311..31faaf5 100644 --- a/tools/ExampleAssembly/ExampleInternalClass.cs +++ b/tools/ExampleAssembly/ExampleInternalClass.cs @@ -1,15 +1,14 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// An internal class. +/// +internal class ExampleInternalClass { /// - /// An internal class. + /// A public class nested in an internal class. /// - internal class ExampleInternalClass + public class ExamplePublicNestedClass { - /// - /// A public class nested in an internal class. - /// - public class ExamplePublicNestedClass - { - } } } diff --git a/tools/ExampleAssembly/ExampleLongEnum.cs b/tools/ExampleAssembly/ExampleLongEnum.cs index c10869e..7bc7ca0 100644 --- a/tools/ExampleAssembly/ExampleLongEnum.cs +++ b/tools/ExampleAssembly/ExampleLongEnum.cs @@ -1,18 +1,17 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// A 64-bit enumeration. +/// +public enum ExampleLongEnum : ulong { /// - /// A 64-bit enumeration. + /// Backward. /// - public enum ExampleLongEnum : ulong - { - /// - /// Backward. - /// - Backward = ulong.MinValue, + Backward = ulong.MinValue, - /// - /// Forward. - /// - Forward = ulong.MaxValue, - } + /// + /// Forward. + /// + Forward = ulong.MaxValue, } diff --git a/tools/ExampleAssembly/ExampleLongSummary.cs b/tools/ExampleAssembly/ExampleLongSummary.cs index 684ff42..6c1f96f 100644 --- a/tools/ExampleAssembly/ExampleLongSummary.cs +++ b/tools/ExampleAssembly/ExampleLongSummary.cs @@ -1,58 +1,57 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// This class has an exceptionally long summary. Even the first paragraph is really long, which is important +/// to test because some documenters put a lot of information in the summary, perhaps not expecting that +/// someone will ever actually generate documentation. +/// With multiple paragraphs. +/// And text weirdly between paragraphs. +/// +/// var someCode = new SomeCode(); +/// someCode.Print(); +/// +/// +/// +/// Welcome to the remarks that use lists. First a bullet list. +/// +/// implicit description +/// explicit description +/// termdescription +/// term only +/// +/// And now a number list. +/// +/// implicit description +/// explicit description +/// termdescription +/// term only +/// +/// Followed by a table list. +/// +/// +/// header 1 +/// header 2 +/// header 3 +/// +/// +/// 1-1 +/// 1-2 +/// 1-3 +/// +/// +/// 2-1 +/// 2-2 +/// 2-3 +/// +/// +/// And now a definition/unknown list. +/// +/// implicit description +/// explicit description +/// termdescription +/// term only +/// +/// +public class ExampleLongSummary { - /// - /// This class has an exceptionally long summary. Even the first paragraph is really long, which is important - /// to test because some documenters put a lot of information in the summary, perhaps not expecting that - /// someone will ever actually generate documentation. - /// With multiple paragraphs. - /// And text weirdly between paragraphs. - /// - /// var someCode = new SomeCode(); - /// someCode.Print(); - /// - /// - /// - /// Welcome to the remarks that use lists. First a bullet list. - /// - /// implicit description - /// explicit description - /// termdescription - /// term only - /// - /// And now a number list. - /// - /// implicit description - /// explicit description - /// termdescription - /// term only - /// - /// Followed by a table list. - /// - /// - /// header 1 - /// header 2 - /// header 3 - /// - /// - /// 1-1 - /// 1-2 - /// 1-3 - /// - /// - /// 2-1 - /// 2-2 - /// 2-3 - /// - /// - /// And now a definition/unknown list. - /// - /// implicit description - /// explicit description - /// termdescription - /// term only - /// - /// - public class ExampleLongSummary - { - } } diff --git a/tools/ExampleAssembly/ExampleObsoleteClass.cs b/tools/ExampleAssembly/ExampleObsoleteClass.cs index 3ae4527..a617c68 100644 --- a/tools/ExampleAssembly/ExampleObsoleteClass.cs +++ b/tools/ExampleAssembly/ExampleObsoleteClass.cs @@ -1,10 +1,9 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// An obsolete class. +/// +[Obsolete] +public class ExampleObsoleteClass { - /// - /// An obsolete class. - /// - [Obsolete] - public class ExampleObsoleteClass - { - } } diff --git a/tools/ExampleAssembly/ExampleRecord.cs b/tools/ExampleAssembly/ExampleRecord.cs index 828067a..901691b 100644 --- a/tools/ExampleAssembly/ExampleRecord.cs +++ b/tools/ExampleAssembly/ExampleRecord.cs @@ -1,13 +1,12 @@ -namespace ExampleAssembly -{ - /// - /// A C# 9 generic record. - /// - /// A string. - /// An integer. - /// A hash set. - /// A generic type parameter. - /// An lambda parameter. - /// Some generic type. - public record ExampleRecord(string Name, int Age, HashSet Days, T GenericType, Action GenericLambda); -} +namespace ExampleAssembly; + +/// +/// A C# 9 generic record. +/// +/// A string. +/// An integer. +/// A hash set. +/// A generic type parameter. +/// An lambda parameter. +/// Some generic type. +public record ExampleRecord(string Name, int Age, HashSet Days, T GenericType, Action GenericLambda); diff --git a/tools/ExampleAssembly/ExampleRefOutDelegate.cs b/tools/ExampleAssembly/ExampleRefOutDelegate.cs index 4153bbb..270c3be 100644 --- a/tools/ExampleAssembly/ExampleRefOutDelegate.cs +++ b/tools/ExampleAssembly/ExampleRefOutDelegate.cs @@ -1,7 +1,6 @@ -namespace ExampleAssembly -{ - /// - /// A delegate. - /// - public delegate void ExampleRefOutDelegate(ref bool isRef, out bool isOut, bool isIn = true); -} +namespace ExampleAssembly; + +/// +/// A delegate. +/// +public delegate void ExampleRefOutDelegate(ref bool isRef, out bool isOut, bool isIn = true); diff --git a/tools/ExampleAssembly/ExampleSealedClass.cs b/tools/ExampleAssembly/ExampleSealedClass.cs index d68aa7a..f9c9247 100644 --- a/tools/ExampleAssembly/ExampleSealedClass.cs +++ b/tools/ExampleAssembly/ExampleSealedClass.cs @@ -1,9 +1,8 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// A sealed class. +/// +public sealed class ExampleSealedClass { - /// - /// A sealed class. - /// - public sealed class ExampleSealedClass - { - } } diff --git a/tools/ExampleAssembly/ExampleSealedRecord.cs b/tools/ExampleAssembly/ExampleSealedRecord.cs index 7e90a50..969c9b3 100644 --- a/tools/ExampleAssembly/ExampleSealedRecord.cs +++ b/tools/ExampleAssembly/ExampleSealedRecord.cs @@ -1,19 +1,18 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// A sealed C# 9 record. +/// +public sealed record ExampleSealedRecord { /// - /// A sealed C# 9 record. + /// Constructs an instance. /// - public sealed record ExampleSealedRecord - { - /// - /// Constructs an instance. - /// - /// The value. - public ExampleSealedRecord(string value) => Value = value ?? throw new ArgumentNullException(nameof(value)); + /// The value. + public ExampleSealedRecord(string value) => Value = value ?? throw new ArgumentNullException(nameof(value)); - /// - /// The value. - /// - public string Value { get; } - } + /// + /// The value. + /// + public string Value { get; } } diff --git a/tools/ExampleAssembly/ExampleStaticClass.cs b/tools/ExampleAssembly/ExampleStaticClass.cs index ee61b93..4e2d3ab 100644 --- a/tools/ExampleAssembly/ExampleStaticClass.cs +++ b/tools/ExampleAssembly/ExampleStaticClass.cs @@ -1,25 +1,24 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// A static class. +/// +public static class ExampleStaticClass { /// - /// A static class. + /// Gets the next enumerated value. /// - public static class ExampleStaticClass + public static ExampleEnum GetNext(this ExampleEnum value) { - /// - /// Gets the next enumerated value. - /// - public static ExampleEnum GetNext(this ExampleEnum value) - { - throw new NotImplementedException(); - } - - /// - /// Clones the specified array. - /// - /// The array to clone. - /// A clone of the specified array. - /// This method is merely useful in avoiding the cast that is otherwise necessary - /// when calling . - public static T[] Clone(T[] array) => (T[]) array.Clone(); + throw new NotImplementedException(); } + + /// + /// Clones the specified array. + /// + /// The array to clone. + /// A clone of the specified array. + /// This method is merely useful in avoiding the cast that is otherwise necessary + /// when calling . + public static T[] Clone(T[] array) => (T[]) array.Clone(); } diff --git a/tools/ExampleAssembly/ExampleStruct.cs b/tools/ExampleAssembly/ExampleStruct.cs index e0629ef..12176a7 100644 --- a/tools/ExampleAssembly/ExampleStruct.cs +++ b/tools/ExampleAssembly/ExampleStruct.cs @@ -1,264 +1,263 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// A structure. +/// +public struct ExampleStruct { /// - /// A structure. - /// - public struct ExampleStruct - { - /// - /// Unary plus. - /// - public static ExampleStruct operator +(ExampleStruct value) - { - throw new NotImplementedException(); - } - - /// - /// Unary minus. - /// - public static ExampleStruct operator -(ExampleStruct value) - { - throw new NotImplementedException(); - } - - /// - /// Not. - /// - public static ExampleStruct operator !(ExampleStruct value) - { - throw new NotImplementedException(); - } - - /// - /// Bitwise complement. - /// - public static ExampleStruct operator ~(ExampleStruct value) - { - throw new NotImplementedException(); - } - - /// - /// Increment. - /// - public static ExampleStruct operator ++(ExampleStruct value) - { - throw new NotImplementedException(); - } - - /// - /// Decrement. - /// - public static ExampleStruct operator --(ExampleStruct value) - { - throw new NotImplementedException(); - } - - /// - /// True. - /// - public static bool operator true(ExampleStruct value) - { - throw new NotImplementedException(); - } - - /// - /// False. - /// - public static bool operator false(ExampleStruct value) - { - throw new NotImplementedException(); - } - - /// - /// Add. - /// - public static ExampleStruct operator +(ExampleStruct value1, ExampleStruct value2) - { - throw new NotImplementedException(); - } - - /// - /// Subtract. - /// - public static ExampleStruct operator -(ExampleStruct value1, ExampleStruct value2) - { - throw new NotImplementedException(); - } - - /// - /// Multiply. - /// - public static ExampleStruct operator *(ExampleStruct value1, ExampleStruct value2) - { - throw new NotImplementedException(); - } - - /// - /// Divide. - /// - public static ExampleStruct operator /(ExampleStruct value1, ExampleStruct value2) - { - throw new NotImplementedException(); - } - - /// - /// Modulus. - /// - public static ExampleStruct operator %(ExampleStruct value1, ExampleStruct value2) - { - throw new NotImplementedException(); - } - - /// - /// And. - /// - public static ExampleStruct operator &(ExampleStruct value1, ExampleStruct value2) - { - throw new NotImplementedException(); - } - - /// - /// Or. - /// - public static ExampleStruct operator |(ExampleStruct value1, ExampleStruct value2) - { - throw new NotImplementedException(); - } - - /// - /// Exclusive or. - /// - public static ExampleStruct operator ^(ExampleStruct value1, ExampleStruct value2) - { - throw new NotImplementedException(); - } - - /// - /// Left shift. - /// - public static ExampleStruct operator <<(ExampleStruct value1, int value2) - { - throw new NotImplementedException(); - } - - /// - /// Right shift. - /// - public static ExampleStruct operator >>(ExampleStruct value1, int value2) - { - throw new NotImplementedException(); - } - - /// - /// Equal. - /// - public static bool operator ==(ExampleStruct value1, ExampleStruct value2) - { - throw new NotImplementedException(); - } - - /// - /// Not equal. - /// - public static bool operator !=(ExampleStruct value1, ExampleStruct value2) - { - return !(value1 == value2); - } - - /// - /// Less than. - /// - public static bool operator <(ExampleStruct value1, ExampleStruct value2) - { - throw new NotImplementedException(); - } - - /// - /// Greater than. - /// - public static bool operator >(ExampleStruct value1, ExampleStruct value2) - { - throw new NotImplementedException(); - } - - /// - /// Less than or equal to. - /// - public static bool operator <=(ExampleStruct value1, ExampleStruct value2) - { - throw new NotImplementedException(); - } - - /// - /// Greater than or equal to. - /// - public static bool operator >=(ExampleStruct value1, ExampleStruct value2) - { - throw new NotImplementedException(); - } - - /// - /// Integer indexer. - /// - public ExampleStruct this[int index] - { - get { throw new NotImplementedException(); } - } - - /// - /// String indexer. - /// - public ExampleStruct this[string index] - { - get { throw new NotImplementedException(); } - } - - /// - /// Explicit cast from integer. - /// - public static explicit operator ExampleStruct(int value) - { - throw new NotImplementedException(); - } - - /// - /// Explicit cast to integer. - /// - public static explicit operator int(ExampleStruct value) - { - throw new NotImplementedException(); - } - - /// - /// Implicit cast from string. - /// - public static implicit operator ExampleStruct(string value) - { - throw new NotImplementedException(); - } - - /// - /// Implicit cast to string. - /// - public static implicit operator string(ExampleStruct value) - { - throw new NotImplementedException(); - } - - /// - /// Check for equality. - /// - public override bool Equals(object? obj) - { - throw new NotImplementedException(); - } - - /// - /// Check for equality. - /// - public override int GetHashCode() - { - throw new NotImplementedException(); - } + /// Unary plus. + /// + public static ExampleStruct operator +(ExampleStruct value) + { + throw new NotImplementedException(); + } + + /// + /// Unary minus. + /// + public static ExampleStruct operator -(ExampleStruct value) + { + throw new NotImplementedException(); + } + + /// + /// Not. + /// + public static ExampleStruct operator !(ExampleStruct value) + { + throw new NotImplementedException(); + } + + /// + /// Bitwise complement. + /// + public static ExampleStruct operator ~(ExampleStruct value) + { + throw new NotImplementedException(); + } + + /// + /// Increment. + /// + public static ExampleStruct operator ++(ExampleStruct value) + { + throw new NotImplementedException(); + } + + /// + /// Decrement. + /// + public static ExampleStruct operator --(ExampleStruct value) + { + throw new NotImplementedException(); + } + + /// + /// True. + /// + public static bool operator true(ExampleStruct value) + { + throw new NotImplementedException(); + } + + /// + /// False. + /// + public static bool operator false(ExampleStruct value) + { + throw new NotImplementedException(); + } + + /// + /// Add. + /// + public static ExampleStruct operator +(ExampleStruct value1, ExampleStruct value2) + { + throw new NotImplementedException(); + } + + /// + /// Subtract. + /// + public static ExampleStruct operator -(ExampleStruct value1, ExampleStruct value2) + { + throw new NotImplementedException(); + } + + /// + /// Multiply. + /// + public static ExampleStruct operator *(ExampleStruct value1, ExampleStruct value2) + { + throw new NotImplementedException(); + } + + /// + /// Divide. + /// + public static ExampleStruct operator /(ExampleStruct value1, ExampleStruct value2) + { + throw new NotImplementedException(); + } + + /// + /// Modulus. + /// + public static ExampleStruct operator %(ExampleStruct value1, ExampleStruct value2) + { + throw new NotImplementedException(); + } + + /// + /// And. + /// + public static ExampleStruct operator &(ExampleStruct value1, ExampleStruct value2) + { + throw new NotImplementedException(); + } + + /// + /// Or. + /// + public static ExampleStruct operator |(ExampleStruct value1, ExampleStruct value2) + { + throw new NotImplementedException(); + } + + /// + /// Exclusive or. + /// + public static ExampleStruct operator ^(ExampleStruct value1, ExampleStruct value2) + { + throw new NotImplementedException(); + } + + /// + /// Left shift. + /// + public static ExampleStruct operator <<(ExampleStruct value1, int value2) + { + throw new NotImplementedException(); + } + + /// + /// Right shift. + /// + public static ExampleStruct operator >>(ExampleStruct value1, int value2) + { + throw new NotImplementedException(); + } + + /// + /// Equal. + /// + public static bool operator ==(ExampleStruct value1, ExampleStruct value2) + { + throw new NotImplementedException(); + } + + /// + /// Not equal. + /// + public static bool operator !=(ExampleStruct value1, ExampleStruct value2) + { + return !(value1 == value2); + } + + /// + /// Less than. + /// + public static bool operator <(ExampleStruct value1, ExampleStruct value2) + { + throw new NotImplementedException(); + } + + /// + /// Greater than. + /// + public static bool operator >(ExampleStruct value1, ExampleStruct value2) + { + throw new NotImplementedException(); + } + + /// + /// Less than or equal to. + /// + public static bool operator <=(ExampleStruct value1, ExampleStruct value2) + { + throw new NotImplementedException(); + } + + /// + /// Greater than or equal to. + /// + public static bool operator >=(ExampleStruct value1, ExampleStruct value2) + { + throw new NotImplementedException(); + } + + /// + /// Integer indexer. + /// + public ExampleStruct this[int index] + { + get { throw new NotImplementedException(); } + } + + /// + /// String indexer. + /// + public ExampleStruct this[string index] + { + get { throw new NotImplementedException(); } + } + + /// + /// Explicit cast from integer. + /// + public static explicit operator ExampleStruct(int value) + { + throw new NotImplementedException(); + } + + /// + /// Explicit cast to integer. + /// + public static explicit operator int(ExampleStruct value) + { + throw new NotImplementedException(); + } + + /// + /// Implicit cast from string. + /// + public static implicit operator ExampleStruct(string value) + { + throw new NotImplementedException(); + } + + /// + /// Implicit cast to string. + /// + public static implicit operator string(ExampleStruct value) + { + throw new NotImplementedException(); + } + + /// + /// Check for equality. + /// + public override bool Equals(object? obj) + { + throw new NotImplementedException(); + } + + /// + /// Check for equality. + /// + public override int GetHashCode() + { + throw new NotImplementedException(); } } diff --git a/tools/ExampleAssembly/ExampleTriGenericClass.cs b/tools/ExampleAssembly/ExampleTriGenericClass.cs index 4c353ff..13f0fa7 100644 --- a/tools/ExampleAssembly/ExampleTriGenericClass.cs +++ b/tools/ExampleAssembly/ExampleTriGenericClass.cs @@ -1,14 +1,13 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// A generic class with three generic type parameters. +/// +/// The first generic type. +/// The second generic type. +/// The third generic type. +public class ExampleTriGenericClass + where TTwo : struct, IEnumerable + where TThree : class, TOne, IEnumerable { - /// - /// A generic class with three generic type parameters. - /// - /// The first generic type. - /// The second generic type. - /// The third generic type. - public class ExampleTriGenericClass - where TTwo : struct, IEnumerable - where TThree : class, TOne, IEnumerable - { - } } diff --git a/tools/ExampleAssembly/ExampleTuple.cs b/tools/ExampleAssembly/ExampleTuple.cs index 95ce46e..f7ebd92 100644 --- a/tools/ExampleAssembly/ExampleTuple.cs +++ b/tools/ExampleAssembly/ExampleTuple.cs @@ -1,47 +1,46 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// A helper class for tuples. +/// +public static class ExampleTuple { - /// - /// A helper class for tuples. - /// - public static class ExampleTuple - { - } +} - /// - /// A 1-tuple. - /// - /// The first type. - public struct ExampleTuple - { - } +/// +/// A 1-tuple. +/// +/// The first type. +public struct ExampleTuple +{ +} - /// - /// A 2-tuple. - /// - /// The first type. - /// The second type. - public struct ExampleTuple - { - } +/// +/// A 2-tuple. +/// +/// The first type. +/// The second type. +public struct ExampleTuple +{ +} - /// - /// A 3-tuple. - /// - /// The first type. - /// The second type. - /// The third type. - public struct ExampleTuple - { - } +/// +/// A 3-tuple. +/// +/// The first type. +/// The second type. +/// The third type. +public struct ExampleTuple +{ +} - /// - /// A 4-tuple. - /// - /// The first type. - /// The second type. - /// The third type. - /// The fourth type. - public struct ExampleTuple - { - } +/// +/// A 4-tuple. +/// +/// The first type. +/// The second type. +/// The third type. +/// The fourth type. +public struct ExampleTuple +{ } diff --git a/tools/ExampleAssembly/ExampleUnbrowsableClass.cs b/tools/ExampleAssembly/ExampleUnbrowsableClass.cs index ee32f94..94260d0 100644 --- a/tools/ExampleAssembly/ExampleUnbrowsableClass.cs +++ b/tools/ExampleAssembly/ExampleUnbrowsableClass.cs @@ -1,12 +1,11 @@ using System.ComponentModel; -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// An unbrowsable class. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public class ExampleUnbrowsableClass { - /// - /// An unbrowsable class. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public class ExampleUnbrowsableClass - { - } } diff --git a/tools/ExampleAssembly/IExampleContravariantInterface.cs b/tools/ExampleAssembly/IExampleContravariantInterface.cs index 15530e7..0ada367 100644 --- a/tools/ExampleAssembly/IExampleContravariantInterface.cs +++ b/tools/ExampleAssembly/IExampleContravariantInterface.cs @@ -1,9 +1,8 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// An interface with contravariant generic parameters. +/// +public interface IExampleContravariantInterface { - /// - /// An interface with contravariant generic parameters. - /// - public interface IExampleContravariantInterface - { - } } diff --git a/tools/ExampleAssembly/IExampleCovariantInterface.cs b/tools/ExampleAssembly/IExampleCovariantInterface.cs index ee4cdfc..cebae4c 100644 --- a/tools/ExampleAssembly/IExampleCovariantInterface.cs +++ b/tools/ExampleAssembly/IExampleCovariantInterface.cs @@ -1,9 +1,8 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// An interface with covariant generic parameters. +/// +public interface IExampleCovariantInterface { - /// - /// An interface with covariant generic parameters. - /// - public interface IExampleCovariantInterface - { - } } diff --git a/tools/ExampleAssembly/IExampleDerivedInterface.cs b/tools/ExampleAssembly/IExampleDerivedInterface.cs index 2860f12..8921f5c 100644 --- a/tools/ExampleAssembly/IExampleDerivedInterface.cs +++ b/tools/ExampleAssembly/IExampleDerivedInterface.cs @@ -1,9 +1,8 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// A derived interface. +/// +public interface IExampleDerivedInterface : IReadOnlyDictionary, IDictionary { - /// - /// A derived interface. - /// - public interface IExampleDerivedInterface : IReadOnlyDictionary, IDictionary - { - } } diff --git a/tools/ExampleAssembly/IExampleInterface.cs b/tools/ExampleAssembly/IExampleInterface.cs index d3b1d83..22c83cb 100644 --- a/tools/ExampleAssembly/IExampleInterface.cs +++ b/tools/ExampleAssembly/IExampleInterface.cs @@ -1,13 +1,12 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// An interface. +/// +public interface IExampleInterface { /// - /// An interface. + /// An interface method. /// - public interface IExampleInterface - { - /// - /// An interface method. - /// - int ExampleMethod(string value); - } + int ExampleMethod(string value); } diff --git a/tools/ExampleAssembly/IExampleInternalInterface.cs b/tools/ExampleAssembly/IExampleInternalInterface.cs index 059f4f4..2d17ef1 100644 --- a/tools/ExampleAssembly/IExampleInternalInterface.cs +++ b/tools/ExampleAssembly/IExampleInternalInterface.cs @@ -1,9 +1,8 @@ -namespace ExampleAssembly +namespace ExampleAssembly; + +/// +/// An internal interface. +/// +internal interface IExampleInternalInterface { - /// - /// An internal interface. - /// - internal interface IExampleInternalInterface - { - } } diff --git a/tools/ExampleAssembly/InnerNamespace/ExampleInnerClass.cs b/tools/ExampleAssembly/InnerNamespace/ExampleInnerClass.cs index 6cfef12..a843df3 100644 --- a/tools/ExampleAssembly/InnerNamespace/ExampleInnerClass.cs +++ b/tools/ExampleAssembly/InnerNamespace/ExampleInnerClass.cs @@ -1,15 +1,14 @@ -namespace ExampleAssembly.InnerNamespace +namespace ExampleAssembly.InnerNamespace; + +/// +/// A class in an inner namespace. +/// +public class ExampleInnerClass { /// - /// A class in an inner namespace. + /// An explicit default constructor. /// - public class ExampleInnerClass + public ExampleInnerClass() { - /// - /// An explicit default constructor. - /// - public ExampleInnerClass() - { - } } } diff --git a/tools/ExampleAssembly/IsExternalInit.cs b/tools/ExampleAssembly/IsExternalInit.cs deleted file mode 100644 index 50093a5..0000000 --- a/tools/ExampleAssembly/IsExternalInit.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.ComponentModel; - -namespace System.Runtime.CompilerServices -{ - /// - /// Bug fix for C# 9 record when not using .NET 5. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - internal class IsExternalInit - { - } -} diff --git a/tools/XmlDocGen/Program.cs b/tools/XmlDocGen/Program.cs index 893a7df..549fd9f 100644 --- a/tools/XmlDocGen/Program.cs +++ b/tools/XmlDocGen/Program.cs @@ -1,3 +1,8 @@ using XmlDocMarkdown.Core; -return XmlDocMarkdownApp.Run(args); +return XmlDocMarkdownApp.Run(args, + (assembly, settings) => + { + settings.NewLine = "\n"; + settings.ShouldClean = true; + });