Skip to content

Commit

Permalink
Merge pull request #3498 from aws/muhamoth/DOTNET-7684-Support-More-C…
Browse files Browse the repository at this point in the history
…omplicated-inheritdoc-Syntax

Docs: [API Ref] Support More Complicated <inheritdoc/> Syntax
  • Loading branch information
muhammad-othman authored Oct 4, 2024
2 parents bb6db11 + ec27560 commit bcd8eb9
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 50 deletions.
137 changes: 104 additions & 33 deletions docgenerator/SDKDocGeneratorLib/NDocUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public static class NDocUtilities
public const string hrefAttributeName = "href";
public const string nameAttributeName = "name";
public const string targetAttributeName = "target";
public const string pathAttributeName = "path";

// inner attribute of a cross reference tag we're interested in
public static readonly string innerCrefAttributeText = crefAttributeName + "=\"";
Expand Down Expand Up @@ -107,23 +108,23 @@ private static IDictionary<string, XElement> CreateNDocTable(string filePath, st
#endregion


public static XElement FindDocumentation(AbstractWrapper wrapper)
public static XElement FindDocumentation(AbstractWrapper wrapper, AbstractTypeProvider typeProvider)
{
var ndoc = GetDocumentationInstance(wrapper.DocId);
return FindDocumentation(ndoc, wrapper);
return FindDocumentation(ndoc, wrapper, typeProvider);
}

public static XElement FindDocumentation(IDictionary<string, XElement> ndoc, AbstractWrapper wrapper)
public static XElement FindDocumentation(IDictionary<string, XElement> ndoc, AbstractWrapper wrapper, AbstractTypeProvider typeProvider)
{
if (ndoc == null)
return null;

if (wrapper is TypeWrapper)
return FindDocumentation(ndoc, (TypeWrapper)wrapper);
return FindDocumentation(ndoc, (TypeWrapper)wrapper, typeProvider);
if (wrapper is PropertyInfoWrapper)
return FindDocumentation(ndoc, (PropertyInfoWrapper)wrapper);
return FindDocumentation(ndoc, (PropertyInfoWrapper)wrapper, typeProvider);
if (wrapper is MethodInfoWrapper)
return FindDocumentation(ndoc, (MethodInfoWrapper)wrapper);
return FindDocumentation(ndoc, (MethodInfoWrapper)wrapper, typeProvider);
if (wrapper is ConstructorInfoWrapper)
return FindDocumentation(ndoc, (ConstructorInfoWrapper)wrapper);
if (wrapper is FieldInfoWrapper)
Expand Down Expand Up @@ -158,7 +159,7 @@ public static XElement FindFieldDocumentation(IDictionary<string, XElement> ndoc
return element;
}

public static XElement FindDocumentation(IDictionary<string, XElement> ndoc, TypeWrapper type)
public static XElement FindDocumentation(IDictionary<string, XElement> ndoc, TypeWrapper type, AbstractTypeProvider typeProvider)
{
var signature = "T:" + type.FullName;
XElement element;
Expand All @@ -170,36 +171,35 @@ public static XElement FindDocumentation(IDictionary<string, XElement> ndoc, Typ
if (inheritdocElement == null)
return element;

if (inheritdocElement.Attribute("cref") != null || inheritdocElement.Attribute("path") != null)
if (inheritdocElement.Attribute(crefAttributeName) != null)
{
Trace.WriteLine($"Skipping following <inheritdoc> for {signature} since cref and/or path are not supported yet.");
return element;
return FindCrefDocumentation(ndoc, typeProvider, inheritdocElement);
}

if (type.BaseType.FullName != "System.Object") // we never expect to inherit class-level docs from here
{
var baseTypeDocs = FindDocumentation(ndoc, type.BaseType);
var baseTypeDocs = FindDocumentation(ndoc, type.BaseType, typeProvider);

if (baseTypeDocs != null)
{
return baseTypeDocs;
return ExtractPathDocumentation(inheritdocElement, baseTypeDocs);
}
}

foreach (var baseInterface in type.GetInterfaces())
{
var interfaceDocs = FindDocumentation(ndoc, baseInterface);
var interfaceDocs = FindDocumentation(ndoc, baseInterface, typeProvider);

if (interfaceDocs != null)
{
return interfaceDocs;
return ExtractPathDocumentation(inheritdocElement, interfaceDocs);
}
}

return element;
}

public static XElement FindDocumentation(IDictionary<string, XElement> ndoc, PropertyInfoWrapper info)
public static XElement FindDocumentation(IDictionary<string, XElement> ndoc, PropertyInfoWrapper info, AbstractTypeProvider typeProvider)
{
var type = info.DeclaringType;
var signature = string.Format("P:{0}.{1}", type.FullName, info.Name);
Expand All @@ -212,23 +212,22 @@ public static XElement FindDocumentation(IDictionary<string, XElement> ndoc, Pro
if (inheritdocElement == null)
return element;

if (inheritdocElement.Attribute("cref") != null || inheritdocElement.Attribute("path") != null)
if (inheritdocElement.Attribute(crefAttributeName) != null)
{
Trace.WriteLine($"Skipping following <inheritdoc> for {signature} since cref and/or path are not supported yet.");
return element;
return FindCrefDocumentation(ndoc, typeProvider, inheritdocElement);
}

var baseTypeMatchingProperties = info.DeclaringType.BaseType.GetProperties().Where(property => property.Name.Equals(info.Name, StringComparison.OrdinalIgnoreCase));

if (baseTypeMatchingProperties.Count() == 1)
return FindDocumentation(ndoc, baseTypeMatchingProperties.First());
return ExtractPathDocumentation(inheritdocElement, FindDocumentation(ndoc, baseTypeMatchingProperties.First(), typeProvider));

foreach (var baseInterface in info.DeclaringType.GetInterfaces())
{
var interfaceMatchingProperties = baseInterface.GetProperties().Where(property => property.Name.Equals(info.Name, StringComparison.OrdinalIgnoreCase));

if (interfaceMatchingProperties.Count() == 1)
return FindDocumentation(ndoc, interfaceMatchingProperties.First());
return ExtractPathDocumentation(inheritdocElement, FindDocumentation(ndoc, interfaceMatchingProperties.First(), typeProvider));
}

return element;
Expand Down Expand Up @@ -316,7 +315,7 @@ private static void DetermineParameterName(TypeWrapper parameterTypeInfo, String
}
}

public static XElement FindDocumentation(IDictionary<string, XElement> ndoc, MethodInfoWrapper info)
public static XElement FindDocumentation(IDictionary<string, XElement> ndoc, MethodInfoWrapper info, AbstractTypeProvider typeProvider)
{
var signature = DetermineNDocNameLookupSignature(info);

Expand All @@ -329,23 +328,22 @@ public static XElement FindDocumentation(IDictionary<string, XElement> ndoc, Met
if (inheritdocElement == null)
return element;

if (inheritdocElement.Attribute("cref") != null || inheritdocElement.Attribute("path") != null)
if (inheritdocElement.Attribute(crefAttributeName) != null)
{
Trace.WriteLine($"Skipping following <inheritdoc> for {signature} since cref and/or path are not supported yet.");
return element;
return FindCrefDocumentation(ndoc, typeProvider, inheritdocElement);
}

var baseTypeMatchingMethods = info.DeclaringType.BaseType.GetMethodsToDocument().Where(method => method.FullName.Equals(info.FullName, StringComparison.OrdinalIgnoreCase));

if (baseTypeMatchingMethods.Count() == 1)
return FindDocumentation(ndoc, baseTypeMatchingMethods.First());
return ExtractPathDocumentation(inheritdocElement, FindDocumentation(ndoc, baseTypeMatchingMethods.First(), typeProvider));

foreach (var baseInterface in info.DeclaringType.GetInterfaces())
{
var interfaceMatchingMethods = baseInterface.GetMethodsToDocument().Where(method => method.FullName.Equals(info.FullName, StringComparison.OrdinalIgnoreCase));

if (interfaceMatchingMethods.Count() == 1)
return FindDocumentation(ndoc, interfaceMatchingMethods.First());
return ExtractPathDocumentation(inheritdocElement, FindDocumentation(ndoc, interfaceMatchingMethods.First(), typeProvider));
}

return element;
Expand Down Expand Up @@ -410,7 +408,7 @@ public static string FindReturnDocumentation(XElement ndoc)
/// <param name="ndoc"></param>
/// <param name="info"></param>
/// <returns></returns>
public static XElement FindDocumentationAsync(IDictionary<string, XElement> ndoc, MethodInfoWrapper info)
public static XElement FindDocumentationAsync(IDictionary<string, XElement> ndoc, MethodInfoWrapper info, AbstractTypeProvider typeProvider)
{
if (ndoc == null)
return null;
Expand Down Expand Up @@ -459,23 +457,22 @@ public static XElement FindDocumentationAsync(IDictionary<string, XElement> ndoc
if (inheritdocElement == null)
return element;

if (inheritdocElement.Attribute("cref") != null || inheritdocElement.Attribute("path") != null)
if (inheritdocElement.Attribute(crefAttributeName) != null)
{
Trace.WriteLine($"Skipping following <inheritdoc> for {signature} since cref and/or path are not supported yet.");
return element;
return FindCrefDocumentation(ndoc, typeProvider, inheritdocElement);
}

var baseTypeMatchingMethods = info.DeclaringType.BaseType.GetMethodsToDocument().Where(method => method.FullName.Equals(info.FullName, StringComparison.OrdinalIgnoreCase));

if (baseTypeMatchingMethods.Count() == 1)
return FindDocumentation(ndoc, baseTypeMatchingMethods.First());
return ExtractPathDocumentation(inheritdocElement, FindDocumentation(ndoc, baseTypeMatchingMethods.First(), typeProvider));

foreach (var baseInterface in info.DeclaringType.GetInterfaces())
{
var interfaceMatchingMethods = baseInterface.GetMethodsToDocument().Where(method => method.FullName.Equals(info.FullName, StringComparison.OrdinalIgnoreCase));

if (interfaceMatchingMethods.Count() == 1)
return FindDocumentation(ndoc, interfaceMatchingMethods.First());
return ExtractPathDocumentation(inheritdocElement, FindDocumentation(ndoc, interfaceMatchingMethods.First(), typeProvider));
}

return element;
Expand All @@ -495,6 +492,80 @@ public static string TransformDocumentationToHTML(XElement element, string rootN
return DocBlobToHTML(rootNode, typeProvider, version);
}

private static XElement FindCrefDocumentation(IDictionary<string, XElement> ndoc, AbstractTypeProvider typeProvider, XElement inheritdocElement)
{
var crefValue = inheritdocElement.Attribute(crefAttributeName)?.Value;
if (crefValue == null)
return inheritdocElement;

var attributParts = crefValue.Split(':');
if (attributParts.Length != 2)
return inheritdocElement;

var attributePart = attributParts[1];
var targetType = typeProvider.GetType(attributePart);

XElement targetDocs = null;

if (targetType == null) // the cref attribute is pointing to a method or a property
{
if (attributePart.LastIndexOf('.') < 0)
return inheritdocElement;

var typeName = attributePart.Substring(0, attributePart.LastIndexOf('.'));
targetType = typeProvider.GetType(typeName);

if (targetType == null)
return inheritdocElement;

var typeMemberName = attributePart.Substring(attributePart.LastIndexOf('.') + 1);

var matchingMethods = targetType.GetMethodsToDocument().Where(method => method.FullName.Equals(attributePart, StringComparison.OrdinalIgnoreCase));
var matchingProperties = targetType.GetProperties().Where(property => property.Name.Equals(typeMemberName, StringComparison.OrdinalIgnoreCase));

if (matchingMethods.Count() == 1)
targetDocs = FindDocumentation(ndoc, matchingMethods.First(), typeProvider);
else if (matchingProperties.Count() == 1)
targetDocs = FindDocumentation(ndoc, matchingProperties.First(), typeProvider);
else
return inheritdocElement;
}
else
{
targetDocs = FindDocumentation(ndoc, targetType, typeProvider);
}

if (targetDocs != null)
{
return ExtractPathDocumentation(inheritdocElement, targetDocs);
}

return inheritdocElement;
}

private static XElement ExtractPathDocumentation(XElement inheritdocElement, XElement docElement)
{
if (inheritdocElement.Attribute(pathAttributeName) == null)
return docElement;

var pathValue = inheritdocElement.Attribute(pathAttributeName).Value;

var targetPathDocs = docElement.XPathSelectElement(pathValue);

if (targetPathDocs != null)
{
var docElementCopy = new XElement(docElement);
docElementCopy.RemoveNodes();

// Wrap the targetPathDocs in summary tag to be picked up by the html transformer.
docElementCopy.Add(new XElement("summary", targetPathDocs));

return docElementCopy;
}

return docElement;
}

private static string SeeAlsoElementToHTML(XElement rootNode, AbstractTypeProvider typeProvider, FrameworkVersion version)
{
var reader = rootNode.CreateReader();
Expand All @@ -508,7 +579,7 @@ private static string SeeAlsoElementToHTML(XElement rootNode, AbstractTypeProvid
content += string.Format(@"<div><a href=""{0}"" target=""_parent"" rel=""noopener noreferrer"">{1}</a></div>", href.Value, innerXml);
}

var cref = rootNode.Attribute("cref");
var cref = rootNode.Attribute(crefAttributeName);
if (cref != null)
{
content += BaseWriter.CreateCrossReferenceTagReplacement(typeProvider, cref.Value, version);
Expand Down
8 changes: 4 additions & 4 deletions docgenerator/SDKDocGeneratorLib/Writers/BaseWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -517,10 +517,10 @@ protected void AddVersionInformation(TextWriter writer, AbstractWrapper wrapper)
{
AddSectionHeader(writer, "Version Information");

var docs472 = NDocUtilities.FindDocumentation(Artifacts.NDocForPlatform("net472"), wrapper);
var docsCore20 = NDocUtilities.FindDocumentation(Artifacts.NDocForPlatform("netstandard2.0"), wrapper);
var docsNetCoreApp31 = NDocUtilities.FindDocumentation(Artifacts.NDocForPlatform("netcoreapp3.1"), wrapper);
var docsNet80 = NDocUtilities.FindDocumentation(Artifacts.NDocForPlatform("net8.0"), wrapper);
var docs472 = NDocUtilities.FindDocumentation(Artifacts.NDocForPlatform("net472"), wrapper, TypeProvider);
var docsCore20 = NDocUtilities.FindDocumentation(Artifacts.NDocForPlatform("netstandard2.0"), wrapper, TypeProvider);
var docsNetCoreApp31 = NDocUtilities.FindDocumentation(Artifacts.NDocForPlatform("netcoreapp3.1"), wrapper, TypeProvider);
var docsNet80 = NDocUtilities.FindDocumentation(Artifacts.NDocForPlatform("net8.0"), wrapper, TypeProvider);

// If there is no documentation then assume it is available for all platforms.
var boolNoDocs = docs472 == null && docsCore20 == null && docsNetCoreApp31 == null
Expand Down
12 changes: 6 additions & 6 deletions docgenerator/SDKDocGeneratorLib/Writers/ClassWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ protected override void WriteContent(TextWriter writer)

protected override XElement GetSummaryDocumentation()
{
var element = NDocUtilities.FindDocumentation(this._versionType);
var element = NDocUtilities.FindDocumentation(this._versionType, TypeProvider);
return element;
}

Expand Down Expand Up @@ -155,7 +155,7 @@ void AddConstructor(TextWriter writer, ConstructorInfoWrapper info)

writer.WriteLine("<td>");

var docs = NDocUtilities.FindDocumentation(info);
var docs = NDocUtilities.FindDocumentation(info, TypeProvider);
var html = NDocUtilities.TransformDocumentationToHTML(docs, "summary", this.Artifacts.ManifestAssemblyContext.SdkAssembly, this._version);

writer.WriteLine(html);
Expand Down Expand Up @@ -206,7 +206,7 @@ void AddProperty(TextWriter writer, PropertyInfoWrapper propertyInfo)
html = string.Format("Inherited from {0}.{1}.", propertyInfo.DeclaringType.Namespace, propertyInfo.DeclaringType.Name);
}
else {
var docs = NDocUtilities.FindDocumentation(propertyInfo);
var docs = NDocUtilities.FindDocumentation(propertyInfo, TypeProvider);
html = NDocUtilities.TransformDocumentationToHTML(docs, "summary", Artifacts.ManifestAssemblyContext.SdkAssembly, this._version);
}

Expand Down Expand Up @@ -262,7 +262,7 @@ void AddMethod(TextWriter writer, MethodInfoWrapper info)
html = string.Format("Inherited from {0}.{1}.", info.DeclaringType.Namespace, info.DeclaringType.Name);
}
else {
var docs = NDocUtilities.FindDocumentation(info);
var docs = NDocUtilities.FindDocumentation(info, TypeProvider);
html = NDocUtilities.TransformDocumentationToHTML(docs, "summary", Artifacts.ManifestAssemblyContext.SdkAssembly, this._version);
}
writer.WriteLine(html);
Expand Down Expand Up @@ -310,7 +310,7 @@ void AddField(TextWriter writer, FieldInfoWrapper info)
html = string.Format("Inherited from {0}.{1}.", info.DeclaringType.Namespace, info.DeclaringType.Name);
}
else {
var docs = NDocUtilities.FindDocumentation(info);
var docs = NDocUtilities.FindDocumentation(info, TypeProvider);
html = NDocUtilities.TransformDocumentationToHTML(docs, "summary", Artifacts.ManifestAssemblyContext.SdkAssembly, this._version);
}

Expand Down Expand Up @@ -360,7 +360,7 @@ void AddEvent(TextWriter writer, EventInfoWrapper info)
html = string.Format("Inherited from {0}.{1}.", info.DeclaringType.Namespace, info.DeclaringType.Name);
}
else {
var docs = NDocUtilities.FindDocumentation(info);
var docs = NDocUtilities.FindDocumentation(info, TypeProvider);
html = NDocUtilities.TransformDocumentationToHTML(docs, "summary", Artifacts.ManifestAssemblyContext.SdkAssembly, this._version);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ protected override string GetMemberType()

protected override XElement GetSummaryDocumentation()
{
var element = NDocUtilities.FindDocumentation(this._constructorInfo);
var element = NDocUtilities.FindDocumentation(this._constructorInfo, TypeProvider);
return element;
}

Expand Down
2 changes: 1 addition & 1 deletion docgenerator/SDKDocGeneratorLib/Writers/EventWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ protected override string GetMemberType()

protected override XElement GetSummaryDocumentation()
{
var element = NDocUtilities.FindDocumentation(this._eventInfo);
var element = NDocUtilities.FindDocumentation(this._eventInfo, TypeProvider);
return element;
}

Expand Down
2 changes: 1 addition & 1 deletion docgenerator/SDKDocGeneratorLib/Writers/FieldWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ protected override string GetMemberType()

protected override XElement GetSummaryDocumentation()
{
var element = NDocUtilities.FindDocumentation(this._fieldInfo);
var element = NDocUtilities.FindDocumentation(this._fieldInfo, TypeProvider);
return element;
}

Expand Down
Loading

0 comments on commit bcd8eb9

Please sign in to comment.