-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathInlineInheritDocPipe.cs
94 lines (81 loc) · 3.56 KB
/
InlineInheritDocPipe.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Summary.Extensions;
using Summary.Pipes;
namespace Summary.Roslyn.CSharp;
/// <summary>
/// A <see cref="IPipe{I,O}" /> that inlines <c><inheritdoc/></c> tags.
/// </summary>
/// <remarks>
/// Under the hood, the process of inlining works as follows:
/// <br />
/// - each member in the <see cref="Doc" /> is analyzed
/// - if this member contains an <c><inheritdoc/></c> element, it's removed from the member comment
/// - then, the inherited documentation (either from the base type or from the specified cref) is added to the member comment.
/// </remarks>
public class InlineInheritDocPipe : IPipe<Doc, Doc>
{
public Task<Doc> Run(Doc doc)
{
var inlined = doc.Members.Select(Inline).ToArray();
return Task.FromResult(new Doc(inlined));
DocMember Inline(DocMember member)
{
member = member with { Comment = InlineComment(member.Comment) };
return member switch
{
// For type declarations we want both to inline the comment of the type
// as well as the comments of the type members.
DocTypeDeclaration type => type with { Members = type.Members.Select(Inline).ToArray() },
_ => member,
};
// Order nodes so that <inheritdoc /> tags are always at the end.
// This allows inlining them without overriding the user-defined nodes.
DocComment InlineComment(DocComment comment) =>
comment with
{
Nodes = comment.Nodes
.OrderBy(x => x is DocCommentInheritDoc)
.SelectMany(InlineNode)
.ToArray(),
};
IEnumerable<DocCommentNode> InlineNode(DocCommentNode node) => node switch
{
DocCommentInheritDoc inherit => InlineInheritDoc(inherit),
_ => new[] { node },
};
IEnumerable<DocCommentNode> InlineInheritDoc(DocCommentInheritDoc inherit)
{
// Exclude all spaces from the `cref` for better searching.
var cref = inherit.Cref?.Replace(" ", "");
var source = Source();
if (source == member)
return Enumerable.Empty<DocCommentNode>();
if (source is null)
return Enumerable.Empty<DocCommentNode>();
// We need to inline all comments in `source` to support complex chains
// (e.g., `C` inherits `B` which inherits `A`).
source = Inline(source);
return source.Comment.Nodes;
DocMember? Source()
{
if (cref is null or "")
{
return member switch
{
DocTypeDeclaration type =>
doc.Index
.BaseDeclarations(type)
.FirstOrDefault(),
_ =>
doc.Index.BaseDeclarations(member)
.SelectMany(x => x.MembersOfType(member))
.FirstOrDefault(x => x.FullyQualifiedName.EndsWith(member.Name)),
};
}
return doc.Index.Member(cref, member);
}
}
}
}
}