Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve inheritdoc #5491

Merged
merged 3 commits into from
Jan 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Documentation/spec/triple_slash_comments_spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Custom tags
-------
### inheritdoc
`docfx` supports a subset of the [inheritdoc functionality available in Sandcastle](https://ewsoftware.github.io/XMLCommentsGuide/html/86453FFB-B978-4A2A-9EB5-70E118CA8073.htm). Specifically, it implements most of the "Top-Level Inheritance Rules". It does not implement:
* Support for the `cref` or `select` attributes.
* Support for the `select` attribute.
* Automatic inheritance of documentation for explicit interface implementations.
* Support for inline `inheritdoc` tags (i.e., an `inheritdoc` tag inside of an `example` tag).

1 change: 1 addition & 0 deletions RELEASENOTE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Version Notes (Current Version: v2.49)

v2.49(Pre-release)
-----------
1. Add `cref` attribute support (#1306) for inheritdoc and fix copying from templated sources (#1516).

v2.48.1
-----------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public class MetadataItem : ICloneable

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

[YamlIgnore]
[JsonIgnore]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public class TripleSlashCommentModel

public Dictionary<string, string> TypeParameters { get; private set; }

public bool IsInheritDoc { get; private set; }
public string InheritDoc { get; private set; }

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

public static TripleSlashCommentModel CreateModel(string xml, SyntaxLanguage language, ITripleSlashCommentParserContext context)
Expand Down Expand Up @@ -290,22 +290,48 @@ private List<string> GetExamples(XPathNavigator nav, ITripleSlashCommentParserCo
return GetMultipleExampleNodes(nav, "/member/example").ToList();
}

private bool GetIsInheritDoc(XPathNavigator nav, ITripleSlashCommentParserContext context)
private string GetInheritDoc(XPathNavigator nav, ITripleSlashCommentParserContext context)
{
var node = nav.SelectSingleNode("/member/inheritdoc");
if (node == null)
{
return false;
return null;
}

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;
// 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.
// Only cref is supported currently
var cRef = node.GetAttribute("cref", node.NamespaceURI);
if (!string.IsNullOrEmpty(cRef))
{
// Strict check is needed as value could be an invalid href,
// e.g. !:Dictionary&lt;TKey, string&gt; when user manually changed the intellisensed generic type
var match = CommentIdRegex.Match(cRef);
if (match.Success)
{
var id = match.Groups["id"].Value;
var type = match.Groups["type"].Value;

if (type == "Overload")
{
id += '*';
}

context.AddReferenceDelegate?.Invoke(id, cRef);
return id;
}
}
else
{
Logger.LogWarning("Unsupported attribute on <inheritdoc />; inheritdoc element will be ignored.");
return null;
}
}
return true;

// Default inheritdoc (no explicit reference)
return string.Empty;
}

private void ResolveCodeSource(XDocument doc, ITripleSlashCommentParserContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ type FSharpCompilation (compilation: FSharpCheckProjectResults, projPath: string
md.Sees <- cm.Sees
md.SeeAlsos <- cm.SeeAlsos
md.Examples <- cm.Examples
md.IsInheritDoc <- cm.IsInheritDoc
md.InheritDoc <- cm.InheritDoc
if not (isNull md.Syntax) then
if not (isNull md.Syntax.Parameters) then
for pmd in md.Syntax.Parameters do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,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.InheritDoc = commentModel.InheritDoc;
item.CommentModel = commentModel;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ private static void PatchViewModel(MetadataItem item, string comment)
}
}
}
item.IsInheritDoc = commentModel.IsInheritDoc;
item.InheritDoc = commentModel.InheritDoc;
// todo more.
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,17 @@ public void Run(MetadataModel yaml, ResolverContext context)
s => s.IsInvalid ? null : s.Items,
(current, parent) =>
{
if (current.IsInheritDoc)
if (current.InheritDoc != null)
{
InheritDoc(current, context);
current.InheritDoc = null;
}
return true;
});
}

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

switch (dest.Type)
{
case MemberType.Constructor:
Expand Down Expand Up @@ -126,22 +125,37 @@ private static void InheritDoc(MetadataItem dest, ResolverContext context)

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

// An explicit <inheritdoc/> (i.e. cref) overrides the default behavior
if (!string.IsNullOrEmpty(dest.InheritDoc) && context.Members.TryGetValue(dest.InheritDoc, out src))
{
srcName = dest.InheritDoc;
}

if (string.IsNullOrEmpty(srcName))
return;

if (src == null && !context.Members.TryGetValue(srcName, out src))
{
// Try to resolve any templated references before giving up
if (!context.References.TryGetValue(srcName, out var referenceItem) || !context.Members.TryGetValue(referenceItem.Definition, out src))
return;
}

Copy(dest, src, context);
}

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

dest.CopyInheritedData(src);
dest.InheritDoc = null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public int Main(string[] args)
string input = @"
<member name='T:TestClass1.Partial1'>
<summary>
Parital classes <see cref='T:System.AccessViolationException'/><see cref='T:System.AccessViolationException'/>can not cross assemblies, Test <see langword='null'/>
Partial classes <see cref='T:System.AccessViolationException'/><see cref='T:System.AccessViolationException'/>can not cross assemblies, Test <see langword='null'/>

```
Classes in assemblies are by definition complete.
Expand Down Expand Up @@ -134,11 +134,11 @@ Check empty code.
};

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

var summary = commentModel.Summary;
Assert.Equal(@"
Parital classes <xref href=""System.AccessViolationException"" data-throw-if-not-resolved=""false""></xref><xref href=""System.AccessViolationException"" data-throw-if-not-resolved=""false""></xref>can not cross assemblies, Test <xref uid=""langword_csharp_null"" name=""null"" href=""""></xref>
Partial classes <xref href=""System.AccessViolationException"" data-throw-if-not-resolved=""false""></xref><xref href=""System.AccessViolationException"" data-throw-if-not-resolved=""false""></xref>can not cross assemblies, Test <xref uid=""langword_csharp_null"" name=""null"" href=""""></xref>

```
Classes in assemblies are by definition complete.
Expand Down Expand Up @@ -242,7 +242,25 @@ public void InheritDoc()
};

var commentModel = TripleSlashCommentModel.CreateModel(input, SyntaxLanguage.CSharp, context);
Assert.True(commentModel.IsInheritDoc);
Assert.True(commentModel.InheritDoc != null, nameof(commentModel.InheritDoc));
}

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

var commentModel = TripleSlashCommentModel.CreateModel(input, SyntaxLanguage.CSharp, context);
Assert.Equal("ClassLibrary1.MyClass.DoThing", commentModel.InheritDoc);
}

[Trait("Related", "TripleSlashComments")]
Expand Down