diff --git a/src/Microsoft.DocAsCode.Dotnet/ExtractMetadata/ExtractMetadataWorker.cs b/src/Microsoft.DocAsCode.Dotnet/ExtractMetadata/ExtractMetadataWorker.cs index e55fb632533..102e2038c9d 100644 --- a/src/Microsoft.DocAsCode.Dotnet/ExtractMetadata/ExtractMetadataWorker.cs +++ b/src/Microsoft.DocAsCode.Dotnet/ExtractMetadata/ExtractMetadataWorker.cs @@ -144,11 +144,13 @@ await LoadCompilationFromProject(project.AbsolutePath) is { } compilation) var projectMetadataList = new List(); var extensionMethods = assemblies.SelectMany(assembly => assembly.Item1.FindExtensionMethods()).ToArray(); var filter = new SymbolFilter(_config, _options); + var allAssemblies = new HashSet(assemblies.Select(a => a.Item1), SymbolEqualityComparer.Default); foreach (var (assembly, compilation) in assemblies) { Logger.LogInfo($"Processing {assembly.Name}"); - var projectMetadata = assembly.Accept(new SymbolVisitorAdapter(compilation, new(compilation), _config, filter, extensionMethods)); + var projectMetadata = assembly.Accept(new SymbolVisitorAdapter( + compilation, new(compilation, _config.MemberLayout, allAssemblies), _config, filter, extensionMethods)); if (projectMetadata != null) projectMetadataList.Add(projectMetadata); diff --git a/src/Microsoft.DocAsCode.Dotnet/SymbolFormatter.cs b/src/Microsoft.DocAsCode.Dotnet/SymbolFormatter.cs index 7b9ea57044d..4db2d6abed5 100644 --- a/src/Microsoft.DocAsCode.Dotnet/SymbolFormatter.cs +++ b/src/Microsoft.DocAsCode.Dotnet/SymbolFormatter.cs @@ -108,7 +108,8 @@ public static ImmutableArray GetSyntaxParts(ISymbol symbol, S } } - public static List ToLinkItems(this ImmutableArray parts, Compilation compilation, SyntaxLanguage language, bool overload) + public static List ToLinkItems(this ImmutableArray parts, + Compilation compilation, MemberLayout memberLayout, HashSet allAssemblies, bool overload) { var result = new List(); foreach (var part in parts) @@ -133,7 +134,7 @@ LinkItem ToLinkItem(SymbolDisplayPart part) { Name = overload ? VisitorHelper.GetOverloadId(symbol) : VisitorHelper.GetId(symbol), DisplayName = part.ToString(), - Href = SymbolUrlResolver.GetSymbolUrl(symbol, compilation), + Href = SymbolUrlResolver.GetSymbolUrl(symbol, compilation, memberLayout, allAssemblies), IsExternalPath = symbol.IsExtern || symbol.DeclaringSyntaxReferences.Length == 0, }; } diff --git a/src/Microsoft.DocAsCode.Dotnet/SymbolHelper.cs b/src/Microsoft.DocAsCode.Dotnet/SymbolHelper.cs index dbbfbb6b6a3..363333701d0 100644 --- a/src/Microsoft.DocAsCode.Dotnet/SymbolHelper.cs +++ b/src/Microsoft.DocAsCode.Dotnet/SymbolHelper.cs @@ -9,7 +9,7 @@ internal static class SymbolHelper public static MetadataItem? GenerateMetadataItem(this IAssemblySymbol assembly, Compilation compilation, ExtractMetadataConfig? config = null, DotnetApiOptions? options = null, IMethodSymbol[]? extensionMethods = null) { config ??= new(); - return assembly.Accept(new SymbolVisitorAdapter(compilation, new(compilation), config, new(config, options ?? new()), extensionMethods)); + return assembly.Accept(new SymbolVisitorAdapter(compilation, new(compilation, MemberLayout.SamePage, new(new[] { assembly }, SymbolEqualityComparer.Default)), config, new(config, options ?? new()), extensionMethods)); } public static bool IsInstanceInterfaceMember(this ISymbol symbol) diff --git a/src/Microsoft.DocAsCode.Dotnet/SymbolUrlResolver.cs b/src/Microsoft.DocAsCode.Dotnet/SymbolUrlResolver.cs index 22fda2c32d8..4420ce0ac16 100644 --- a/src/Microsoft.DocAsCode.Dotnet/SymbolUrlResolver.cs +++ b/src/Microsoft.DocAsCode.Dotnet/SymbolUrlResolver.cs @@ -1,4 +1,5 @@ -using Microsoft.CodeAnalysis; +using System.Text.RegularExpressions; +using Microsoft.CodeAnalysis; #nullable enable @@ -6,9 +7,34 @@ namespace Microsoft.DocAsCode.Dotnet; internal static partial class SymbolUrlResolver { - public static string? GetSymbolUrl(ISymbol symbol, Compilation compilation) + public static string? GetSymbolUrl(ISymbol symbol, Compilation compilation, MemberLayout memberLayout, HashSet allAssemblies) { - return GetMicrosoftLearnUrl(symbol) + return GetDocfxUrl(symbol, memberLayout, allAssemblies) + ?? GetMicrosoftLearnUrl(symbol) ?? GetPdbSourceLinkUrl(compilation, symbol); } + + internal static string? GetDocfxUrl(ISymbol symbol, MemberLayout memberLayout, HashSet allAssemblies) + { + if (symbol.ContainingAssembly is null || !allAssemblies.Contains(symbol.ContainingAssembly)) + return null; + + var commentId = symbol.GetDocumentationCommentId(); + if (commentId is null) + return null; + + var parts = commentId.Split(':'); + var type = parts[0]; + var uid = parts[1]; + + return type switch + { + "!" => null, + "N" or "T" => $"{uid.Replace('`', '-')}.html", + "M" or "F" or "P" or "E" => memberLayout is MemberLayout.SeparatePages && !symbol.IsEnumMember() + ? $"{VisitorHelper.GetId(symbol).Replace('`', '-')}.html" + : $"{VisitorHelper.GetId(symbol.ContainingType).Replace('`', '-')}.html#{Regex.Replace(uid, @"/\W/", "_")}", + _ => throw new NotSupportedException($"Unknown comment ID format '{type}"), + }; + } } diff --git a/src/Microsoft.DocAsCode.Dotnet/Visitors/YamlModelGenerator.cs b/src/Microsoft.DocAsCode.Dotnet/Visitors/YamlModelGenerator.cs index 2fd38359821..e0191e9731d 100644 --- a/src/Microsoft.DocAsCode.Dotnet/Visitors/YamlModelGenerator.cs +++ b/src/Microsoft.DocAsCode.Dotnet/Visitors/YamlModelGenerator.cs @@ -9,10 +9,14 @@ namespace Microsoft.DocAsCode.Dotnet; internal class YamlModelGenerator { private readonly Compilation _compilation; + private readonly MemberLayout _memberLayout; + private readonly HashSet _allAssemblies; - public YamlModelGenerator(Compilation compilation) + public YamlModelGenerator(Compilation compilation, MemberLayout memberLayout, HashSet allAssemblies) { _compilation = compilation; + _memberLayout = memberLayout; + _allAssemblies = allAssemblies; } public void DefaultVisit(ISymbol symbol, MetadataItem item) @@ -35,9 +39,9 @@ public void GenerateReference(ISymbol symbol, ReferenceItem reference, bool asOv if (!reference.QualifiedNameParts.ContainsKey(SyntaxLanguage.CSharp)) reference.QualifiedNameParts.Add(SyntaxLanguage.CSharp, new()); - reference.NameParts[SyntaxLanguage.CSharp] = SymbolFormatter.GetNameParts(symbol, SyntaxLanguage.CSharp, nullableReferenceType: false, asOverload).ToLinkItems(_compilation, SyntaxLanguage.CSharp, asOverload); - reference.NameWithTypeParts[SyntaxLanguage.CSharp] = SymbolFormatter.GetNameWithTypeParts(symbol, SyntaxLanguage.CSharp, nullableReferenceType: false, asOverload).ToLinkItems(_compilation, SyntaxLanguage.CSharp, asOverload); - reference.QualifiedNameParts[SyntaxLanguage.CSharp] = SymbolFormatter.GetQualifiedNameParts(symbol, SyntaxLanguage.CSharp, nullableReferenceType: false, asOverload).ToLinkItems(_compilation, SyntaxLanguage.CSharp, asOverload); + reference.NameParts[SyntaxLanguage.CSharp] = SymbolFormatter.GetNameParts(symbol, SyntaxLanguage.CSharp, nullableReferenceType: false, asOverload).ToLinkItems(_compilation, _memberLayout, _allAssemblies, asOverload); + reference.NameWithTypeParts[SyntaxLanguage.CSharp] = SymbolFormatter.GetNameWithTypeParts(symbol, SyntaxLanguage.CSharp, nullableReferenceType: false, asOverload).ToLinkItems(_compilation, _memberLayout, _allAssemblies, asOverload); + reference.QualifiedNameParts[SyntaxLanguage.CSharp] = SymbolFormatter.GetQualifiedNameParts(symbol, SyntaxLanguage.CSharp, nullableReferenceType: false, asOverload).ToLinkItems(_compilation, _memberLayout, _allAssemblies, asOverload); if (!reference.NameParts.ContainsKey(SyntaxLanguage.VB)) reference.NameParts.Add(SyntaxLanguage.VB, new()); @@ -46,9 +50,9 @@ public void GenerateReference(ISymbol symbol, ReferenceItem reference, bool asOv if (!reference.QualifiedNameParts.ContainsKey(SyntaxLanguage.VB)) reference.QualifiedNameParts.Add(SyntaxLanguage.VB, new()); - reference.NameParts[SyntaxLanguage.VB] = SymbolFormatter.GetNameParts(symbol, SyntaxLanguage.VB, nullableReferenceType: false, asOverload).ToLinkItems(_compilation, SyntaxLanguage.VB, asOverload); - reference.NameWithTypeParts[SyntaxLanguage.VB] = SymbolFormatter.GetNameWithTypeParts(symbol, SyntaxLanguage.VB, nullableReferenceType: false, asOverload).ToLinkItems(_compilation, SyntaxLanguage.VB, asOverload); - reference.QualifiedNameParts[SyntaxLanguage.VB] = SymbolFormatter.GetQualifiedNameParts(symbol, SyntaxLanguage.VB, nullableReferenceType: false, asOverload).ToLinkItems(_compilation, SyntaxLanguage.VB, asOverload); + reference.NameParts[SyntaxLanguage.VB] = SymbolFormatter.GetNameParts(symbol, SyntaxLanguage.VB, nullableReferenceType: false, asOverload).ToLinkItems(_compilation, _memberLayout, _allAssemblies, asOverload); + reference.NameWithTypeParts[SyntaxLanguage.VB] = SymbolFormatter.GetNameWithTypeParts(symbol, SyntaxLanguage.VB, nullableReferenceType: false, asOverload).ToLinkItems(_compilation, _memberLayout, _allAssemblies, asOverload); + reference.QualifiedNameParts[SyntaxLanguage.VB] = SymbolFormatter.GetQualifiedNameParts(symbol, SyntaxLanguage.VB, nullableReferenceType: false, asOverload).ToLinkItems(_compilation, _memberLayout, _allAssemblies, asOverload); } public void GenerateSyntax(ISymbol symbol, SyntaxDetail syntax, SymbolFilter filter)