From 9c2afc98c258f1240ae56e71ad82b2794a631e3a Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Tue, 11 Oct 2022 23:47:37 +0200 Subject: [PATCH 1/6] Support sphinx --- .../CommandLine.DocumentationGenerator.csproj | 4 +- src/CommandLine/CommandLine.csproj | 2 +- .../Commands/GenerateDocCommand.cs | 86 +++++++++- .../Commands/GenerateDocRootCommand.cs | 7 +- src/CommandLine/DocumentationHost.cs | 1 + .../AbstractGenerateDocCommandLineOptions.cs | 4 +- .../Properties/launchSettings.json | 2 +- src/Documentation/DocumentationContext.cs | 17 ++ src/Documentation/DocumentationGenerator.cs | 46 +++--- .../DocumentationGeneratorResult.cs | 47 +----- src/Documentation/DocumentationUtility.cs | 151 +++++++++++++----- src/Documentation/DocumentationWriter.cs | 20 +-- .../Markdown/DocusaurusDocumentationWriter.cs | 69 +------- .../Markdown/MarkdownDocumentationWriter.cs | 2 +- .../Markdown/SphinxDocumentationWriter.cs | 18 +++ .../CommonDocumentationUrlProvider.cs | 3 + .../SphinxDocumentationUrlProvider.cs | 27 ++++ tools/build_cli.cmd | 2 +- tools/reinstall_dotnet_cli.cmd | 2 +- 19 files changed, 317 insertions(+), 193 deletions(-) create mode 100644 src/Documentation/Markdown/SphinxDocumentationWriter.cs create mode 100644 src/Documentation/UrlProviders/SphinxDocumentationUrlProvider.cs diff --git a/src/CommandLine.DocumentationGenerator/CommandLine.DocumentationGenerator.csproj b/src/CommandLine.DocumentationGenerator/CommandLine.DocumentationGenerator.csproj index 83012de6c7..a662b5de87 100644 --- a/src/CommandLine.DocumentationGenerator/CommandLine.DocumentationGenerator.csproj +++ b/src/CommandLine.DocumentationGenerator/CommandLine.DocumentationGenerator.csproj @@ -1,10 +1,10 @@  - net5.0 + net6.0 - + net5.0;net6.0 diff --git a/src/CommandLine/CommandLine.csproj b/src/CommandLine/CommandLine.csproj index 1a37f6328b..e7a4d4f441 100644 --- a/src/CommandLine/CommandLine.csproj +++ b/src/CommandLine/CommandLine.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 diff --git a/src/CommandLine/Commands/GenerateDocCommand.cs b/src/CommandLine/Commands/GenerateDocCommand.cs index aeec2e630c..33d004b300 100644 --- a/src/CommandLine/Commands/GenerateDocCommand.cs +++ b/src/CommandLine/Commands/GenerateDocCommand.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -112,7 +113,7 @@ public override async Task ExecuteAsync(ProjectOrSolution project ignoredCommonParts: IgnoredCommonParts, includeContainingNamespaceFilter: IncludeContainingNamespaceFilter, filesLayout: FilesLayout, - scrollToContent: Options.ScrollToContent); + scrollToContent: (DocumentationHost == DocumentationHost.GitHub) && Options.ScrollToContent); ImmutableArray compilations = await GetCompilationsAsync(projectOrSolution, cancellationToken); @@ -123,7 +124,9 @@ public override async Task ExecuteAsync(ProjectOrSolution project if (GroupByCommonNamespace) { commonNamespaces = DocumentationUtility.FindCommonNamespaces( - documentationModel.Types.Concat(documentationModel.GetExtendedExternalTypes())); + documentationModel.Types + .Concat(documentationModel.GetExtendedExternalTypes()) + .Where(f => !documentationOptions.ShouldBeIgnored(f))); } UrlSegmentProvider urlSegmentProvider = new DefaultUrlSegmentProvider(FilesLayout, commonNamespaces); @@ -138,6 +141,8 @@ DocumentationUrlProvider GetUrlProvider() return new GitHubDocumentationUrlProvider(urlSegmentProvider, externalProviders); case DocumentationHost.Docusaurus: return new DocusaurusDocumentationUrlProvider(urlSegmentProvider, externalProviders); + case DocumentationHost.Sphinx: + return new SphinxDocumentationUrlProvider(urlSegmentProvider, externalProviders); default: throw new InvalidOperationException($"Unknown value '{DocumentationHost}'."); } @@ -148,6 +153,7 @@ MarkdownWriterSettings GetMarkdownWriterSettings() switch (DocumentationHost) { case DocumentationHost.GitHub: + case DocumentationHost.Sphinx: return MarkdownWriterSettings.Default; case DocumentationHost.Docusaurus: return new MarkdownWriterSettings(new MarkdownFormat(angleBracketEscapeStyle: AngleBracketEscapeStyle.EntityRef)); @@ -168,6 +174,8 @@ DocumentationWriter CreateDocumentationWriter(DocumentationContext context) return new MarkdownDocumentationWriter(context, writer); case DocumentationHost.Docusaurus: return new DocusaurusDocumentationWriter(context, writer); + case DocumentationHost.Sphinx: + return new SphinxDocumentationWriter(context, writer); default: throw new InvalidOperationException($"Unknown value '{DocumentationHost}'."); } @@ -206,7 +214,18 @@ DocumentationWriter CreateDocumentationWriter(DocumentationContext context) WriteLine($"Generate documentation to '{Options.Output}'", Verbosity.Minimal); - foreach (DocumentationGeneratorResult documentationFile in generator.Generate(heading: Options.Heading, cancellationToken)) + IEnumerable results = generator.Generate(heading: Options.Heading, cancellationToken); + + if (DocumentationHost == DocumentationHost.Sphinx) + { + List resultList = results.ToList(); + + AddTableOfContents(resultList); + + results = resultList; + } + + foreach (DocumentationGeneratorResult documentationFile in results) { string path = Path.Combine(directoryPath, documentationFile.FilePath); @@ -221,5 +240,66 @@ DocumentationWriter CreateDocumentationWriter(DocumentationContext context) return CommandResults.Success; } + + private void AddTableOfContents(IEnumerable results) + { + foreach (DocumentationGeneratorResult result in results) + { + string content = result.Content; + string filePath = result.FilePath; + string directoryPath = Path.GetDirectoryName(filePath); + + IEnumerable children = results.Where(r => + { + if (r != result) + { + string path = r.FilePath; + + if (path.StartsWith(directoryPath)) + { + string relativePath = path.Substring(directoryPath.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + + if (relativePath.Count(f => f == Path.DirectorySeparatorChar || f == Path.AltDirectorySeparatorChar) == 1) + { + return true; + } + } + } + + return false; + }); + + if (children.Any()) + { + var sb = new StringBuilder(); + + sb.AppendLine(); + sb.AppendLine("```{toctree}"); + sb.AppendLine(":hidden:"); + sb.AppendLine(":maxdepth: 1"); + sb.AppendLine(); + + foreach (DocumentationGeneratorResult child in children + .OrderBy(child => child.Label)) + { + Debug.Assert(child.Label is not null); + + sb.Append(child.Label); + sb.Append(" <"); + + sb.Append(child.FilePath + .Substring(directoryPath.Length) + .TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + .Replace('\\', '/')); + + sb.AppendLine(">"); + } + + sb.AppendLine("```"); + + result.Content += sb.ToString(); + } + } + } } } diff --git a/src/CommandLine/Commands/GenerateDocRootCommand.cs b/src/CommandLine/Commands/GenerateDocRootCommand.cs index a77a62411f..bf4d6bcbc5 100644 --- a/src/CommandLine/Commands/GenerateDocRootCommand.cs +++ b/src/CommandLine/Commands/GenerateDocRootCommand.cs @@ -62,7 +62,7 @@ public override async Task ExecuteAsync(ProjectOrSolution project ignoredRootParts: IgnoredParts, includeContainingNamespaceFilter: IncludeContainingNamespaceFilter, includeSystemNamespace: Options.IncludeSystemNamespace, - scrollToContent: Options.ScrollToContent); + scrollToContent: (DocumentationHost == DocumentationHost.GitHub) && Options.ScrollToContent); ImmutableArray compilations = await GetCompilationsAsync(projectOrSolution, cancellationToken); @@ -80,6 +80,8 @@ DocumentationUrlProvider GetUrlProvider() return new GitHubDocumentationUrlProvider(urlSegmentProvider, externalProviders); case DocumentationHost.Docusaurus: return new DocusaurusDocumentationUrlProvider(urlSegmentProvider, externalProviders); + case DocumentationHost.Sphinx: + return new SphinxDocumentationUrlProvider(urlSegmentProvider, externalProviders); default: throw new InvalidOperationException($"Unknown value '{DocumentationHost}'."); } @@ -90,6 +92,7 @@ MarkdownWriterSettings GetMarkdownWriterSettings() switch (DocumentationHost) { case DocumentationHost.GitHub: + case DocumentationHost.Sphinx: return MarkdownWriterSettings.Default; case DocumentationHost.Docusaurus: return new MarkdownWriterSettings(new MarkdownFormat(angleBracketEscapeStyle: AngleBracketEscapeStyle.EntityRef)); @@ -110,6 +113,8 @@ DocumentationWriter CreateDocumentationWriter(DocumentationContext context) return new MarkdownDocumentationWriter(context, writer); case DocumentationHost.Docusaurus: return new DocusaurusDocumentationWriter(context, writer); + case DocumentationHost.Sphinx: + return new SphinxDocumentationWriter(context, writer); default: throw new InvalidOperationException($"Unknown value '{DocumentationHost}'."); } diff --git a/src/CommandLine/DocumentationHost.cs b/src/CommandLine/DocumentationHost.cs index eaa9a3693e..989482a396 100644 --- a/src/CommandLine/DocumentationHost.cs +++ b/src/CommandLine/DocumentationHost.cs @@ -6,5 +6,6 @@ internal enum DocumentationHost { GitHub, Docusaurus, + Sphinx, } } diff --git a/src/CommandLine/Options/AbstractGenerateDocCommandLineOptions.cs b/src/CommandLine/Options/AbstractGenerateDocCommandLineOptions.cs index 6043a25bfd..da0a51385a 100644 --- a/src/CommandLine/Options/AbstractGenerateDocCommandLineOptions.cs +++ b/src/CommandLine/Options/AbstractGenerateDocCommandLineOptions.cs @@ -37,7 +37,7 @@ public abstract class AbstractGenerateDocCommandLineOptions : MSBuildCommandLine [Option( longName: OptionNames.Host, Required = true, - HelpText = "Defines a host where the content will be published. Allowed values are github or docusaurus.", + HelpText = "Defines a host where the content will be published. Allowed values are docusaurus, github or sphinx.", MetaValue = "")] public string Host { get; set; } @@ -59,7 +59,7 @@ public abstract class AbstractGenerateDocCommandLineOptions : MSBuildCommandLine [Option( longName: "scroll-to-content", - HelpText = "Indicates whether a link should lead to the top of the documentation content.")] + HelpText = "Indicates whether a link should lead to the top of the documentation content. This option is applicable when host is set to 'github'.")] public bool ScrollToContent { get; set; } [Option( diff --git a/src/CommandLine/Properties/launchSettings.json b/src/CommandLine/Properties/launchSettings.json index 3f374ad18d..0f42d35ba0 100644 --- a/src/CommandLine/Properties/launchSettings.json +++ b/src/CommandLine/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "CommandLine": { "commandName": "Project", - "commandLineArgs": "migrate \"E:\\Projects\\Roslynator\\src\\Migration.Test\" --target-version 3.0 --identifier roslynator.analyzers -d" + "commandLineArgs": "generate-doc \"C:\\code\\datamole\\ddp-kernel-dg-device-sdk\\src\\Core\\Core.csproj\" --properties Configuration=Release --output \"C:\\d\\docs\\source\\kernel\\services\\data-gateway-agent\\device-sdk\" --visibility public --heading \"Datamole Data Gateway SDK\" --host sphinx --ignored-root-parts class-hierarchy --ignored-common-parts content --ignored-names \"System.Runtime.CompilerServices.IsExternalInit\" --files-layout flat-namespaces --group-by-common-namespace" } } } \ No newline at end of file diff --git a/src/Documentation/DocumentationContext.cs b/src/Documentation/DocumentationContext.cs index 843472e3cc..37e96d2271 100644 --- a/src/Documentation/DocumentationContext.cs +++ b/src/Documentation/DocumentationContext.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using Microsoft.CodeAnalysis; namespace Roslynator.Documentation @@ -10,6 +11,7 @@ namespace Roslynator.Documentation public class DocumentationContext { private readonly Func _createWriter; + private ImmutableHashSet<(INamespaceSymbol, string)> _commonNamespacesAsText; public DocumentationContext( DocumentationModel documentationModel, @@ -44,6 +46,21 @@ public DocumentationContext( public ImmutableHashSet CommonNamespaces { get; } + internal ImmutableHashSet<(INamespaceSymbol symbol, string displayString)> CommonNamespacesAsText + { + get + { + if (_commonNamespacesAsText is null) + { + _commonNamespacesAsText = CommonNamespaces + .Select(f => (f, f.ToDisplayString(TypeSymbolDisplayFormats.Name_ContainingTypes_Namespaces_GlobalNamespace_OmittedAsContaining))) + .ToImmutableHashSet(); + } + + return _commonNamespacesAsText; + } + } + public DocumentationWriter CreateWriter() { return _createWriter(this); diff --git a/src/Documentation/DocumentationGenerator.cs b/src/Documentation/DocumentationGenerator.cs index 7a63c12296..b9cc697c69 100644 --- a/src/Documentation/DocumentationGenerator.cs +++ b/src/Documentation/DocumentationGenerator.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; @@ -60,12 +61,19 @@ internal ImmutableArray EnabledAndSortedRootParts { if (_enabledAndSortedRootParts.IsDefault) { - _enabledAndSortedRootParts = Enum.GetValues(typeof(RootDocumentationParts)) + IEnumerable parts = Enum.GetValues(typeof(RootDocumentationParts)) .Cast() .Where(f => f != RootDocumentationParts.None && f != RootDocumentationParts.All - && (Options.IgnoredRootParts & f) == 0) - .OrderBy(f => f, RootPartComparer) + && (Options.IgnoredRootParts & f) == 0); + + if (parts.Contains(RootDocumentationParts.Namespaces) + && parts.Contains(RootDocumentationParts.Types)) + { + parts = parts.Where(f => f != RootDocumentationParts.Namespaces); + } + + _enabledAndSortedRootParts = parts.OrderBy(f => f, RootPartComparer) .ToImmutableArray(); } @@ -149,8 +157,6 @@ public IEnumerable Generate(string heading = null, DocumentationDepth depth = Options.Depth; - DocumentationGeneratorResult objectModel = default; - using (DocumentationWriter writer = CreateWriter()) { yield return GenerateRoot(writer, heading); @@ -196,9 +202,6 @@ public IEnumerable Generate(string heading = null, } } - if (objectModel.HasContent) - yield return objectModel; - foreach (INamedTypeSymbol typeSymbol in DocumentationModel.GetExtendedExternalTypes()) { if (!Options.ShouldBeIgnored(typeSymbol)) @@ -221,7 +224,7 @@ internal DocumentationGeneratorResult GenerateRoot(DocumentationWriter writer, s writer.WriteStartDocument(null, DocumentationFileKind.Root); if (Options.ScrollToContent) - writer.WriteLinkDestination(WellKnownNames.TopFragmentName); + writer.WriteLinkTarget(WellKnownNames.TopFragmentName); writer.WriteStartHeading(1); writer.WriteString(heading); @@ -254,17 +257,20 @@ private void GenerateRoot(DocumentationWriter writer) break; } + case RootDocumentationParts.Types: case RootDocumentationParts.Namespaces: { IEnumerable namespaceSymbols = typeSymbols .Select(f => f.ContainingNamespace) .Distinct(MetadataNameEqualityComparer.Instance); + bool includeTypes = (Options.IgnoredRootParts & RootDocumentationParts.Types) == 0; + writer.WriteTypesByNamespace( typeSymbols, - Resources.TypesTitle, + (includeTypes) ? Resources.TypesTitle : Resources.NamespacesTitle, 2, - includeTypes: (Options.IgnoredRootParts & RootDocumentationParts.Types) == 0); + includeTypes: includeTypes); break; } @@ -304,7 +310,7 @@ bool HasContent(RootDocumentationParts part) case RootDocumentationParts.ClassHierarchy: return typeSymbols.Any(f => !f.IsStatic && f.TypeKind == TypeKind.Class); case RootDocumentationParts.Types: - return false; + return typeSymbols.Any(); case RootDocumentationParts.Other: return DocumentationModel.GetExtendedExternalTypes().Any(f => !Options.ShouldBeIgnored(f)); default: @@ -398,7 +404,7 @@ private DocumentationGeneratorResult GenerateNamespace(INamespaceSymbol namespac } case NamespaceDocumentationParts.Namespaces: { - if (!typeSymbols.Any()) + if (HasContent(NamespaceDocumentationParts.Namespaces)) { IEnumerable namespaces = DocumentationModel .Types @@ -451,7 +457,7 @@ void WriteNamespaces(IEnumerable namespaces) foreach (INamespaceSymbol namespaceSymbol in namespaces) { writer.WriteStartBulletItem(); - writer.WriteLink(namespaceSymbol, TypeSymbolDisplayFormats.Name_ContainingTypes_Namespaces); + writer.WriteLink(namespaceSymbol, TypeSymbolDisplayFormats.Name); writer.WriteEndBulletItem(); } } @@ -531,7 +537,7 @@ private DocumentationGeneratorResult GenerateExtendedExternalType(INamedTypeSymb if (Options.ScrollToContent) { - writer.WriteLinkDestination(WellKnownNames.TopFragmentName); + writer.WriteLinkTarget(WellKnownNames.TopFragmentName); writer.WriteLine(); } @@ -926,7 +932,7 @@ private IEnumerable GenerateMembers(TypeDocumentat if (Options.ScrollToContent) { - writer.WriteLinkDestination(WellKnownNames.TopFragmentName); + writer.WriteLinkTarget(WellKnownNames.TopFragmentName); writer.WriteLine(); } @@ -960,10 +966,10 @@ private IEnumerable GenerateMembers(TypeDocumentat { string id = DocumentationUrlProvider.GetFragment(overloadSymbol); + writer.WriteLinkTarget(id); writer.WriteStartHeading(2); writer.WriteString(overloadSymbol.ToDisplayString(format, additionalOptions)); writer.WriteSpace(); - writer.WriteLinkDestination(id); writer.WriteEndHeading(); GenerateMemberContent(writer, overloadSymbol, headingLevelBase: 1); @@ -1078,12 +1084,12 @@ void GenerateMemberContent(DocumentationWriter writer, ISymbol symbol, int headi private DocumentationGeneratorResult CreateResult(DocumentationWriter writer, DocumentationFileKind kind, ISymbol symbol = null) { - string fileName = UrlProvider.GetFileName(kind); - - return new DocumentationGeneratorResult(writer?.ToString(), GetPath(), kind); + return new DocumentationGeneratorResult(writer?.ToString(), GetPath(), kind, (symbol is not null) ? DocumentationUtility.GetSymbolLabel(symbol, Context) : null); string GetPath() { + string fileName = UrlProvider.GetFileName(kind); + switch (kind) { case DocumentationFileKind.Root: diff --git a/src/Documentation/DocumentationGeneratorResult.cs b/src/Documentation/DocumentationGeneratorResult.cs index ca1c33f680..4dafbdd0eb 100644 --- a/src/Documentation/DocumentationGeneratorResult.cs +++ b/src/Documentation/DocumentationGeneratorResult.cs @@ -1,64 +1,29 @@ // Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Diagnostics; namespace Roslynator.Documentation { [DebuggerDisplay("{DebuggerDisplay,nq}")] - public readonly struct DocumentationGeneratorResult : IEquatable + public class DocumentationGeneratorResult { - public DocumentationGeneratorResult(string content, string filePath, DocumentationFileKind kind) + public DocumentationGeneratorResult(string content, string filePath, DocumentationFileKind kind, string label) { Content = content; FilePath = filePath; Kind = kind; + Label = label; } - public string Content { get; } + public string Content { get; internal set; } public string FilePath { get; } public DocumentationFileKind Kind { get; } - internal bool HasContent - { - get { return !string.IsNullOrWhiteSpace(Content); } - } + public string Label { get; } [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private string DebuggerDisplay - { - get { return $"{Kind} {FilePath} {Content}"; } - } - - public override bool Equals(object obj) - { - return obj is DocumentationGeneratorResult other && Equals(other); - } - - public bool Equals(DocumentationGeneratorResult other) - { - return Kind == other.Kind - && FilePath == other.FilePath - && Content == other.Content; - } - - public override int GetHashCode() - { - return Hash.Combine( - StringComparer.Ordinal.GetHashCode(Content), - Hash.Combine(StringComparer.OrdinalIgnoreCase.GetHashCode(FilePath), (int)Kind)); - } - - public static bool operator ==(in DocumentationGeneratorResult file1, in DocumentationGeneratorResult file2) - { - return file1.Equals(file2); - } - - public static bool operator !=(in DocumentationGeneratorResult file1, in DocumentationGeneratorResult file2) - { - return !(file1 == file2); - } + private string DebuggerDisplay => $"{Kind} {FilePath} {Content}"; } } diff --git a/src/Documentation/DocumentationUtility.cs b/src/Documentation/DocumentationUtility.cs index 2c06bb2cad..1f91228384 100644 --- a/src/Documentation/DocumentationUtility.cs +++ b/src/Documentation/DocumentationUtility.cs @@ -1,6 +1,8 @@ // Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; @@ -10,6 +12,95 @@ namespace Roslynator.Documentation { internal static class DocumentationUtility { + public static string GetSymbolLabel(ISymbol symbol, DocumentationContext context) + { + if (symbol.IsKind(SymbolKind.Namespace)) + { + switch (context.Options.FilesLayout) + { + case FilesLayout.FlatNamespaces: + { + string label = symbol.ToDisplayString(TypeSymbolDisplayFormats.Name_ContainingTypes_Namespaces_GlobalNamespace_OmittedAsContaining); + + if (context.CommonNamespaces.Count > 0 + && !context.CommonNamespaces.Contains((INamespaceSymbol)symbol, MetadataNameEqualityComparer.Instance)) + { + (INamespaceSymbol symbol, string displayString) commonNamespace = default; + + foreach ((INamespaceSymbol _, string displayString) cn in context.CommonNamespacesAsText) + { + if (label.StartsWith(cn.displayString) + && label[cn.displayString.Length] == '.') + { + Debug.Assert(commonNamespace == default); + commonNamespace = cn; + } + } + + Debug.Assert(commonNamespace != default); + + if (commonNamespace != default) + label = label.Substring(commonNamespace.displayString.Length + 1); + } + + return label; + } + case FilesLayout.Hierarchical: + { + if (context.CommonNamespaces.Contains((INamespaceSymbol)symbol, MetadataNameEqualityComparer.Instance)) + return symbol.ToDisplayString(TypeSymbolDisplayFormats.Name_ContainingTypes_Namespaces_GlobalNamespace_OmittedAsContaining); + + return symbol.Name; + } + default: + { + throw new InvalidOperationException($"Unknown value '{context.Options.FilesLayout}'."); + } + } + } + else if (symbol.IsKind(SymbolKind.NamedType)) + { + string label = symbol.ToDisplayString(TypeSymbolDisplayFormats.Name_TypeParameters); + + if (symbol.ContainingType != null) + { + label = symbol.ContainingType.ToDisplayString(TypeSymbolDisplayFormats.Name_TypeParameters) + + "." + + label; + } + + return label; + } + else + { + string label = symbol.Name; + + if (symbol is IMethodSymbol methodSymbol) + { + if (methodSymbol.MethodKind == MethodKind.Constructor) + { + label = symbol.ContainingType.ToDisplayString(TypeSymbolDisplayFormats.Name_TypeParameters); + } + } + else if (symbol.Kind == SymbolKind.Property + && ((IPropertySymbol)symbol).IsIndexer) + { + label = "Item[]"; + } + + ISymbol explicitImplementation = symbol.GetFirstExplicitInterfaceImplementation(); + + if (explicitImplementation != null) + { + label = explicitImplementation.ContainingType.ToDisplayString(TypeSymbolDisplayFormats.Name_TypeParameters) + + "." + + label; + } + + return label; + } + } + public static bool ShouldGenerateNamespaceFile(INamespaceSymbol namespaceSymbol, IEnumerable commonNamespaces) { foreach (INamespaceSymbol commonNamespace in commonNamespaces) @@ -26,62 +117,38 @@ public static bool ShouldGenerateNamespaceFile(INamespaceSymbol namespaceSymbol, public static List FindCommonNamespaces(IEnumerable symbols) { - var commonNamespaces = new List(); + List<(INamespaceSymbol symbol, string text)> namespaces = symbols + .Select(f => f.ContainingNamespace) + .Distinct(MetadataNameEqualityComparer.Instance) + .Select(f => (f, f.ToDisplayString(TypeSymbolDisplayFormats.Name_ContainingTypes_Namespaces_GlobalNamespace))) + .ToList(); - var map = new Dictionary>>( - comparer: MetadataNameEqualityComparer.Instance); - - foreach (ITypeSymbol symbol in symbols) + for (int i = namespaces.Count - 1; i >= 0; i--) { - List namespaces = symbol.GetContainingNamespaces().ToList(); - - namespaces.Reverse(); - - if (!map.TryGetValue(namespaces[0], out List> value)) + for (int j = i - 1; j >= 0; j--) { - value = new List>(); - map[namespaces[0]] = value; - } - - value.Add(namespaces); - } - - foreach (KeyValuePair>> kvp in map) - { - List> values = kvp.Value; - List first = values[0]; - int i = 1; - - while (i < first.Count) - { - var success = true; + string n1 = namespaces[i].text; + string n2 = namespaces[j].text; - for (int j = 1; j < values.Count; j++) + if (n1 == n2) { - List other = values[j]; - - if (i >= other.Count - || !MetadataNameEqualityComparer.Instance.Equals(first[i], other[i])) - { - success = false; - break; - } + namespaces.RemoveAt(j); } - - if (success) + else if (n2.StartsWith(n1) + && n2[n1.Length] == '.') { - i++; + namespaces.RemoveAt(j); } - else + else if (n1.StartsWith(n2) + && n1[n2.Length] == '.') { + namespaces.RemoveAt(i); break; } } - - commonNamespaces.AddRange(first[i - 1].GetContainingNamespacesAndSelf()); } - return commonNamespaces; + return namespaces.ConvertAll(f => f.symbol); } public static string CreateLocalLink(ISymbol symbol, string prefix = null) diff --git a/src/Documentation/DocumentationWriter.cs b/src/Documentation/DocumentationWriter.cs index ffda7570a6..153b7bf0a1 100644 --- a/src/Documentation/DocumentationWriter.cs +++ b/src/Documentation/DocumentationWriter.cs @@ -226,7 +226,7 @@ public virtual void WriteBlockQuote(string text) public abstract void WriteLineBreak(); - public abstract void WriteLinkDestination(string name); + public abstract void WriteLinkTarget(string name); public virtual void WriteValue(bool value) { @@ -306,6 +306,7 @@ public virtual void WriteContent(IEnumerable names, bool addLinkToRoot = while (true) { + //TODO: link to a section in Sphinx WriteLink(en.Current, UrlProvider.GetFragment(en.Current)); if (en.MoveNext()) @@ -1224,7 +1225,8 @@ void WriteClassHierarchy(ImmutableHashSet duplicates) if (isExternal) WriteString(")"); - WriteLinkDestination(CreateLocalLink(baseType)); + //TODO: link to other items in class hierarchy + //WriteLinkTarget(CreateLocalLink(baseType)); WriteEndBulletItem(); @@ -1731,7 +1733,7 @@ internal void WriteHeading( { if (!string.IsNullOrEmpty(linkDestination)) { - WriteLinkDestination(linkDestination); + WriteLinkTarget(linkDestination); WriteLine(); } @@ -1921,19 +1923,19 @@ private string GetUrl( ? UrlSegmentProvider.GetSegments(CurrentSymbol) : default; - string fragment = GetFragment(); + string target = GetLocalLinkTarget(); - if (fragment == null + if (target == null && Options.ScrollToContent) { - fragment = "#" + WellKnownNames.TopFragmentName; + target = WellKnownNames.TopFragmentName; } - string url = UrlProvider.GetLocalUrl(segments, containingFolders, fragment).Url; + string url = UrlProvider.GetLocalUrl(segments, containingFolders, target).Url; return Options.RootDirectoryUrl + url; - string GetFragment() + string GetLocalLinkTarget() { if (symbol.Kind == SymbolKind.Method || (symbol.Kind == SymbolKind.Property && ((IPropertySymbol)symbol).IsIndexer)) @@ -1949,7 +1951,7 @@ string GetFragment() if (en.MoveNext() && en.MoveNext()) { - return "#" + DocumentationUrlProvider.GetFragment(symbol); + return DocumentationUrlProvider.GetFragment(symbol); } } } diff --git a/src/Documentation/Markdown/DocusaurusDocumentationWriter.cs b/src/Documentation/Markdown/DocusaurusDocumentationWriter.cs index 15f85013ec..89c9bac5fa 100644 --- a/src/Documentation/Markdown/DocusaurusDocumentationWriter.cs +++ b/src/Documentation/Markdown/DocusaurusDocumentationWriter.cs @@ -1,6 +1,5 @@ // Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using DotMarkdown; using Microsoft.CodeAnalysis; @@ -17,7 +16,7 @@ public override void WriteStartDocument(ISymbol symbol, DocumentationFileKind fi string label = null; if (symbol != null) - label = GetSidebarLabel(symbol); + label = DocumentationUtility.GetSymbolLabel(symbol, Context); if (fileKind == DocumentationFileKind.Root) label = Context.Options.RootFileHeading; @@ -42,71 +41,5 @@ public override void WriteStartDocument(ISymbol symbol, DocumentationFileKind fi WriteLine(); WriteLine(); } - - private string GetSidebarLabel(ISymbol symbol) - { - if (symbol.IsKind(SymbolKind.Namespace)) - { - switch (Context.Options.FilesLayout) - { - case FilesLayout.FlatNamespaces: - { - return symbol.ToDisplayString(TypeSymbolDisplayFormats.Name_ContainingTypes_Namespaces_GlobalNamespace_OmittedAsContaining); - } - case FilesLayout.Hierarchical: - { - if (Context.CommonNamespaces.Contains((INamespaceSymbol)symbol)) - return symbol.ToDisplayString(TypeSymbolDisplayFormats.Name_ContainingTypes_Namespaces_GlobalNamespace_OmittedAsContaining); - - return symbol.Name; - } - default: - { - throw new InvalidOperationException($"Unknown value '{Context.Options.FilesLayout}'."); - } - } - } - else if (symbol.IsKind(SymbolKind.NamedType)) - { - string label = symbol.ToDisplayString(TypeSymbolDisplayFormats.Name_TypeParameters); - - if (symbol.ContainingType != null) - { - label = symbol.ContainingType.ToDisplayString(TypeSymbolDisplayFormats.Name_TypeParameters) - + "." - + label; - } - - return label; - } - else - { - string label = symbol.Name; - - if (symbol is IMethodSymbol methodSymbol) - { - if (methodSymbol.MethodKind == MethodKind.Constructor) - { - label = symbol.ContainingType.ToDisplayString(TypeSymbolDisplayFormats.Name_TypeParameters); - } - } - else if (symbol.Kind == SymbolKind.Property - && ((IPropertySymbol)symbol).IsIndexer) - { - label = "Item[]"; - } - - ISymbol explicitImplementation = symbol.GetFirstExplicitInterfaceImplementation(); - - if (explicitImplementation != null) - { - label = explicitImplementation.ContainingType.ToDisplayString(TypeSymbolDisplayFormats.Name_TypeParameters) - + "." - + label; - } - - return label; - } - } } } diff --git a/src/Documentation/Markdown/MarkdownDocumentationWriter.cs b/src/Documentation/Markdown/MarkdownDocumentationWriter.cs index f24216213c..085cab8fe5 100644 --- a/src/Documentation/Markdown/MarkdownDocumentationWriter.cs +++ b/src/Documentation/Markdown/MarkdownDocumentationWriter.cs @@ -134,7 +134,7 @@ public override void WriteLineBreak() WriteLine(); } - public override void WriteLinkDestination(string name) + public override void WriteLinkTarget(string name) { WriteRaw($""); } diff --git a/src/Documentation/Markdown/SphinxDocumentationWriter.cs b/src/Documentation/Markdown/SphinxDocumentationWriter.cs new file mode 100644 index 0000000000..9ff06f5363 --- /dev/null +++ b/src/Documentation/Markdown/SphinxDocumentationWriter.cs @@ -0,0 +1,18 @@ +// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using DotMarkdown; + +namespace Roslynator.Documentation.Markdown +{ + public class SphinxDocumentationWriter : MarkdownDocumentationWriter + { + public SphinxDocumentationWriter(DocumentationContext context, MarkdownWriter writer) : base(context, writer) + { + } + + public override void WriteLinkTarget(string name) + { + WriteRaw($"({name})="); + } + } +} diff --git a/src/Documentation/UrlProviders/CommonDocumentationUrlProvider.cs b/src/Documentation/UrlProviders/CommonDocumentationUrlProvider.cs index 85b00847f5..22c762cfdc 100644 --- a/src/Documentation/UrlProviders/CommonDocumentationUrlProvider.cs +++ b/src/Documentation/UrlProviders/CommonDocumentationUrlProvider.cs @@ -49,6 +49,9 @@ public override string GetFileName(DocumentationFileKind kind) public override DocumentationUrlInfo GetLocalUrl(ImmutableArray folders, ImmutableArray containingFolders = default, string fragment = null) { + if (!string.IsNullOrEmpty(fragment)) + fragment = "#" + fragment; + string url = CreateLocalUrl(); return new DocumentationUrlInfo(url, DocumentationUrlKind.Local); diff --git a/src/Documentation/UrlProviders/SphinxDocumentationUrlProvider.cs b/src/Documentation/UrlProviders/SphinxDocumentationUrlProvider.cs new file mode 100644 index 0000000000..80fd2c9911 --- /dev/null +++ b/src/Documentation/UrlProviders/SphinxDocumentationUrlProvider.cs @@ -0,0 +1,27 @@ +// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Roslynator.Documentation +{ + internal sealed class SphinxDocumentationUrlProvider : CommonDocumentationUrlProvider + { + public SphinxDocumentationUrlProvider(UrlSegmentProvider segmentProvider, IEnumerable externalProviders = null) + : base(segmentProvider, externalProviders) + { + } + + public override string IndexFileName => "index.md"; + + public override DocumentationUrlInfo GetLocalUrl( + ImmutableArray folders, + ImmutableArray containingFolders = default, + string fragment = null) + { + return (!string.IsNullOrEmpty(fragment)) + ? new DocumentationUrlInfo(fragment, DocumentationUrlKind.Local) + : base.GetLocalUrl(folders, containingFolders, fragment); + } + } +} diff --git a/tools/build_cli.cmd b/tools/build_cli.cmd index a38b814272..ed856cb135 100644 --- a/tools/build_cli.cmd +++ b/tools/build_cli.cmd @@ -9,7 +9,7 @@ del /Q "%_outDir%\Roslynator.DotNet.Cli.*.nupkg" orang delete "../src" -a d -n "bin,obj" l li e -i "packages,node_modules" l li e ne -t n --content-only -y su s -dotnet restore --force "..\src\CommandLine.sln" +dotnet restore --force "..\src\CommandLine.sln" /p:RoslynatorCommandLine=true rd /S /Q "..\src\CommandLine\bin\Release" diff --git a/tools/reinstall_dotnet_cli.cmd b/tools/reinstall_dotnet_cli.cmd index ead79793ae..b446310887 100644 --- a/tools/reinstall_dotnet_cli.cmd +++ b/tools/reinstall_dotnet_cli.cmd @@ -2,6 +2,6 @@ dotnet tool uninstall roslynator.dotnet.cli -g -dotnet tool install roslynator.dotnet.cli -g --add-source "..\src\CommandLine\bin\Release" +dotnet tool install roslynator.dotnet.cli -g --add-source "..\out\Release" pause \ No newline at end of file From e1dd5e890916404c0e39f52511ef2be6d40d2a76 Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Sun, 16 Oct 2022 12:24:22 +0200 Subject: [PATCH 2/6] update changelog --- ChangeLog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 8ae12bc703..b1fe6f2259 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add code fix for CS0037 ([#929](https://github.com/josefpihrt/roslynator/pull/929)). - [CLI] Generate reference documentation that can be published with Docusaurus ([#918](https://github.com/josefpihrt/roslynator/pull/918)). - `roslynator generate-doc --host docusaurus` +- [CLI] Generate reference documentation that can be published with Sphinx ([#961](https://github.com/josefpihrt/roslynator/pull/961)). + - `roslynator generate-doc --host sphinx` ### Changed From bb01c50e871f80c543d47f28523e075e84559095 Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Fri, 21 Oct 2022 15:08:53 +0200 Subject: [PATCH 3/6] Update --- src/Documentation/DocumentationResources.cs | 4 +++- tools/reinstall_dotnet_cli_debug.cmd | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Documentation/DocumentationResources.cs b/src/Documentation/DocumentationResources.cs index 95818078ec..210878d94d 100644 --- a/src/Documentation/DocumentationResources.cs +++ b/src/Documentation/DocumentationResources.cs @@ -287,10 +287,12 @@ internal string GetHeading(RootDocumentationParts part) return NamespacesTitle; case RootDocumentationParts.ClassHierarchy: return ClassHierarchyTitle; + case RootDocumentationParts.Types: + return TypesTitle; case RootDocumentationParts.Other: return OtherTitle; default: - throw new ArgumentException("", nameof(part)); + throw new ArgumentException($"Unknown enum value '{part}'.", nameof(part)); } } diff --git a/tools/reinstall_dotnet_cli_debug.cmd b/tools/reinstall_dotnet_cli_debug.cmd index bdfd7bb2b4..9275f41b40 100644 --- a/tools/reinstall_dotnet_cli_debug.cmd +++ b/tools/reinstall_dotnet_cli_debug.cmd @@ -1,6 +1,6 @@ @echo off -rd /S /Q "..\src\CommandLine\bin\Debug\netcoreapp3.1" +rd /S /Q "..\src\CommandLine\bin\Debug\net6.0" del /Q "..\src\CommandLine\bin\Debug\Roslynator.DotNet.Cli.*.nupkg" From 6d555c713b527bc33a6f879569f53391857de850 Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Sat, 22 Oct 2022 16:37:40 +0200 Subject: [PATCH 4/6] update --- src/Documentation/DocumentationWriter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Documentation/DocumentationWriter.cs b/src/Documentation/DocumentationWriter.cs index 153b7bf0a1..fbfe37d05f 100644 --- a/src/Documentation/DocumentationWriter.cs +++ b/src/Documentation/DocumentationWriter.cs @@ -306,7 +306,7 @@ public virtual void WriteContent(IEnumerable names, bool addLinkToRoot = while (true) { - //TODO: link to a section in Sphinx + //TODO: Sphinx: link to a section WriteLink(en.Current, UrlProvider.GetFragment(en.Current)); if (en.MoveNext()) @@ -1225,7 +1225,7 @@ void WriteClassHierarchy(ImmutableHashSet duplicates) if (isExternal) WriteString(")"); - //TODO: link to other items in class hierarchy + //TODO: Sphinx: link to other items in class hierarchy //WriteLinkTarget(CreateLocalLink(baseType)); WriteEndBulletItem(); From ffcf7b85da01605ed518af098d07b1fe3f75dfd0 Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Sat, 22 Oct 2022 17:37:27 +0200 Subject: [PATCH 5/6] update --- .../Commands/GenerateDocCommand.cs | 2 +- .../Commands/GenerateDocRootCommand.cs | 2 +- .../Properties/launchSettings.json | 2 +- src/Documentation/DocumentationGenerator.cs | 8 +++---- src/Documentation/DocumentationWriter.cs | 24 +++++++++++++------ .../Markdown/DocusaurusDocumentationWriter.cs | 4 ++++ .../Markdown/GitHubDocumentationWriter.cs | 17 +++++++++++++ .../Markdown/MarkdownDocumentationWriter.cs | 2 +- .../Markdown/SphinxDocumentationWriter.cs | 4 ++++ 9 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 src/Documentation/Markdown/GitHubDocumentationWriter.cs diff --git a/src/CommandLine/Commands/GenerateDocCommand.cs b/src/CommandLine/Commands/GenerateDocCommand.cs index 33d004b300..c84678ab3f 100644 --- a/src/CommandLine/Commands/GenerateDocCommand.cs +++ b/src/CommandLine/Commands/GenerateDocCommand.cs @@ -171,7 +171,7 @@ DocumentationWriter CreateDocumentationWriter(DocumentationContext context) switch (DocumentationHost) { case DocumentationHost.GitHub: - return new MarkdownDocumentationWriter(context, writer); + return new GitHubDocumentationWriter(context, writer); case DocumentationHost.Docusaurus: return new DocusaurusDocumentationWriter(context, writer); case DocumentationHost.Sphinx: diff --git a/src/CommandLine/Commands/GenerateDocRootCommand.cs b/src/CommandLine/Commands/GenerateDocRootCommand.cs index 01088ed57a..5f3c67ef2a 100644 --- a/src/CommandLine/Commands/GenerateDocRootCommand.cs +++ b/src/CommandLine/Commands/GenerateDocRootCommand.cs @@ -110,7 +110,7 @@ DocumentationWriter CreateDocumentationWriter(DocumentationContext context) switch (DocumentationHost) { case DocumentationHost.GitHub: - return new MarkdownDocumentationWriter(context, writer); + return new GitHubDocumentationWriter(context, writer); case DocumentationHost.Docusaurus: return new DocusaurusDocumentationWriter(context, writer); case DocumentationHost.Sphinx: diff --git a/src/CommandLine/Properties/launchSettings.json b/src/CommandLine/Properties/launchSettings.json index 0f42d35ba0..3f374ad18d 100644 --- a/src/CommandLine/Properties/launchSettings.json +++ b/src/CommandLine/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "CommandLine": { "commandName": "Project", - "commandLineArgs": "generate-doc \"C:\\code\\datamole\\ddp-kernel-dg-device-sdk\\src\\Core\\Core.csproj\" --properties Configuration=Release --output \"C:\\d\\docs\\source\\kernel\\services\\data-gateway-agent\\device-sdk\" --visibility public --heading \"Datamole Data Gateway SDK\" --host sphinx --ignored-root-parts class-hierarchy --ignored-common-parts content --ignored-names \"System.Runtime.CompilerServices.IsExternalInit\" --files-layout flat-namespaces --group-by-common-namespace" + "commandLineArgs": "migrate \"E:\\Projects\\Roslynator\\src\\Migration.Test\" --target-version 3.0 --identifier roslynator.analyzers -d" } } } \ No newline at end of file diff --git a/src/Documentation/DocumentationGenerator.cs b/src/Documentation/DocumentationGenerator.cs index b9cc697c69..1fd217b90c 100644 --- a/src/Documentation/DocumentationGenerator.cs +++ b/src/Documentation/DocumentationGenerator.cs @@ -345,7 +345,7 @@ private DocumentationGeneratorResult GenerateNamespace(INamespaceSymbol namespac .Select(f => Resources.GetHeading(f)); if ((Options.IgnoredNamespaceParts & NamespaceDocumentationParts.Content) == 0) - writer.WriteContent(names, addLinkToRoot: true); + writer.WriteContent(names, includeLinkToRoot: true); break; } @@ -550,7 +550,7 @@ private DocumentationGeneratorResult GenerateExtendedExternalType(INamedTypeSymb writer.WriteEndHeading(); if ((Options.IgnoredRootParts & RootDocumentationParts.Content) == 0) - writer.WriteContent(Array.Empty(), addLinkToRoot: true); + writer.WriteContent(Array.Empty(), includeLinkToRoot: true); writer.WriteTable( DocumentationModel.GetExtensionMethods(typeSymbol), @@ -607,7 +607,7 @@ private DocumentationGeneratorResult GenerateType(TypeDocumentationModel typeMod .Select(f => Resources.GetHeading(f)); if ((Options.IgnoredTypeParts & TypeDocumentationParts.Content) == 0) - writer.WriteContent(names, addLinkToRoot: true); + writer.WriteContent(names, includeLinkToRoot: true); break; } @@ -939,7 +939,7 @@ private IEnumerable GenerateMembers(TypeDocumentat writer.WriteMemberTitle(symbol, isOverloaded); if ((Options.IgnoredMemberParts & MemberDocumentationParts.Content) == 0) - writer.WriteContent(Array.Empty(), addLinkToRoot: true); + writer.WriteContent(Array.Empty(), includeLinkToRoot: true); if ((Options.IgnoredMemberParts & MemberDocumentationParts.ContainingType) == 0) writer.WriteContainingType(symbol.ContainingType, Resources.ContainingTypeTitle); diff --git a/src/Documentation/DocumentationWriter.cs b/src/Documentation/DocumentationWriter.cs index fbfe37d05f..8573705b31 100644 --- a/src/Documentation/DocumentationWriter.cs +++ b/src/Documentation/DocumentationWriter.cs @@ -41,6 +41,10 @@ protected DocumentationWriter(DocumentationContext context) private UrlSegmentProvider UrlSegmentProvider => Context.UrlProvider.SegmentProvider; + internal abstract bool IncludeLinkInClassHierarchy { get; } + + internal abstract bool IncludeLinkToRoot { get; } + private SymbolXmlDocumentation GetXmlDocumentation(ISymbol symbol) { return DocumentationModel.GetXmlDocumentation(symbol, Options.PreferredCultureName); @@ -285,28 +289,34 @@ private void WriteContainingNamespacePrefix(ISymbol symbol) } } - public virtual void WriteContent(IEnumerable names, bool addLinkToRoot = false, bool beginWithSeparator = false) + public virtual void WriteContent(IEnumerable names, bool includeLinkToRoot = false, bool beginWithSeparator = false) { + if (!IncludeLinkToRoot) + includeLinkToRoot = false; + IEnumerator en = names.GetEnumerator(); - if (addLinkToRoot) + if (includeLinkToRoot) { if (beginWithSeparator) WriteContentSeparator(); WriteLink(Resources.HomeTitle, UrlProvider.GetUrlToRoot(UrlSegmentProvider.GetSegments(CurrentSymbol).Length, '/', scrollToContent: Options.ScrollToContent)); } + else if (names.Count() == 1) + { + return; + } if (en.MoveNext()) { - if (addLinkToRoot || beginWithSeparator) + if (includeLinkToRoot || beginWithSeparator) { WriteContentSeparator(); } while (true) { - //TODO: Sphinx: link to a section WriteLink(en.Current, UrlProvider.GetFragment(en.Current)); if (en.MoveNext()) @@ -322,7 +332,7 @@ public virtual void WriteContent(IEnumerable names, bool addLinkToRoot = WriteLine(); WriteLine(); } - else if (addLinkToRoot) + else if (includeLinkToRoot) { WriteLine(); WriteLine(); @@ -1225,8 +1235,8 @@ void WriteClassHierarchy(ImmutableHashSet duplicates) if (isExternal) WriteString(")"); - //TODO: Sphinx: link to other items in class hierarchy - //WriteLinkTarget(CreateLocalLink(baseType)); + if (IncludeLinkInClassHierarchy) + WriteLinkTarget(CreateLocalLink(baseType)); WriteEndBulletItem(); diff --git a/src/Documentation/Markdown/DocusaurusDocumentationWriter.cs b/src/Documentation/Markdown/DocusaurusDocumentationWriter.cs index 89c9bac5fa..caf34bf197 100644 --- a/src/Documentation/Markdown/DocusaurusDocumentationWriter.cs +++ b/src/Documentation/Markdown/DocusaurusDocumentationWriter.cs @@ -11,6 +11,10 @@ public DocusaurusDocumentationWriter(DocumentationContext context, MarkdownWrite { } + internal override bool IncludeLinkInClassHierarchy => true; + + internal override bool IncludeLinkToRoot => false; + public override void WriteStartDocument(ISymbol symbol, DocumentationFileKind fileKind) { string label = null; diff --git a/src/Documentation/Markdown/GitHubDocumentationWriter.cs b/src/Documentation/Markdown/GitHubDocumentationWriter.cs new file mode 100644 index 0000000000..b96b950979 --- /dev/null +++ b/src/Documentation/Markdown/GitHubDocumentationWriter.cs @@ -0,0 +1,17 @@ +// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using DotMarkdown; + +namespace Roslynator.Documentation.Markdown +{ + public class GitHubDocumentationWriter : MarkdownDocumentationWriter + { + public GitHubDocumentationWriter(DocumentationContext context, MarkdownWriter writer) : base(context, writer) + { + } + + internal override bool IncludeLinkInClassHierarchy => true; + + internal override bool IncludeLinkToRoot => true; + } +} diff --git a/src/Documentation/Markdown/MarkdownDocumentationWriter.cs b/src/Documentation/Markdown/MarkdownDocumentationWriter.cs index 085cab8fe5..a66a0a2c3e 100644 --- a/src/Documentation/Markdown/MarkdownDocumentationWriter.cs +++ b/src/Documentation/Markdown/MarkdownDocumentationWriter.cs @@ -5,7 +5,7 @@ namespace Roslynator.Documentation.Markdown { - public class MarkdownDocumentationWriter : DocumentationWriter + public abstract class MarkdownDocumentationWriter : DocumentationWriter { private readonly MarkdownWriter _writer; diff --git a/src/Documentation/Markdown/SphinxDocumentationWriter.cs b/src/Documentation/Markdown/SphinxDocumentationWriter.cs index 9ff06f5363..ff14b77f1f 100644 --- a/src/Documentation/Markdown/SphinxDocumentationWriter.cs +++ b/src/Documentation/Markdown/SphinxDocumentationWriter.cs @@ -10,6 +10,10 @@ public SphinxDocumentationWriter(DocumentationContext context, MarkdownWriter wr { } + internal override bool IncludeLinkInClassHierarchy => false; + + internal override bool IncludeLinkToRoot => false; + public override void WriteLinkTarget(string name) { WriteRaw($"({name})="); From f152df4582c395a381ec69c0391896e5aa206117 Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Sat, 22 Oct 2022 19:41:36 +0200 Subject: [PATCH 6/6] Update --- src/CommandLine/AssemblyResolver.cs | 3 ++- src/CommandLine/Commands/GenerateDocCommand.cs | 2 -- src/CommandLine/DocumentationHost.cs | 2 +- src/Documentation/DocumentationContext.cs | 11 +++-------- src/Documentation/DocumentationGenerator.cs | 3 ++- .../Markdown/MarkdownDocumentationWriter.cs | 2 +- 6 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/CommandLine/AssemblyResolver.cs b/src/CommandLine/AssemblyResolver.cs index b124192a54..151e975c88 100644 --- a/src/CommandLine/AssemblyResolver.cs +++ b/src/CommandLine/AssemblyResolver.cs @@ -56,7 +56,8 @@ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEven Debug.Assert( (!assemblyName.Name.StartsWith("Microsoft.") || assemblyName.Name.StartsWith("Microsoft.VisualStudio.") - || string.Equals(assemblyName.Name, "Microsoft.DiaSymReader", StringComparison.Ordinal)) + || string.Equals(assemblyName.Name, "Microsoft.DiaSymReader", StringComparison.Ordinal) + || assemblyName.Name.EndsWith(".Analyzers")) && !assemblyName.Name.StartsWith("System."), assemblyName.ToString()); diff --git a/src/CommandLine/Commands/GenerateDocCommand.cs b/src/CommandLine/Commands/GenerateDocCommand.cs index c84678ab3f..327bf87f27 100644 --- a/src/CommandLine/Commands/GenerateDocCommand.cs +++ b/src/CommandLine/Commands/GenerateDocCommand.cs @@ -219,9 +219,7 @@ DocumentationWriter CreateDocumentationWriter(DocumentationContext context) if (DocumentationHost == DocumentationHost.Sphinx) { List resultList = results.ToList(); - AddTableOfContents(resultList); - results = resultList; } diff --git a/src/CommandLine/DocumentationHost.cs b/src/CommandLine/DocumentationHost.cs index 989482a396..253d9c93a1 100644 --- a/src/CommandLine/DocumentationHost.cs +++ b/src/CommandLine/DocumentationHost.cs @@ -4,8 +4,8 @@ namespace Roslynator.CommandLine { internal enum DocumentationHost { - GitHub, Docusaurus, + GitHub, Sphinx, } } diff --git a/src/Documentation/DocumentationContext.cs b/src/Documentation/DocumentationContext.cs index 37e96d2271..23b2d43c69 100644 --- a/src/Documentation/DocumentationContext.cs +++ b/src/Documentation/DocumentationContext.cs @@ -50,14 +50,9 @@ public DocumentationContext( { get { - if (_commonNamespacesAsText is null) - { - _commonNamespacesAsText = CommonNamespaces - .Select(f => (f, f.ToDisplayString(TypeSymbolDisplayFormats.Name_ContainingTypes_Namespaces_GlobalNamespace_OmittedAsContaining))) - .ToImmutableHashSet(); - } - - return _commonNamespacesAsText; + return _commonNamespacesAsText ??= CommonNamespaces + .Select(f => (f, f.ToDisplayString(TypeSymbolDisplayFormats.Name_ContainingTypes_Namespaces_GlobalNamespace_OmittedAsContaining))) + .ToImmutableHashSet(); } } diff --git a/src/Documentation/DocumentationGenerator.cs b/src/Documentation/DocumentationGenerator.cs index 1fd217b90c..b3987c0362 100644 --- a/src/Documentation/DocumentationGenerator.cs +++ b/src/Documentation/DocumentationGenerator.cs @@ -73,7 +73,8 @@ internal ImmutableArray EnabledAndSortedRootParts parts = parts.Where(f => f != RootDocumentationParts.Namespaces); } - _enabledAndSortedRootParts = parts.OrderBy(f => f, RootPartComparer) + _enabledAndSortedRootParts = parts + .OrderBy(f => f, RootPartComparer) .ToImmutableArray(); } diff --git a/src/Documentation/Markdown/MarkdownDocumentationWriter.cs b/src/Documentation/Markdown/MarkdownDocumentationWriter.cs index a66a0a2c3e..cf35feb5ba 100644 --- a/src/Documentation/Markdown/MarkdownDocumentationWriter.cs +++ b/src/Documentation/Markdown/MarkdownDocumentationWriter.cs @@ -9,7 +9,7 @@ public abstract class MarkdownDocumentationWriter : DocumentationWriter { private readonly MarkdownWriter _writer; - public MarkdownDocumentationWriter(DocumentationContext context, MarkdownWriter writer) : base(context) + protected MarkdownDocumentationWriter(DocumentationContext context, MarkdownWriter writer) : base(context) { _writer = writer; }