8000 Merge carlossanlop/main with jeffhandley/retain-directives by carlossanlop · Pull Request #99 · dotnet/api-docs-sync · GitHub
[go: up one dir, main page]

Skip to content

Merge carlossanlop/main with jeffhandley/retain-directives #99

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

Closed
wants to merge 7 commits into from
Closed
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ bin
obj
.ionide/
artifacts/
*/*.user
*/*.user
*.received.*
3 changes: 3 additions & 0 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<Project>
<Sdk Name="Microsoft.Build.CentralPackageVersions" Version="2.1.3" />
</Project>
F438 6 changes: 4 additions & 2 deletions DocsPortingTool.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28705.295
# Visual Studio Version 17
VisualStudioVersion = 17.1.31926.61
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libraries", "Libraries\Libraries.csproj", "{87BBF4FD-260C-4AC4-802B-7D2B29629C07}"
EndProject
Expand All @@ -11,9 +11,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
BackportInstructions.md = BackportInstructions.md
Directory.Build.targets = Directory.Build.targets
.github\workflows\dotnet.yml = .github\workflows\dotnet.yml
global.json = global.json
install-as-tool.ps1 = install-as-tool.ps1
Packages.props = Packages.props
README.md = README.md
EndProjectSection
EndProject
Expand Down
2 changes: 1 addition & 1 deletion Libraries/Docs/APIKind.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Libraries.Docs
{
internal enum APIKind
public enum APIKind
{
Type,
Member
Expand Down
76 changes: 71 additions & 5 deletions Libraries/Docs/DocsAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@

namespace Libraries.Docs
{
internal abstract class DocsAPI : IDocsAPI
public abstract class DocsAPI : IDocsAPI
{
private DocsSummary? _summary;
private DocsRemarks? _remarks;
private List<DocsExample>? _examples;
private List<DocsParam>? _params;
private List<DocsParameter>? _parameters;
private List<DocsTypeParameter>? _typeParameters;
Expand Down Expand Up @@ -156,7 +159,7 @@ public List<string> AltMembers
{
if (Docs != null)
{
_altMemberCrefs = Docs.Elements("altmember").Select(x => XmlHelper.GetAttributeValue(x, "cref").DocIdEscaped()).ToList();
_altMemberCrefs = Docs.Elements("altmember").Select(x => XmlHelper.GetAttributeValue(x, "cref")).ToList();
}
else
{
Expand Down Expand Up @@ -190,8 +193,71 @@ public List<DocsRelated> Relateds
public abstract string ReturnType { get; }
public abstract string Returns { get; set; }

public DocsSummary SummaryElement
{
get
{
if (_summary == null)
{
XElement? xe = Docs?.Element("summary");

if (xe != null)
{
_summary = new(xe);
}
else
{
throw new InvalidOperationException($"There was no <summary> element. Doc ID: {DocId}");
}
}

return _summary;
}
}

public abstract string Remarks { get; set; }

public DocsRemarks RemarksElement
{
get
{
if (_remarks == null)
{
XElement? xe = Docs?.Element("remarks");

if (xe != null)
{
_remarks = new(xe);

if (!_remarks.ExampleContent?.ParsedText?.IsDocsEmpty() ?? false)
{
ExampleElements.Add(_remarks.ExampleContent!);
}
}
else
{
_remarks = new(new XElement("remarks"));
}
}

return _remarks;
}
}

public List<DocsExample> ExampleElements
{
get
{
if (_examples == null)
{
IEnumerable<XElement> elems = Docs.Elements("example");
_examples = elems.Select(e => new DocsExample(e)).ToList();
}

return _examples;
}
}

public List<DocsAssemblyInfo> AssemblyInfos
{
get
Expand All @@ -206,10 +272,10 @@ public List<DocsAssemblyInfo> AssemblyInfos

public DocsParam SaveParam(XElement xeIntelliSenseXmlParam)
{
XElement xeDocsParam = new XElement(xeIntelliSenseXmlParam.Name);
XElement xeDocsParam = new(xeIntelliSenseXmlParam.Name);
xeDocsParam.ReplaceAttributes(xeIntelliSenseXmlParam.Attributes());
XmlHelper.SaveFormattedAsXml(xeDocsParam, xeIntelliSenseXmlParam.Value);
DocsParam docsParam = new DocsParam(this, xeDocsParam);
DocsParam docsParam = new(this, xeDocsParam);
Changed = true;
return docsParam;
}
Expand All @@ -229,7 +295,7 @@ public APIKind Kind

public DocsTypeParam AddTypeParam(string name, string value)
{
XElement typeParam = new XElement("typeparam");
XElement typeParam = new("typeparam");
typeParam.SetAttributeValue("name", name);
XmlHelper.AddChildFormattedAsXml(Docs, typeParam, value);
Changed = true;
Expand Down
129 changes: 129 additions & 0 deletions Libraries/Docs/DocsApiReference.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using static System.Net.WebUtility;

namespace Libraries.Docs
{
public class DocsApiReference
{
public bool IsOverload { get; private init; }

public char? Prefix { get; private init; }

public string Api { get; private init; }

// Generic parameters need to support both single and double backtick conventions
private const string GenericParameterPattern = @"`{1,2}(?<arity>\d+)";
private const string ApiChars = @"[A-Za-z0-9\-\._~:\/#\[\]\{\}@!\$&'\(\)\*\+,;`%]";
private const string ApiReferencePattern = @"((?<prefix>[A-Za-z]):)?(?<api>(" + ApiChars + @")+)?(?<extraVars>\?(" + ApiChars + @")+=(" + ApiChars + @")+)?";
private static readonly Regex XrefPattern = new("<xref:(?<api>" + ApiReferencePattern + ")\\s*>", RegexOptions.Compiled);

public DocsApiReference(string apiReference)
{
Api = UrlDecode(apiReference);
var match = Regex.Match(Api, ApiReferencePattern);

if (match.Success)
{
Api = match.Groups["api"].Value;

if (match.Groups["prefix"].Success)
{
Prefix = match.Groups["prefix"].Value[0];
IsOverload = Prefix == 'O';
}
}

if (Api.EndsWith('*'))
{
IsOverload = true;
Api = Api[..^1];
}

Api = ReplacePrimitivesWithShorthands(Api);
Api = ParseGenericTypes(Api);
}

public override string ToString()
{
if (Prefix is not null)
{
return $"{Prefix}:{Api}";
}

return Api;
}

private static readonly Dictionary<string, string> PrimitiveTypes = new()
{
{ "System.Boolean", "bool" },
{ "System.Byte", "byte" },
{ "System.Char", "char" },
{ "System.Decimal", "decimal" },
{ "System.Double", "double" },
{ "System.Int16", "short" },
{ "System.Int32", "int" },
{ "System.Int64", "long" },
{ "System.Object", "object" }, // Ambiguous: could be 'object' or 'dynamic' https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/built-in-types
{ "System.SByte", "sbyte" },
{ "System.Single", "float" },
{ "System.String", "string" },
{ "System.UInt16", "ushort" },
{ "System.UInt32", "uint" },
{ "System.UInt64", "ulong" },
{ "System.Void", "void" }
};

public static 57AE string ReplacePrimitivesWithShorthands(string apiReference)
{
foreach ((string key, string value) in PrimitiveTypes)
{
apiReference = Regex.Replace(apiReference, key, value);
}

return apiReference;
}

public static string ParseGenericTypes(string apiReference)
{
int genericParameterArity = 0;
return Regex.Replace(apiReference, GenericParameterPattern, MapGenericParameter);

string MapGenericParameter(Match match)
{
int arity = int.Parse(match.Groups["arity"].Value);

if (genericParameterArity == 0)
{
// This is the first match that declares the generic parameter arity of the method
// e.g. GenericMethod``3 ---> GenericMethod{T1,T2,T3}(...);
Debug.Assert(arity > 0);
genericParameterArity = arity;
return WrapInCurlyBrackets(string.Join(",", Enumerable.Range(0, arity).Select(CreateGenericParameterName)));
}

// Subsequent matches are references to generic parameters in the method signature,
// e.g. GenericMethod{T1,T2,T3}(..., List{``1} parameter, ...); ---> List{T2} parameter
return CreateGenericParameterName(arity);

// This naming scheme does not map to the exact generic parameter names;
// however this is still accepted by intellisense and backporters can rename
// manually with the help of tooling.
string CreateGenericParameterName(int index) => genericParameterArity == 1 ? "T" : $"T{index + 1}";

static string WrapInCurlyBrackets(string input) => $"{{{input}}}";
}
}

public static string ReplaceMarkdownXrefWithSeeCref(string markdown)
{
return XrefPattern.Replace(markdown, match =>
{
var api = new DocsApiReference(match.Groups["api"].Value);
return @$"<see cref=""{api}"" />";
});
}
}
}
2 changes: 1 addition & 1 deletion Libraries/Docs/DocsAssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Libraries.Docs
{
internal class DocsAssemblyInfo
public class DocsAssemblyInfo
{
private readonly XElement XEAssemblyInfo;
public string AssemblyName
Expand Down
18 changes: 11 additions & 7 deletions Libraries/Docs/DocsAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System.Xml.Linq;
using System.Linq;
using System.Xml.Linq;

namespace Libraries.Docs
{
internal class DocsAttribute
public class DocsAttribute
{
private readonly XElement XEAttribute;

Expand All @@ -13,12 +14,15 @@ public string FrameworkAlternate
return XmlHelper.GetAttributeValue(XEAttribute, "FrameworkAlternate");
}
}
public string AttributeName

public string? AttributeName
{
get
{
return XmlHelper.GetChildElementValue(XEAttribute, "AttributeName");
}
get => GetAttributeName("C#");
}

public string? GetAttributeName(string language)
{
return XEAttribute.Elements("AttributeName").Where(x => XmlHelper.GetAttributeValue(x, "Language") == language).SingleOrDefault()?.Value;
}

public DocsAttribute(XElement xeAttribute)
Expand Down
6 changes: 3 additions & 3 deletions Libraries/Docs/DocsCommentsContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@

namespace Libraries.Docs
{
internal class DocsCommentsContainer
public class DocsCommentsContainer
{
private Configuration Config { get; set; }

private XDocument? xDoc = null;

public readonly Dictionary<string, DocsType> Types = new();
public readonly Dictionary<string, DocsMember> Members = new();
internal readonly Dictionary<string, DocsType> Types = new();
internal readonly Dictionary<string, DocsMember> Members = new();

public DocsCommentsContainer(Configuration config)
{
Expand Down
21 changes: 21 additions & 0 deletions Libraries/Docs/DocsExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
using System.Xml.Linq;

namespace Libraries.Docs
{
public class DocsExample : DocsMarkdownElement
{
public DocsExample(XElement xeExample) : base(xeExample)
{
}

protected override string ExtractElements(string markdown)
{
markdown = base.ExtractElements(markdown);
markdown = RemoveMarkdownHeading(markdown, "Examples?");

return markdown;
}
}
}
2 changes: 1 addition & 1 deletion Libraries/Docs/DocsException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace Libraries.Docs
{
internal class DocsException
public class DocsException
{
private readonly XElement XEException;

Expand Down
Loading
0