Skip to content

Handle langword for see elements #61069

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

Merged
merged 3 commits into from
May 22, 2025
Merged
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
46 changes: 39 additions & 7 deletions src/OpenApi/gen/XmlComments/XmlComment.cs
Original file line number Diff line number Diff line change
@@ -35,8 +35,11 @@ private XmlComment(Compilation compilation, string xml)
// Transform triple slash comment
var doc = XDocument.Parse(xml, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);

ResolveCrefLink(compilation, doc, $"//{DocumentationCommentXmlNames.SeeAlsoElementName}[@cref]");
ResolveCrefLink(compilation, doc, $"//{DocumentationCommentXmlNames.SeeElementName}[@cref]");
ResolveCrefLink(compilation, doc, DocumentationCommentXmlNames.SeeAlsoElementName);
ResolveCrefLink(compilation, doc, DocumentationCommentXmlNames.SeeElementName);

ResolveLangKeyword(doc, DocumentationCommentXmlNames.SeeElementName);

// Resolve <list> and <item> tags into bullets
ResolveListTags(doc);
// Resolve <code> tags into code blocks
@@ -171,18 +174,20 @@ private static void ResolveParaTags(XDocument document)
/// </summary>
/// <param name="compilation">The compilation to resolve type symbol declarations from.</param>
/// <param name="node">The target node to process crefs in.</param>
/// <param name="nodeSelector">The node type to process crefs for, can be `see` or `seealso`.</param>
private static void ResolveCrefLink(Compilation compilation, XNode node, string nodeSelector)
/// <param name="elementName">The node type to process crefs for, can be `see` or `seealso`.</param>
private static void ResolveCrefLink(Compilation compilation, XNode node, string elementName)
{
if (node == null || string.IsNullOrEmpty(nodeSelector))
if (node == null || string.IsNullOrEmpty(elementName))
{
return;
}

var nodes = node.XPathSelectElements(nodeSelector + "[@cref]").ToArray();
var attributeName = DocumentationCommentXmlNames.CrefAttributeName;

var nodes = node.XPathSelectElements($"//{elementName}[@{attributeName}]").ToArray();
foreach (var item in nodes)
{
var cref = item.Attribute(DocumentationCommentXmlNames.CrefAttributeName).Value;
var cref = item.Attribute(attributeName).Value;
if (string.IsNullOrEmpty(cref))
{
continue;
@@ -197,6 +202,33 @@ private static void ResolveCrefLink(Compilation compilation, XNode node, string
}
}

/// <summary>
/// Resolves the links in the XML documentation into language keywords.
/// </summary>
/// <param name="node">The target node to process crefs in.</param>
/// <param name="elementName">The node type to process langwords for, can be `see` or `seealso`.</param>
private static void ResolveLangKeyword(XNode node, string elementName)
{
if (node == null || string.IsNullOrEmpty(elementName))
{
return;
}

var attributeName = DocumentationCommentXmlNames.LangwordAttributeName;

var nodes = node.XPathSelectElements($"//{elementName}[@{attributeName}]").ToArray();
foreach (var item in nodes)
{
var langword = item.Attribute(attributeName).Value;
if (string.IsNullOrEmpty(langword))
{
continue;
}

item.ReplaceWith(new XText($"`{langword}`"));
}
}

private static IEnumerable<string?> GetMultipleExampleNodes(XPathNavigator navigator, string selector)
{
var iterator = navigator.Select(selector);
Original file line number Diff line number Diff line change
@@ -437,6 +437,40 @@ public static T GetGenericValue<T>(T para)
return para;
}
}
/// <summary>
/// A class that implements the <see cref="IDisposable"/> interface.
/// </summary>
public class DisposableType : IDisposable
{
/// <summary>
/// Finalizes an instance of the <see cref="DisposableType"/> class.
/// </summary>
~DisposableType()
{
Dispose(false);
}
/// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true" /> to release both managed and unmanaged resources;
/// <see langword="false" /> to release only unmanaged resources.
/// <see langword="null" /> to indicate a no-op.
/// </param>
protected virtual void Dispose(bool disposing)
{
// No-op
}
}
""";
var generator = new XmlCommentGenerator();
await SnapshotTestHelper.Verify(source, generator, out var compilation);
Original file line number Diff line number Diff line change
@@ -148,7 +148,8 @@ type as a cref attribute.
generic types to open generics for use in
typeof expressions.", null, null, null, null, false, null, null, null));
cache.Add(@"T:ParamsAndParamRefs", new XmlComment(@"This shows examples of typeparamref and typeparam tags", null, null, null, null, false, null, null, null));
cache.Add(@"P:ExampleClass.Label", new XmlComment(null, null, @" The string? ExampleClass.Label is a <see langword=""string"" />
cache.Add(@"T:DisposableType", new XmlComment(@"A class that implements the IDisposable interface.", null, null, null, null, false, null, null, null));
cache.Add(@"P:ExampleClass.Label", new XmlComment(null, null, @" The string? ExampleClass.Label is a `string`
that you use for a label.
Note that there isn't a way to provide a ""cref"" to
each accessor, only to the property itself.", null, @"The `Label` property represents a label
@@ -191,6 +192,7 @@ that implement this interface when the
method as a cref attribute.
The parameter and return value are both of an arbitrary type,
T", null, null, false, null, null, null));
cache.Add(@"M:DisposableType.Dispose", new XmlComment(null, null, null, null, null, false, null, null, null));

return cache;
}