Skip to content

Commit

Permalink
Basic support for top-level inheritdoc elements. (#1178)
Browse files Browse the repository at this point in the history
* Basic support for top-level inheritdoc elements.
Does not support inheritdoc elements nested inside other elements.
Does not support any attributes on inheritdoc elements.
Fixes #247.

* Address code review feedback.
Specifically, fix spelling mistakes and remove and unused field.
  • Loading branch information
AustinWise authored and vicancy committed Jan 22, 2017
1 parent 82eadcb commit a2ba6d5
Show file tree
Hide file tree
Showing 11 changed files with 271 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,14 @@ public class ApiParameter
[JsonProperty("attributes")]
[MergeOption(MergeOption.Ignore)]
public List<AttributeInfo> Attributes { get; set; }

public void CopyInheritedData(ApiParameter src)
{
if (src == null)
throw new ArgumentNullException(nameof(src));

if (Description == null)
Description = src.Description;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,10 @@ public class ExceptionInfo
[JsonProperty("description")]
[MarkdownContent]
public string Description { get; set; }

public ExceptionInfo Clone()
{
return (ExceptionInfo)MemberwiseClone();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public class LinkInfo
[YamlMember(Alias = "altText")]
[JsonProperty("altText")]
public string AltText { get; set; }

public LinkInfo Clone()
{
return (LinkInfo)MemberwiseClone();
}
}

[Serializable]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace Microsoft.DocAsCode.Metadata.ManagedReference
{
using System;
using System.Collections.Generic;
using System.Linq;

using Newtonsoft.Json;
using YamlDotNet.Serialization;
Expand Down Expand Up @@ -154,6 +155,10 @@ public class MetadataItem : ICloneable
[JsonProperty("references")]
public Dictionary<string, ReferenceItem> References { get; set; }

[YamlIgnore]
[JsonIgnore]
public bool IsInheritDoc { get; set; }

[YamlIgnore]
[JsonIgnore]
public TripleSlashCommentModel CommentModel { get; set; }
Expand All @@ -167,5 +172,30 @@ public object Clone()
{
return MemberwiseClone();
}

public void CopyInheritedData(MetadataItem src)
{
if (src == null)
throw new ArgumentNullException(nameof(src));

if (Summary == null)
Summary = src.Summary;
if (Remarks == null)
Remarks = src.Remarks;

if (Exceptions == null && src.Exceptions != null)
Exceptions = src.Exceptions.Select(e => e.Clone()).ToList();
if (Sees == null && src.Sees != null)
Sees = src.Sees.Select(s => s.Clone()).ToList();
if (SeeAlsos == null && src.SeeAlsos != null)
SeeAlsos = src.SeeAlsos.Select(s => s.Clone()).ToList();
if (Examples == null && src.Examples != null)
Examples = new List<string>(src.Examples);

if (CommentModel != null && src.CommentModel != null)
CommentModel.CopyInheritedData(src.CommentModel);
if (Syntax != null && src.Syntax != null)
Syntax.CopyInheritedData(src.Syntax);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

namespace Microsoft.DocAsCode.Metadata.ManagedReference
{
using System;
using System.Collections.Generic;

using Newtonsoft.Json;
Expand All @@ -27,5 +28,31 @@ public class SyntaxDetail
[YamlMember(Alias = "return")]
[JsonProperty("return")]
public ApiParameter Return { get; set; }

public void CopyInheritedData(SyntaxDetail src)
{
if (src == null)
throw new ArgumentNullException(nameof(src));

CopyInheritedParameterList(Parameters, src.Parameters);
CopyInheritedParameterList(TypeParameters, src.TypeParameters);
if (Return != null && src.Return != null)
Return.CopyInheritedData(src.Return);
}

static void CopyInheritedParameterList(List<ApiParameter> dest, List<ApiParameter> src)
{
if (dest == null || src == null || dest.Count != src.Count)
return;
for (int ndx = 0; ndx < dest.Count; ndx++)
{
var myParam = dest[ndx];
var srcParam = src[ndx];
if (myParam.Name == srcParam.Name && myParam.Type == srcParam.Type)
{
myParam.CopyInheritedData(srcParam);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class TripleSlashCommentModel
public List<string> Examples { get; private set; }
public Dictionary<string, string> Parameters { get; private set; }
public Dictionary<string, string> TypeParameters { get; private set; }
public bool IsInheritDoc { get; private set; }

private TripleSlashCommentModel(string xml, SyntaxLanguage language, ITripleSlashCommentParserContext context)
{
Expand All @@ -59,6 +60,7 @@ private TripleSlashCommentModel(string xml, SyntaxLanguage language, ITripleSlas
Examples = GetExamples(nav, context);
Parameters = GetParameters(nav, context);
TypeParameters = GetTypeParameters(nav, context);
IsInheritDoc = GetIsInheritDoc(nav, context);
}

public static TripleSlashCommentModel CreateModel(string xml, SyntaxLanguage language, ITripleSlashCommentParserContext context)
Expand All @@ -82,6 +84,32 @@ public static TripleSlashCommentModel CreateModel(string xml, SyntaxLanguage lan
}
}

public void CopyInheritedData(TripleSlashCommentModel src)
{
if (src == null)
throw new ArgumentNullException(nameof(src));

if (Summary == null)
Summary = src.Summary;
if (Remarks == null)
Remarks = src.Remarks;
if (Returns == null)
Returns = src.Returns;

if (Exceptions == null && src.Exceptions != null)
Exceptions = src.Exceptions.Select(e => e.Clone()).ToList();
if (Sees == null && src.Sees != null)
Sees = src.Sees.Select(s => s.Clone()).ToList();
if (SeeAlsos == null && src.SeeAlsos != null)
SeeAlsos = src.SeeAlsos.Select(s => s.Clone()).ToList();
if (Examples == null && src.Examples != null)
Examples = new List<string>(src.Examples);
if (Parameters == null && src.Parameters != null)
Parameters = new Dictionary<string, string>(src.Parameters);
if (TypeParameters == null && src.TypeParameters != null)
TypeParameters = new Dictionary<string, string>(src.TypeParameters);
}

public string GetParameter(string name)
{
if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));
Expand Down Expand Up @@ -215,6 +243,22 @@ private List<string> GetExamples(XPathNavigator nav, ITripleSlashCommentParserCo
return GetMultipleExampleNodes(nav, "/member/example").ToList();
}

private bool GetIsInheritDoc(XPathNavigator nav, ITripleSlashCommentParserContext context)
{
var node = nav.SelectSingleNode("/member/inheritdoc");
if (node == null)
return false;
if (node.HasAttributes)
{
//The Sandcastle implementation of <inheritdoc /> supports two attributes: 'cref' and 'select'.
//These attributes allow changing the source of the inherited doc and controlling what is inherited.
//Until these attributes are supported, ignoring inheritdoc elements with attributes, so as not to misinterpret them.
Logger.LogWarning("Attributes on <inheritdoc /> elements are not supported; inheritdoc element will be ignored.");
return false;
}
return true;
}

private Dictionary<string, string> GetListContent(XPathNavigator navigator, string xpath, string contentType, ITripleSlashCommentParserContext context)
{
var iterator = navigator.Select(xpath);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.DocAsCode.Metadata.ManagedReference
{
using Microsoft.DocAsCode.Common;
using Microsoft.DocAsCode.DataContracts.ManagedReference;
using System.Collections.Generic;
using System.Diagnostics;

/// <summary>
/// Copies doc comments to items marked with 'inheritdoc' from interfaces and base classes.
/// </summary>
public class CopyInherited : IResolverPipeline
{
public void Run(MetadataModel yaml, ResolverContext context)
{
TreeIterator.Preorder(yaml.TocYamlViewModel, null,
s => s.IsInvalid ? null : s.Items,
(current, parent) =>
{
if (current.IsInheritDoc)
InheritDoc(current, context);
return true;
});
}

static void InheritDoc(MetadataItem dest, ResolverContext context)
{
dest.IsInheritDoc = false;

switch (dest.Type)
{
case MemberType.Constructor:
if (dest.Parent == null || dest.Syntax == null || dest.Syntax.Parameters == null)
return;
Debug.Assert(dest.Parent.Type == MemberType.Class);

//try to find the base class
if (dest.Parent.Inheritance?.Count == 0)
return;
MetadataItem baseClass;
if (!context.Members.TryGetValue(dest.Parent.Inheritance[dest.Parent.Inheritance.Count - 1], out baseClass))
return;
if (baseClass.Items == null)
return;

//look a constructor in the base class which has a matching signature
foreach (var ctor in baseClass.Items)
{
if (ctor.Type != MemberType.Constructor)
continue;
if (ctor.Syntax == null || ctor.Syntax.Parameters == null)
continue;
if (ctor.Syntax.Parameters.Count != dest.Syntax.Parameters.Count)
continue;

bool parametersMatch = true;
for (int ndx = 0; ndx < dest.Syntax.Parameters.Count; ndx++)
{
var myParam = dest.Syntax.Parameters[ndx];
var baseParam = ctor.Syntax.Parameters[ndx];
if (myParam.Name != baseParam.Name)
parametersMatch = false;
if (myParam.Type != baseParam.Type)
parametersMatch = false;
}

if (parametersMatch)
{
Copy(dest, ctor, context);
return;
}
}
break;

case MemberType.Method:
case MemberType.Property:
case MemberType.Event:
Copy(dest, dest.Overridden, context);
if (dest.Implements != null)
{
foreach (var item in dest.Implements)
{
Copy(dest, item, context);
}
}
break;

case MemberType.Class:
if (dest.Inheritance.Count != 0)
{
Copy(dest, dest.Inheritance[dest.Inheritance.Count - 1], context);
}
if (dest.Implements != null)
{
foreach (var item in dest.Implements)
{
Copy(dest, item, context);
}
}
break;
}
}

static void Copy(MetadataItem dest, string srcName, ResolverContext context)
{
MetadataItem src;
if (string.IsNullOrEmpty(srcName) || !context.Members.TryGetValue(srcName, out src))
return;

Copy(dest, src, context);
}

static void Copy(MetadataItem dest, MetadataItem src, ResolverContext context)
{
if (src.IsInheritDoc)
{
InheritDoc(src, context);
}

dest.CopyInheritedData(src);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ public class ResolverContext
public bool PreserveRawInlineComments { get; set; }

public Dictionary<string, ReferenceItem> References { get; set; }

public Dictionary<string, MetadataItem> Members { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public static class YamlMetadataResolver
{
new LayoutCheckAndCleanup(),
new SetParent(),
new CopyInherited(),
new ResolveReference(),
new NormalizeSyntax(),
new BuildMembers(),
Expand Down Expand Up @@ -46,6 +47,7 @@ public static MetadataModel ResolveMetadata(
{
ApiFolder = apiFolder,
References = allReferences,
Members = allMembers,
PreserveRawInlineComments = preserveRawInlineComments,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public static void FeedComments(MetadataItem item, ITripleSlashCommentParserCont
item.Sees = commentModel.Sees;
item.SeeAlsos = commentModel.SeeAlsos;
item.Examples = commentModel.Examples;
item.IsInheritDoc = commentModel.IsInheritDoc;
item.CommentModel = commentModel;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ Check empty code.


var commentModel = TripleSlashCommentModel.CreateModel(input, SyntaxLanguage.CSharp, context);
Assert.False(commentModel.IsInheritDoc, nameof(commentModel.IsInheritDoc));

var summary = commentModel.Summary;
Assert.Equal(@"
Expand Down Expand Up @@ -183,5 +184,25 @@ Check empty code.
Assert.Equal("http://www.bing.com", seeAlsos[2].AltText);
Assert.Equal("http://www.bing.com", seeAlsos[2].LinkId);
}

[Trait("Related", "TripleSlashComments")]
[Fact]
public void InheritDoc()
{
const string input = @"
<member name=""M:ClassLibrary1.MyClass.DoThing"">
<inheritdoc />
</member>";
var context = new TripleSlashCommentParserContext
{
AddReferenceDelegate = null,
Normalize = true,
PreserveRawInlineComments = false,
};

var commentModel = TripleSlashCommentModel.CreateModel(input, SyntaxLanguage.CSharp, context);
Assert.True(commentModel.IsInheritDoc);

}
}
}

0 comments on commit a2ba6d5

Please sign in to comment.