mirror of
https://github.com/microsoft/autogen.git
synced 2025-09-03 13:27:41 +00:00
[.Net] Remove merging primitive from .editorconfig and gitignore (#3676)
* remove merging primitive from .editorconfig and gitignore * use file scope namespace * remove file-scope name * fix format
This commit is contained in:
parent
54eaa2bb4e
commit
e2c2f98eff
@ -1,5 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
# EditorConfig is awesome:http://EditorConfig.org
|
||||
# EditorConfig is awesome:http://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
@ -93,7 +92,6 @@ csharp_style_inlined_variable_declaration = true:error
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||
=======
|
||||
; EditorConfig to support per-solution formatting.
|
||||
; Use the EditorConfig VS add-in to make this work.
|
||||
; http://editorconfig.org/
|
||||
@ -137,7 +135,6 @@ csharp_style_var_elsewhere = true:suggestion
|
||||
|
||||
# Disallow throw expressions.
|
||||
csharp_style_throw_expression = false:suggestion
|
||||
>>>>>>> agnext_main
|
||||
|
||||
# Newline settings
|
||||
csharp_new_line_before_open_brace = all
|
||||
@ -146,7 +143,6 @@ csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
<<<<<<< HEAD
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
|
||||
# Identation options
|
||||
@ -236,7 +232,6 @@ dotnet_diagnostic.CA2016.severity = suggestion
|
||||
# disable check for generated code
|
||||
[*.generated.cs]
|
||||
generated_code = true
|
||||
=======
|
||||
|
||||
# Namespace settings
|
||||
csharp_style_namespace_declarations = file_scoped:silent
|
||||
@ -708,4 +703,3 @@ generated_code = true
|
||||
|
||||
# IDE1591 Missing XML comment for publicly visible type or member
|
||||
dotnet_diagnostic.CS1591.severity = none
|
||||
>>>>>>> agnext_main
|
||||
|
6
dotnet/.gitignore
vendored
6
dotnet/.gitignore
vendored
@ -1,6 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
# gitignore file for C#/VS
|
||||
=======
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
@ -20,7 +18,6 @@
|
||||
mono_crash.*
|
||||
|
||||
output
|
||||
>>>>>>> agnext_main
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
@ -29,7 +26,6 @@ output
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
<<<<<<< HEAD
|
||||
build/
|
||||
bld/
|
||||
[Bb]in/
|
||||
@ -51,7 +47,6 @@ output/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
=======
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
@ -525,4 +520,3 @@ sk-azfunc-server/local.settings.json
|
||||
temp
|
||||
.mono/**
|
||||
**/values.xml
|
||||
>>>>>>> agnext_main
|
||||
|
@ -3,37 +3,36 @@
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AutoGen.Mistral
|
||||
namespace AutoGen.Mistral;
|
||||
|
||||
public class Error
|
||||
{
|
||||
public class Error
|
||||
public Error(string type, string message, string? param = default(string), string? code = default(string))
|
||||
{
|
||||
public Error(string type, string message, string? param = default(string), string? code = default(string))
|
||||
{
|
||||
Type = type;
|
||||
Message = message;
|
||||
Param = param;
|
||||
Code = code;
|
||||
}
|
||||
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets Message
|
||||
/// </summary>
|
||||
[JsonPropertyName("message")]
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets Param
|
||||
/// </summary>
|
||||
[JsonPropertyName("param")]
|
||||
public string? Param { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets Code
|
||||
/// </summary>
|
||||
[JsonPropertyName("code")]
|
||||
public string? Code { get; set; }
|
||||
Type = type;
|
||||
Message = message;
|
||||
Param = param;
|
||||
Code = code;
|
||||
}
|
||||
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets Message
|
||||
/// </summary>
|
||||
[JsonPropertyName("message")]
|
||||
public string Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets Param
|
||||
/// </summary>
|
||||
[JsonPropertyName("param")]
|
||||
public string? Param { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or Sets Code
|
||||
/// </summary>
|
||||
[JsonPropertyName("code")]
|
||||
public string? Code { get; set; }
|
||||
}
|
||||
|
@ -10,286 +10,285 @@ using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
// copyright: https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/DocumentationCommentExtensions.cs#L17
|
||||
namespace AutoGen.SourceGenerator
|
||||
namespace AutoGen.SourceGenerator;
|
||||
|
||||
internal static class DocumentCommentExtension
|
||||
{
|
||||
internal static class DocumentCommentExtension
|
||||
public static bool IsMissingOrDefault(this SyntaxToken token)
|
||||
{
|
||||
public static bool IsMissingOrDefault(this SyntaxToken token)
|
||||
return token.IsKind(SyntaxKind.None)
|
||||
|| token.IsMissing;
|
||||
}
|
||||
|
||||
public static string? GetParameterDescriptionFromDocumentationCommentTriviaSyntax(this DocumentationCommentTriviaSyntax documentationCommentTrivia, string parameterName)
|
||||
{
|
||||
var parameterElements = documentationCommentTrivia.Content.GetXmlElements("param");
|
||||
|
||||
var parameter = parameterElements.FirstOrDefault(element =>
|
||||
{
|
||||
return token.IsKind(SyntaxKind.None)
|
||||
|| token.IsMissing;
|
||||
var xml = XElement.Parse(element.ToString());
|
||||
var nameAttribute = xml.Attribute("name");
|
||||
return nameAttribute != null && nameAttribute.Value == parameterName;
|
||||
});
|
||||
|
||||
if (parameter is not null)
|
||||
{
|
||||
var xml = XElement.Parse(parameter.ToString());
|
||||
|
||||
return xml.Nodes().OfType<XText>().FirstOrDefault()?.Value;
|
||||
}
|
||||
|
||||
public static string? GetParameterDescriptionFromDocumentationCommentTriviaSyntax(this DocumentationCommentTriviaSyntax documentationCommentTrivia, string parameterName)
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string? GetNamespaceNameFromClassDeclarationSyntax(this ClassDeclarationSyntax classDeclaration)
|
||||
{
|
||||
return classDeclaration.Parent is NamespaceDeclarationSyntax namespaceDeclarationSyntax ? namespaceDeclarationSyntax.Name.ToString()
|
||||
: classDeclaration.Parent is FileScopedNamespaceDeclarationSyntax fileScopedNamespaceDeclarationSyntax ? fileScopedNamespaceDeclarationSyntax.Name.ToString()
|
||||
: null;
|
||||
}
|
||||
|
||||
public static DocumentationCommentTriviaSyntax? GetDocumentationCommentTriviaSyntax(this SyntaxNode node)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
var parameterElements = documentationCommentTrivia.Content.GetXmlElements("param");
|
||||
|
||||
var parameter = parameterElements.FirstOrDefault(element =>
|
||||
{
|
||||
var xml = XElement.Parse(element.ToString());
|
||||
var nameAttribute = xml.Attribute("name");
|
||||
return nameAttribute != null && nameAttribute.Value == parameterName;
|
||||
});
|
||||
|
||||
if (parameter is not null)
|
||||
{
|
||||
var xml = XElement.Parse(parameter.ToString());
|
||||
|
||||
return xml.Nodes().OfType<XText>().FirstOrDefault()?.Value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string? GetNamespaceNameFromClassDeclarationSyntax(this ClassDeclarationSyntax classDeclaration)
|
||||
foreach (var leadingTrivia in node.GetLeadingTrivia())
|
||||
{
|
||||
return classDeclaration.Parent is NamespaceDeclarationSyntax namespaceDeclarationSyntax ? namespaceDeclarationSyntax.Name.ToString()
|
||||
: classDeclaration.Parent is FileScopedNamespaceDeclarationSyntax fileScopedNamespaceDeclarationSyntax ? fileScopedNamespaceDeclarationSyntax.Name.ToString()
|
||||
: null;
|
||||
}
|
||||
|
||||
public static DocumentationCommentTriviaSyntax? GetDocumentationCommentTriviaSyntax(this SyntaxNode node)
|
||||
{
|
||||
if (node == null)
|
||||
if (leadingTrivia.GetStructure() is DocumentationCommentTriviaSyntax structure)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var leadingTrivia in node.GetLeadingTrivia())
|
||||
{
|
||||
if (leadingTrivia.GetStructure() is DocumentationCommentTriviaSyntax structure)
|
||||
{
|
||||
return structure;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static XmlNodeSyntax GetFirstXmlElement(this SyntaxList<XmlNodeSyntax> content, string elementName)
|
||||
{
|
||||
return content.GetXmlElements(elementName).FirstOrDefault();
|
||||
}
|
||||
|
||||
public static IEnumerable<XmlNodeSyntax> GetXmlElements(this SyntaxList<XmlNodeSyntax> content, string elementName)
|
||||
{
|
||||
foreach (XmlNodeSyntax syntax in content)
|
||||
{
|
||||
if (syntax is XmlEmptyElementSyntax emptyElement)
|
||||
{
|
||||
if (string.Equals(elementName, emptyElement.Name.ToString(), StringComparison.Ordinal))
|
||||
{
|
||||
yield return emptyElement;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (syntax is XmlElementSyntax elementSyntax)
|
||||
{
|
||||
if (string.Equals(elementName, elementSyntax.StartTag?.Name?.ToString(), StringComparison.Ordinal))
|
||||
{
|
||||
yield return elementSyntax;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
return structure;
|
||||
}
|
||||
}
|
||||
|
||||
public static T ReplaceExteriorTrivia<T>(this T node, SyntaxTrivia trivia)
|
||||
where T : XmlNodeSyntax
|
||||
{
|
||||
// Make sure to include a space after the '///' characters.
|
||||
SyntaxTrivia triviaWithSpace = SyntaxFactory.DocumentationCommentExterior(trivia.ToString() + " ");
|
||||
return null;
|
||||
}
|
||||
|
||||
return node.ReplaceTrivia(
|
||||
node.DescendantTrivia(descendIntoTrivia: true).Where(i => i.IsKind(SyntaxKind.DocumentationCommentExteriorTrivia)),
|
||||
(originalTrivia, rewrittenTrivia) => SelectExteriorTrivia(rewrittenTrivia, trivia, triviaWithSpace));
|
||||
public static XmlNodeSyntax GetFirstXmlElement(this SyntaxList<XmlNodeSyntax> content, string elementName)
|
||||
{
|
||||
return content.GetXmlElements(elementName).FirstOrDefault();
|
||||
}
|
||||
|
||||
public static IEnumerable<XmlNodeSyntax> GetXmlElements(this SyntaxList<XmlNodeSyntax> content, string elementName)
|
||||
{
|
||||
foreach (XmlNodeSyntax syntax in content)
|
||||
{
|
||||
if (syntax is XmlEmptyElementSyntax emptyElement)
|
||||
{
|
||||
if (string.Equals(elementName, emptyElement.Name.ToString(), StringComparison.Ordinal))
|
||||
{
|
||||
yield return emptyElement;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (syntax is XmlElementSyntax elementSyntax)
|
||||
{
|
||||
if (string.Equals(elementName, elementSyntax.StartTag?.Name?.ToString(), StringComparison.Ordinal))
|
||||
{
|
||||
yield return elementSyntax;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static SyntaxList<XmlNodeSyntax> WithoutFirstAndLastNewlines(this SyntaxList<XmlNodeSyntax> summaryContent)
|
||||
public static T ReplaceExteriorTrivia<T>(this T node, SyntaxTrivia trivia)
|
||||
where T : XmlNodeSyntax
|
||||
{
|
||||
// Make sure to include a space after the '///' characters.
|
||||
SyntaxTrivia triviaWithSpace = SyntaxFactory.DocumentationCommentExterior(trivia.ToString() + " ");
|
||||
|
||||
return node.ReplaceTrivia(
|
||||
node.DescendantTrivia(descendIntoTrivia: true).Where(i => i.IsKind(SyntaxKind.DocumentationCommentExteriorTrivia)),
|
||||
(originalTrivia, rewrittenTrivia) => SelectExteriorTrivia(rewrittenTrivia, trivia, triviaWithSpace));
|
||||
}
|
||||
|
||||
public static SyntaxList<XmlNodeSyntax> WithoutFirstAndLastNewlines(this SyntaxList<XmlNodeSyntax> summaryContent)
|
||||
{
|
||||
if (summaryContent.Count == 0)
|
||||
{
|
||||
if (summaryContent.Count == 0)
|
||||
{
|
||||
return summaryContent;
|
||||
}
|
||||
|
||||
if (!(summaryContent[0] is XmlTextSyntax firstSyntax))
|
||||
{
|
||||
return summaryContent;
|
||||
}
|
||||
|
||||
if (!(summaryContent[summaryContent.Count - 1] is XmlTextSyntax lastSyntax))
|
||||
{
|
||||
return summaryContent;
|
||||
}
|
||||
|
||||
SyntaxTokenList firstSyntaxTokens = firstSyntax.TextTokens;
|
||||
|
||||
int removeFromStart;
|
||||
if (IsXmlNewLine(firstSyntaxTokens[0]))
|
||||
{
|
||||
removeFromStart = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsXmlWhitespace(firstSyntaxTokens[0]))
|
||||
{
|
||||
return summaryContent;
|
||||
}
|
||||
|
||||
if (!IsXmlNewLine(firstSyntaxTokens[1]))
|
||||
{
|
||||
return summaryContent;
|
||||
}
|
||||
|
||||
removeFromStart = 2;
|
||||
}
|
||||
|
||||
SyntaxTokenList lastSyntaxTokens = lastSyntax.TextTokens;
|
||||
|
||||
int removeFromEnd;
|
||||
if (IsXmlNewLine(lastSyntaxTokens[lastSyntaxTokens.Count - 1]))
|
||||
{
|
||||
removeFromEnd = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsXmlWhitespace(lastSyntaxTokens[lastSyntaxTokens.Count - 1]))
|
||||
{
|
||||
return summaryContent;
|
||||
}
|
||||
|
||||
if (!IsXmlNewLine(lastSyntaxTokens[lastSyntaxTokens.Count - 2]))
|
||||
{
|
||||
return summaryContent;
|
||||
}
|
||||
|
||||
removeFromEnd = 2;
|
||||
}
|
||||
|
||||
for (int i = 0; i < removeFromStart; i++)
|
||||
{
|
||||
firstSyntaxTokens = firstSyntaxTokens.RemoveAt(0);
|
||||
}
|
||||
|
||||
if (firstSyntax == lastSyntax)
|
||||
{
|
||||
lastSyntaxTokens = firstSyntaxTokens;
|
||||
}
|
||||
|
||||
for (int i = 0; i < removeFromEnd; i++)
|
||||
{
|
||||
if (!lastSyntaxTokens.Any())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
lastSyntaxTokens = lastSyntaxTokens.RemoveAt(lastSyntaxTokens.Count - 1);
|
||||
}
|
||||
|
||||
summaryContent = summaryContent.RemoveAt(summaryContent.Count - 1);
|
||||
if (lastSyntaxTokens.Count != 0)
|
||||
{
|
||||
summaryContent = summaryContent.Add(lastSyntax.WithTextTokens(lastSyntaxTokens));
|
||||
}
|
||||
|
||||
if (firstSyntax != lastSyntax)
|
||||
{
|
||||
summaryContent = summaryContent.RemoveAt(0);
|
||||
if (firstSyntaxTokens.Count != 0)
|
||||
{
|
||||
summaryContent = summaryContent.Insert(0, firstSyntax.WithTextTokens(firstSyntaxTokens));
|
||||
}
|
||||
}
|
||||
|
||||
if (summaryContent.Count > 0)
|
||||
{
|
||||
// Make sure to remove the leading trivia
|
||||
summaryContent = summaryContent.Replace(summaryContent[0], summaryContent[0].WithLeadingTrivia());
|
||||
|
||||
// Remove leading spaces (between the <para> start tag and the start of the paragraph content)
|
||||
if (summaryContent[0] is XmlTextSyntax firstTextSyntax && firstTextSyntax.TextTokens.Count > 0)
|
||||
{
|
||||
SyntaxToken firstTextToken = firstTextSyntax.TextTokens[0];
|
||||
string firstTokenText = firstTextToken.Text;
|
||||
string trimmed = firstTokenText.TrimStart();
|
||||
if (trimmed != firstTokenText)
|
||||
{
|
||||
SyntaxToken newFirstToken = SyntaxFactory.Token(
|
||||
firstTextToken.LeadingTrivia,
|
||||
firstTextToken.Kind(),
|
||||
trimmed,
|
||||
firstTextToken.ValueText.TrimStart(),
|
||||
firstTextToken.TrailingTrivia);
|
||||
|
||||
summaryContent = summaryContent.Replace(firstTextSyntax, firstTextSyntax.ReplaceToken(firstTextToken, newFirstToken));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return summaryContent;
|
||||
}
|
||||
|
||||
public static bool IsXmlNewLine(this SyntaxToken node)
|
||||
if (!(summaryContent[0] is XmlTextSyntax firstSyntax))
|
||||
{
|
||||
return node.IsKind(SyntaxKind.XmlTextLiteralNewLineToken);
|
||||
return summaryContent;
|
||||
}
|
||||
|
||||
public static bool IsXmlWhitespace(this SyntaxToken node)
|
||||
if (!(summaryContent[summaryContent.Count - 1] is XmlTextSyntax lastSyntax))
|
||||
{
|
||||
return node.IsKind(SyntaxKind.XmlTextLiteralToken)
|
||||
&& string.IsNullOrWhiteSpace(node.Text);
|
||||
return summaryContent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjust the leading and trailing trivia associated with <see cref="SyntaxKind.XmlTextLiteralNewLineToken"/>
|
||||
/// tokens to ensure the formatter properly indents the exterior trivia.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of syntax node.</typeparam>
|
||||
/// <param name="node">The syntax node to adjust tokens.</param>
|
||||
/// <returns>A <see cref="SyntaxNode"/> equivalent to the input <paramref name="node"/>, adjusted by moving any
|
||||
/// trailing trivia from <see cref="SyntaxKind.XmlTextLiteralNewLineToken"/> tokens to be leading trivia of the
|
||||
/// following token.</returns>
|
||||
public static T AdjustDocumentationCommentNewLineTrivia<T>(this T node)
|
||||
where T : SyntaxNode
|
||||
{
|
||||
var tokensForAdjustment =
|
||||
from token in node.DescendantTokens()
|
||||
where token.IsKind(SyntaxKind.XmlTextLiteralNewLineToken)
|
||||
where token.HasTrailingTrivia
|
||||
let next = token.GetNextToken(includeZeroWidth: true, includeSkipped: true, includeDirectives: true, includeDocumentationComments: true)
|
||||
where !next.IsMissingOrDefault()
|
||||
select new KeyValuePair<SyntaxToken, SyntaxToken>(token, next);
|
||||
SyntaxTokenList firstSyntaxTokens = firstSyntax.TextTokens;
|
||||
|
||||
Dictionary<SyntaxToken, SyntaxToken> replacements = new Dictionary<SyntaxToken, SyntaxToken>();
|
||||
foreach (var pair in tokensForAdjustment)
|
||||
int removeFromStart;
|
||||
if (IsXmlNewLine(firstSyntaxTokens[0]))
|
||||
{
|
||||
removeFromStart = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsXmlWhitespace(firstSyntaxTokens[0]))
|
||||
{
|
||||
replacements[pair.Key] = pair.Key.WithTrailingTrivia();
|
||||
replacements[pair.Value] = pair.Value.WithLeadingTrivia(pair.Value.LeadingTrivia.InsertRange(0, pair.Key.TrailingTrivia));
|
||||
return summaryContent;
|
||||
}
|
||||
|
||||
return node.ReplaceTokens(replacements.Keys, (originalToken, rewrittenToken) => replacements[originalToken]);
|
||||
}
|
||||
|
||||
public static XmlNameSyntax? GetName(this XmlNodeSyntax element)
|
||||
{
|
||||
return (element as XmlElementSyntax)?.StartTag?.Name
|
||||
?? (element as XmlEmptyElementSyntax)?.Name;
|
||||
}
|
||||
|
||||
private static SyntaxTrivia SelectExteriorTrivia(SyntaxTrivia rewrittenTrivia, SyntaxTrivia trivia, SyntaxTrivia triviaWithSpace)
|
||||
{
|
||||
// if the trivia had a trailing space, make sure to preserve it
|
||||
if (rewrittenTrivia.ToString().EndsWith(" "))
|
||||
if (!IsXmlNewLine(firstSyntaxTokens[1]))
|
||||
{
|
||||
return triviaWithSpace;
|
||||
return summaryContent;
|
||||
}
|
||||
|
||||
// otherwise the space is part of the leading trivia of the following token, so don't add an extra one to
|
||||
// the exterior trivia
|
||||
return trivia;
|
||||
removeFromStart = 2;
|
||||
}
|
||||
|
||||
SyntaxTokenList lastSyntaxTokens = lastSyntax.TextTokens;
|
||||
|
||||
int removeFromEnd;
|
||||
if (IsXmlNewLine(lastSyntaxTokens[lastSyntaxTokens.Count - 1]))
|
||||
{
|
||||
removeFromEnd = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsXmlWhitespace(lastSyntaxTokens[lastSyntaxTokens.Count - 1]))
|
||||
{
|
||||
return summaryContent;
|
||||
}
|
||||
|
||||
if (!IsXmlNewLine(lastSyntaxTokens[lastSyntaxTokens.Count - 2]))
|
||||
{
|
||||
return summaryContent;
|
||||
}
|
||||
|
||||
removeFromEnd = 2;
|
||||
}
|
||||
|
||||
for (int i = 0; i < removeFromStart; i++)
|
||||
{
|
||||
firstSyntaxTokens = firstSyntaxTokens.RemoveAt(0);
|
||||
}
|
||||
|
||||
if (firstSyntax == lastSyntax)
|
||||
{
|
||||
lastSyntaxTokens = firstSyntaxTokens;
|
||||
}
|
||||
|
||||
for (int i = 0; i < removeFromEnd; i++)
|
||||
{
|
||||
if (!lastSyntaxTokens.Any())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
lastSyntaxTokens = lastSyntaxTokens.RemoveAt(lastSyntaxTokens.Count - 1);
|
||||
}
|
||||
|
||||
summaryContent = summaryContent.RemoveAt(summaryContent.Count - 1);
|
||||
if (lastSyntaxTokens.Count != 0)
|
||||
{
|
||||
summaryContent = summaryContent.Add(lastSyntax.WithTextTokens(lastSyntaxTokens));
|
||||
}
|
||||
|
||||
if (firstSyntax != lastSyntax)
|
||||
{
|
||||
summaryContent = summaryContent.RemoveAt(0);
|
||||
if (firstSyntaxTokens.Count != 0)
|
||||
{
|
||||
summaryContent = summaryContent.Insert(0, firstSyntax.WithTextTokens(firstSyntaxTokens));
|
||||
}
|
||||
}
|
||||
|
||||
if (summaryContent.Count > 0)
|
||||
{
|
||||
// Make sure to remove the leading trivia
|
||||
summaryContent = summaryContent.Replace(summaryContent[0], summaryContent[0].WithLeadingTrivia());
|
||||
|
||||
// Remove leading spaces (between the <para> start tag and the start of the paragraph content)
|
||||
if (summaryContent[0] is XmlTextSyntax firstTextSyntax && firstTextSyntax.TextTokens.Count > 0)
|
||||
{
|
||||
SyntaxToken firstTextToken = firstTextSyntax.TextTokens[0];
|
||||
string firstTokenText = firstTextToken.Text;
|
||||
string trimmed = firstTokenText.TrimStart();
|
||||
if (trimmed != firstTokenText)
|
||||
{
|
||||
SyntaxToken newFirstToken = SyntaxFactory.Token(
|
||||
firstTextToken.LeadingTrivia,
|
||||
firstTextToken.Kind(),
|
||||
trimmed,
|
||||
firstTextToken.ValueText.TrimStart(),
|
||||
firstTextToken.TrailingTrivia);
|
||||
|
||||
summaryContent = summaryContent.Replace(firstTextSyntax, firstTextSyntax.ReplaceToken(firstTextToken, newFirstToken));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return summaryContent;
|
||||
}
|
||||
|
||||
public static bool IsXmlNewLine(this SyntaxToken node)
|
||||
{
|
||||
return node.IsKind(SyntaxKind.XmlTextLiteralNewLineToken);
|
||||
}
|
||||
|
||||
public static bool IsXmlWhitespace(this SyntaxToken node)
|
||||
{
|
||||
return node.IsKind(SyntaxKind.XmlTextLiteralToken)
|
||||
&& string.IsNullOrWhiteSpace(node.Text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjust the leading and trailing trivia associated with <see cref="SyntaxKind.XmlTextLiteralNewLineToken"/>
|
||||
/// tokens to ensure the formatter properly indents the exterior trivia.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of syntax node.</typeparam>
|
||||
/// <param name="node">The syntax node to adjust tokens.</param>
|
||||
/// <returns>A <see cref="SyntaxNode"/> equivalent to the input <paramref name="node"/>, adjusted by moving any
|
||||
/// trailing trivia from <see cref="SyntaxKind.XmlTextLiteralNewLineToken"/> tokens to be leading trivia of the
|
||||
/// following token.</returns>
|
||||
public static T AdjustDocumentationCommentNewLineTrivia<T>(this T node)
|
||||
where T : SyntaxNode
|
||||
{
|
||||
var tokensForAdjustment =
|
||||
from token in node.DescendantTokens()
|
||||
where token.IsKind(SyntaxKind.XmlTextLiteralNewLineToken)
|
||||
where token.HasTrailingTrivia
|
||||
let next = token.GetNextToken(includeZeroWidth: true, includeSkipped: true, includeDirectives: true, includeDocumentationComments: true)
|
||||
where !next.IsMissingOrDefault()
|
||||
select new KeyValuePair<SyntaxToken, SyntaxToken>(token, next);
|
||||
|
||||
Dictionary<SyntaxToken, SyntaxToken> replacements = new Dictionary<SyntaxToken, SyntaxToken>();
|
||||
foreach (var pair in tokensForAdjustment)
|
||||
{
|
||||
replacements[pair.Key] = pair.Key.WithTrailingTrivia();
|
||||
replacements[pair.Value] = pair.Value.WithLeadingTrivia(pair.Value.LeadingTrivia.InsertRange(0, pair.Key.TrailingTrivia));
|
||||
}
|
||||
|
||||
return node.ReplaceTokens(replacements.Keys, (originalToken, rewrittenToken) => replacements[originalToken]);
|
||||
}
|
||||
|
||||
public static XmlNameSyntax? GetName(this XmlNodeSyntax element)
|
||||
{
|
||||
return (element as XmlElementSyntax)?.StartTag?.Name
|
||||
?? (element as XmlEmptyElementSyntax)?.Name;
|
||||
}
|
||||
|
||||
private static SyntaxTrivia SelectExteriorTrivia(SyntaxTrivia rewrittenTrivia, SyntaxTrivia trivia, SyntaxTrivia triviaWithSpace)
|
||||
{
|
||||
// if the trivia had a trailing space, make sure to preserve it
|
||||
if (rewrittenTrivia.ToString().EndsWith(" "))
|
||||
{
|
||||
return triviaWithSpace;
|
||||
}
|
||||
|
||||
// otherwise the space is part of the leading trivia of the following token, so don't add an extra one to
|
||||
// the exterior trivia
|
||||
return trivia;
|
||||
}
|
||||
}
|
||||
|
@ -12,236 +12,235 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AutoGen.SourceGenerator
|
||||
namespace AutoGen.SourceGenerator;
|
||||
|
||||
[Generator]
|
||||
public partial class FunctionCallGenerator : IIncrementalGenerator
|
||||
{
|
||||
[Generator]
|
||||
public partial class FunctionCallGenerator : IIncrementalGenerator
|
||||
private const string FUNCTION_CALL_ATTRIBUTION = "AutoGen.Core.FunctionAttribute";
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
private const string FUNCTION_CALL_ATTRIBUTION = "AutoGen.Core.FunctionAttribute";
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
#if LAUNCH_DEBUGGER
|
||||
if (!System.Diagnostics.Debugger.IsAttached)
|
||||
{
|
||||
System.Diagnostics.Debugger.Launch();
|
||||
}
|
||||
if (!System.Diagnostics.Debugger.IsAttached)
|
||||
{
|
||||
System.Diagnostics.Debugger.Launch();
|
||||
}
|
||||
#endif
|
||||
var optionProvider = context.AnalyzerConfigOptionsProvider.Select((provider, ct) =>
|
||||
var optionProvider = context.AnalyzerConfigOptionsProvider.Select((provider, ct) =>
|
||||
{
|
||||
var generateFunctionDefinitionContract = provider.GlobalOptions.TryGetValue("build_property.EnableContract", out var value) && value?.ToLowerInvariant() == "true";
|
||||
|
||||
return generateFunctionDefinitionContract;
|
||||
});
|
||||
// step 1
|
||||
// filter syntax tree and search syntax node that satisfied the following conditions
|
||||
// - is partial class
|
||||
var partialClassSyntaxProvider = context.SyntaxProvider.CreateSyntaxProvider<PartialClassOutput?>(
|
||||
(node, ct) =>
|
||||
{
|
||||
var generateFunctionDefinitionContract = provider.GlobalOptions.TryGetValue("build_property.EnableContract", out var value) && value?.ToLowerInvariant() == "true";
|
||||
return node is ClassDeclarationSyntax classDeclarationSyntax && classDeclarationSyntax.Modifiers.Any(SyntaxKind.PartialKeyword);
|
||||
},
|
||||
(ctx, ct) =>
|
||||
{
|
||||
// first check if any method of the class has FunctionAttribution attribute
|
||||
// if not, then return null
|
||||
var filePath = ctx.Node.SyntaxTree.FilePath;
|
||||
var fileName = Path.GetFileNameWithoutExtension(filePath);
|
||||
|
||||
return generateFunctionDefinitionContract;
|
||||
});
|
||||
// step 1
|
||||
// filter syntax tree and search syntax node that satisfied the following conditions
|
||||
// - is partial class
|
||||
var partialClassSyntaxProvider = context.SyntaxProvider.CreateSyntaxProvider<PartialClassOutput?>(
|
||||
(node, ct) =>
|
||||
var classDeclarationSyntax = ctx.Node as ClassDeclarationSyntax;
|
||||
var nameSpace = classDeclarationSyntax?.Parent as NamespaceDeclarationSyntax;
|
||||
var fullClassName = $"{nameSpace?.Name}.{classDeclarationSyntax!.Identifier}";
|
||||
if (classDeclarationSyntax == null)
|
||||
{
|
||||
return node is ClassDeclarationSyntax classDeclarationSyntax && classDeclarationSyntax.Modifiers.Any(SyntaxKind.PartialKeyword);
|
||||
},
|
||||
(ctx, ct) =>
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!classDeclarationSyntax.Members.Any(member => member.AttributeLists.Any(attributeList => attributeList.Attributes.Any(attribute =>
|
||||
{
|
||||
// first check if any method of the class has FunctionAttribution attribute
|
||||
// if not, then return null
|
||||
var filePath = ctx.Node.SyntaxTree.FilePath;
|
||||
var fileName = Path.GetFileNameWithoutExtension(filePath);
|
||||
|
||||
var classDeclarationSyntax = ctx.Node as ClassDeclarationSyntax;
|
||||
var nameSpace = classDeclarationSyntax?.Parent as NamespaceDeclarationSyntax;
|
||||
var fullClassName = $"{nameSpace?.Name}.{classDeclarationSyntax!.Identifier}";
|
||||
if (classDeclarationSyntax == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!classDeclarationSyntax.Members.Any(member => member.AttributeLists.Any(attributeList => attributeList.Attributes.Any(attribute =>
|
||||
{
|
||||
return ctx.SemanticModel.GetSymbolInfo(attribute).Symbol is IMethodSymbol methodSymbol && methodSymbol.ContainingType.ToDisplayString() == FUNCTION_CALL_ATTRIBUTION;
|
||||
}))))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// collect methods that has FunctionAttribution attribute
|
||||
var methodDeclarationSyntaxes = classDeclarationSyntax.Members.Where(member => member.AttributeLists.Any(attributeList => attributeList.Attributes.Any(attribute =>
|
||||
{
|
||||
return ctx.SemanticModel.GetSymbolInfo(attribute).Symbol is IMethodSymbol methodSymbol && methodSymbol.ContainingType.ToDisplayString() == FUNCTION_CALL_ATTRIBUTION;
|
||||
})))
|
||||
.Select(member => member as MethodDeclarationSyntax)
|
||||
.Where(method => method != null);
|
||||
|
||||
var className = classDeclarationSyntax.Identifier.ToString();
|
||||
var namespaceName = classDeclarationSyntax.GetNamespaceNameFromClassDeclarationSyntax();
|
||||
var functionContracts = methodDeclarationSyntaxes.Select(method => CreateFunctionContract(method!, className, namespaceName));
|
||||
|
||||
return new PartialClassOutput(fullClassName, classDeclarationSyntax, functionContracts);
|
||||
})
|
||||
.Where(node => node != null)
|
||||
.Collect();
|
||||
|
||||
var aggregateProvider = optionProvider.Combine(partialClassSyntaxProvider);
|
||||
// step 2
|
||||
context.RegisterSourceOutput(aggregateProvider,
|
||||
(ctx, source) =>
|
||||
return ctx.SemanticModel.GetSymbolInfo(attribute).Symbol is IMethodSymbol methodSymbol && methodSymbol.ContainingType.ToDisplayString() == FUNCTION_CALL_ATTRIBUTION;
|
||||
}))))
|
||||
{
|
||||
var groups = source.Right.GroupBy(item => item!.FullClassName);
|
||||
foreach (var group in groups)
|
||||
return null;
|
||||
}
|
||||
|
||||
// collect methods that has FunctionAttribution attribute
|
||||
var methodDeclarationSyntaxes = classDeclarationSyntax.Members.Where(member => member.AttributeLists.Any(attributeList => attributeList.Attributes.Any(attribute =>
|
||||
{
|
||||
return ctx.SemanticModel.GetSymbolInfo(attribute).Symbol is IMethodSymbol methodSymbol && methodSymbol.ContainingType.ToDisplayString() == FUNCTION_CALL_ATTRIBUTION;
|
||||
})))
|
||||
.Select(member => member as MethodDeclarationSyntax)
|
||||
.Where(method => method != null);
|
||||
|
||||
var className = classDeclarationSyntax.Identifier.ToString();
|
||||
var namespaceName = classDeclarationSyntax.GetNamespaceNameFromClassDeclarationSyntax();
|
||||
var functionContracts = methodDeclarationSyntaxes.Select(method => CreateFunctionContract(method!, className, namespaceName));
|
||||
|
||||
return new PartialClassOutput(fullClassName, classDeclarationSyntax, functionContracts);
|
||||
})
|
||||
.Where(node => node != null)
|
||||
.Collect();
|
||||
|
||||
var aggregateProvider = optionProvider.Combine(partialClassSyntaxProvider);
|
||||
// step 2
|
||||
context.RegisterSourceOutput(aggregateProvider,
|
||||
(ctx, source) =>
|
||||
{
|
||||
var groups = source.Right.GroupBy(item => item!.FullClassName);
|
||||
foreach (var group in groups)
|
||||
{
|
||||
var functionContracts = group.SelectMany(item => item!.FunctionContracts).ToArray();
|
||||
var className = group.First()!.ClassDeclarationSyntax.Identifier.ToString();
|
||||
var namespaceName = group.First()!.ClassDeclarationSyntax.GetNamespaceNameFromClassDeclarationSyntax() ?? string.Empty;
|
||||
var functionTT = new FunctionCallTemplate
|
||||
{
|
||||
var functionContracts = group.SelectMany(item => item!.FunctionContracts).ToArray();
|
||||
var className = group.First()!.ClassDeclarationSyntax.Identifier.ToString();
|
||||
var namespaceName = group.First()!.ClassDeclarationSyntax.GetNamespaceNameFromClassDeclarationSyntax() ?? string.Empty;
|
||||
var functionTT = new FunctionCallTemplate
|
||||
NameSpace = namespaceName,
|
||||
ClassName = className,
|
||||
FunctionContracts = functionContracts.ToArray(),
|
||||
};
|
||||
|
||||
var functionSource = functionTT.TransformText();
|
||||
var fileName = $"{className}.generated.cs";
|
||||
|
||||
ctx.AddSource(fileName, SourceText.From(functionSource, System.Text.Encoding.UTF8));
|
||||
File.WriteAllText(Path.Combine(Path.GetTempPath(), fileName), functionSource);
|
||||
}
|
||||
|
||||
if (source.Left)
|
||||
{
|
||||
var overallFunctionDefinition = source.Right.SelectMany(x => x!.FunctionContracts.Select(y => new { fullClassName = x.FullClassName, y = y }));
|
||||
var overallFunctionDefinitionObject = overallFunctionDefinition.Select(
|
||||
x => new
|
||||
{
|
||||
NameSpace = namespaceName,
|
||||
ClassName = className,
|
||||
FunctionContracts = functionContracts.ToArray(),
|
||||
};
|
||||
|
||||
var functionSource = functionTT.TransformText();
|
||||
var fileName = $"{className}.generated.cs";
|
||||
|
||||
ctx.AddSource(fileName, SourceText.From(functionSource, System.Text.Encoding.UTF8));
|
||||
File.WriteAllText(Path.Combine(Path.GetTempPath(), fileName), functionSource);
|
||||
}
|
||||
|
||||
if (source.Left)
|
||||
{
|
||||
var overallFunctionDefinition = source.Right.SelectMany(x => x!.FunctionContracts.Select(y => new { fullClassName = x.FullClassName, y = y }));
|
||||
var overallFunctionDefinitionObject = overallFunctionDefinition.Select(
|
||||
x => new
|
||||
fullClassName = x.fullClassName,
|
||||
functionDefinition = new
|
||||
{
|
||||
fullClassName = x.fullClassName,
|
||||
functionDefinition = new
|
||||
x.y.Name,
|
||||
x.y.Description,
|
||||
x.y.ReturnType,
|
||||
Parameters = x.y.Parameters.Select(y => new
|
||||
{
|
||||
x.y.Name,
|
||||
x.y.Description,
|
||||
x.y.ReturnType,
|
||||
Parameters = x.y.Parameters.Select(y => new
|
||||
{
|
||||
y.Name,
|
||||
y.Description,
|
||||
y.JsonType,
|
||||
y.JsonItemType,
|
||||
y.Type,
|
||||
y.IsOptional,
|
||||
y.DefaultValue,
|
||||
}),
|
||||
},
|
||||
});
|
||||
y.Name,
|
||||
y.Description,
|
||||
y.JsonType,
|
||||
y.JsonItemType,
|
||||
y.Type,
|
||||
y.IsOptional,
|
||||
y.DefaultValue,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
var json = JsonConvert.SerializeObject(overallFunctionDefinitionObject, formatting: Formatting.Indented);
|
||||
// wrap json inside csharp block, as SG doesn't support generating non-source file
|
||||
json = $@"/* <auto-generated> wrap json inside csharp block, as SG doesn't support generating non-source file
|
||||
var json = JsonConvert.SerializeObject(overallFunctionDefinitionObject, formatting: Formatting.Indented);
|
||||
// wrap json inside csharp block, as SG doesn't support generating non-source file
|
||||
json = $@"/* <auto-generated> wrap json inside csharp block, as SG doesn't support generating non-source file
|
||||
{json}
|
||||
</auto-generated>*/";
|
||||
ctx.AddSource("FunctionDefinition.json", SourceText.From(json, System.Text.Encoding.UTF8));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class PartialClassOutput
|
||||
{
|
||||
public PartialClassOutput(string fullClassName, ClassDeclarationSyntax classDeclarationSyntax, IEnumerable<SourceGeneratorFunctionContract> functionContracts)
|
||||
{
|
||||
FullClassName = fullClassName;
|
||||
ClassDeclarationSyntax = classDeclarationSyntax;
|
||||
FunctionContracts = functionContracts;
|
||||
}
|
||||
|
||||
public string FullClassName { get; }
|
||||
|
||||
public ClassDeclarationSyntax ClassDeclarationSyntax { get; }
|
||||
|
||||
public IEnumerable<SourceGeneratorFunctionContract> FunctionContracts { get; }
|
||||
}
|
||||
|
||||
private SourceGeneratorFunctionContract CreateFunctionContract(MethodDeclarationSyntax method, string? className, string? namespaceName)
|
||||
{
|
||||
// get function_call attribute
|
||||
var functionCallAttribute = method.AttributeLists.SelectMany(attributeList => attributeList.Attributes)
|
||||
.FirstOrDefault(attribute => attribute.Name.ToString() == FUNCTION_CALL_ATTRIBUTION);
|
||||
// get document string if exist
|
||||
var documentationCommentTrivia = method.GetDocumentationCommentTriviaSyntax();
|
||||
|
||||
var functionName = method.Identifier.ToString();
|
||||
var functionDescription = functionCallAttribute?.ArgumentList?.Arguments.FirstOrDefault(argument => argument.NameEquals?.Name.ToString() == "Description")?.Expression.ToString() ?? string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(functionDescription))
|
||||
{
|
||||
// if functionDescription is empty, then try to get it from documentationCommentTrivia
|
||||
// firstly, try getting from <summary> tag
|
||||
var summary = documentationCommentTrivia?.Content.GetFirstXmlElement("summary");
|
||||
if (summary is not null && XElement.Parse(summary.ToString()) is XElement element)
|
||||
{
|
||||
functionDescription = element.Nodes().OfType<XText>().FirstOrDefault()?.Value;
|
||||
|
||||
// remove [space...][//|///][space...] from functionDescription
|
||||
// replace [^\S\r\n]+[\/]+\s* with empty string
|
||||
functionDescription = System.Text.RegularExpressions.Regex.Replace(functionDescription, @"[^\S\r\n]+\/[\/]+\s*", string.Empty);
|
||||
ctx.AddSource("FunctionDefinition.json", SourceText.From(json, System.Text.Encoding.UTF8));
|
||||
}
|
||||
else
|
||||
{
|
||||
// if <summary> tag is not exist, then simply use the entire leading trivia as functionDescription
|
||||
functionDescription = method.GetLeadingTrivia().ToString();
|
||||
|
||||
// remove [space...][//|///][space...] from functionDescription
|
||||
// replace [^\S\r\n]+[\/]+\s* with empty string
|
||||
functionDescription = System.Text.RegularExpressions.Regex.Replace(functionDescription, @"[^\S\r\n]+\/[\/]+\s*", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
// get parameters
|
||||
var parameters = method.ParameterList.Parameters.Select(parameter =>
|
||||
{
|
||||
var description = $"{parameter.Identifier}. type is {parameter.Type}";
|
||||
|
||||
// try to get parameter description from documentationCommentTrivia
|
||||
var parameterDocumentationComment = documentationCommentTrivia?.GetParameterDescriptionFromDocumentationCommentTriviaSyntax(parameter.Identifier.ToString());
|
||||
if (parameterDocumentationComment is not null)
|
||||
{
|
||||
description = parameterDocumentationComment.ToString();
|
||||
// remove [space...][//|///][space...] from functionDescription
|
||||
// replace [^\S\r\n]+[\/]+\s* with empty string
|
||||
description = System.Text.RegularExpressions.Regex.Replace(description, @"[^\S\r\n]+\/[\/]+\s*", string.Empty);
|
||||
}
|
||||
var jsonItemType = parameter.Type!.ToString().EndsWith("[]") ? parameter.Type!.ToString().Substring(0, parameter.Type!.ToString().Length - 2) : null;
|
||||
return new SourceGeneratorParameterContract
|
||||
{
|
||||
Name = parameter.Identifier.ToString(),
|
||||
JsonType = parameter.Type!.ToString() switch
|
||||
{
|
||||
"string" => "string",
|
||||
"string[]" => "array",
|
||||
"System.Int32" or "int" => "integer",
|
||||
"System.Int64" or "long" => "integer",
|
||||
"System.Single" or "float" => "number",
|
||||
"System.Double" or "double" => "number",
|
||||
"System.Boolean" or "bool" => "boolean",
|
||||
"System.DateTime" => "string",
|
||||
"System.Guid" => "string",
|
||||
"System.Object" => "object",
|
||||
_ => "object",
|
||||
},
|
||||
JsonItemType = jsonItemType,
|
||||
Type = parameter.Type!.ToString(),
|
||||
Description = description,
|
||||
IsOptional = parameter.Default != null,
|
||||
// if Default is null or "null", then DefaultValue is null
|
||||
DefaultValue = parameter.Default?.ToString() == "null" ? null : parameter.Default?.Value.ToString(),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return new SourceGeneratorFunctionContract
|
||||
{
|
||||
ClassName = className,
|
||||
Namespace = namespaceName,
|
||||
Name = functionName,
|
||||
Description = functionDescription?.Trim() ?? functionName,
|
||||
Parameters = parameters.ToArray(),
|
||||
ReturnType = method.ReturnType.ToString(),
|
||||
};
|
||||
private class PartialClassOutput
|
||||
{
|
||||
public PartialClassOutput(string fullClassName, ClassDeclarationSyntax classDeclarationSyntax, IEnumerable<SourceGeneratorFunctionContract> functionContracts)
|
||||
{
|
||||
FullClassName = fullClassName;
|
||||
ClassDeclarationSyntax = classDeclarationSyntax;
|
||||
FunctionContracts = functionContracts;
|
||||
}
|
||||
|
||||
public string FullClassName { get; }
|
||||
|
||||
public ClassDeclarationSyntax ClassDeclarationSyntax { get; }
|
||||
|
||||
public IEnumerable<SourceGeneratorFunctionContract> FunctionContracts { get; }
|
||||
}
|
||||
|
||||
private SourceGeneratorFunctionContract CreateFunctionContract(MethodDeclarationSyntax method, string? className, string? namespaceName)
|
||||
{
|
||||
// get function_call attribute
|
||||
var functionCallAttribute = method.AttributeLists.SelectMany(attributeList => attributeList.Attributes)
|
||||
.FirstOrDefault(attribute => attribute.Name.ToString() == FUNCTION_CALL_ATTRIBUTION);
|
||||
// get document string if exist
|
||||
var documentationCommentTrivia = method.GetDocumentationCommentTriviaSyntax();
|
||||
|
||||
var functionName = method.Identifier.ToString();
|
||||
var functionDescription = functionCallAttribute?.ArgumentList?.Arguments.FirstOrDefault(argument => argument.NameEquals?.Name.ToString() == "Description")?.Expression.ToString() ?? string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(functionDescription))
|
||||
{
|
||||
// if functionDescription is empty, then try to get it from documentationCommentTrivia
|
||||
// firstly, try getting from <summary> tag
|
||||
var summary = documentationCommentTrivia?.Content.GetFirstXmlElement("summary");
|
||||
if (summary is not null && XElement.Parse(summary.ToString()) is XElement element)
|
||||
{
|
||||
functionDescription = element.Nodes().OfType<XText>().FirstOrDefault()?.Value;
|
||||
|
||||
// remove [space...][//|///][space...] from functionDescription
|
||||
// replace [^\S\r\n]+[\/]+\s* with empty string
|
||||
functionDescription = System.Text.RegularExpressions.Regex.Replace(functionDescription, @"[^\S\r\n]+\/[\/]+\s*", string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if <summary> tag is not exist, then simply use the entire leading trivia as functionDescription
|
||||
functionDescription = method.GetLeadingTrivia().ToString();
|
||||
|
||||
// remove [space...][//|///][space...] from functionDescription
|
||||
// replace [^\S\r\n]+[\/]+\s* with empty string
|
||||
functionDescription = System.Text.RegularExpressions.Regex.Replace(functionDescription, @"[^\S\r\n]+\/[\/]+\s*", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
// get parameters
|
||||
var parameters = method.ParameterList.Parameters.Select(parameter =>
|
||||
{
|
||||
var description = $"{parameter.Identifier}. type is {parameter.Type}";
|
||||
|
||||
// try to get parameter description from documentationCommentTrivia
|
||||
var parameterDocumentationComment = documentationCommentTrivia?.GetParameterDescriptionFromDocumentationCommentTriviaSyntax(parameter.Identifier.ToString());
|
||||
if (parameterDocumentationComment is not null)
|
||||
{
|
||||
description = parameterDocumentationComment.ToString();
|
||||
// remove [space...][//|///][space...] from functionDescription
|
||||
// replace [^\S\r\n]+[\/]+\s* with empty string
|
||||
description = System.Text.RegularExpressions.Regex.Replace(description, @"[^\S\r\n]+\/[\/]+\s*", string.Empty);
|
||||
}
|
||||
var jsonItemType = parameter.Type!.ToString().EndsWith("[]") ? parameter.Type!.ToString().Substring(0, parameter.Type!.ToString().Length - 2) : null;
|
||||
return new SourceGeneratorParameterContract
|
||||
{
|
||||
Name = parameter.Identifier.ToString(),
|
||||
JsonType = parameter.Type!.ToString() switch
|
||||
{
|
||||
"string" => "string",
|
||||
"string[]" => "array",
|
||||
"System.Int32" or "int" => "integer",
|
||||
"System.Int64" or "long" => "integer",
|
||||
"System.Single" or "float" => "number",
|
||||
"System.Double" or "double" => "number",
|
||||
"System.Boolean" or "bool" => "boolean",
|
||||
"System.DateTime" => "string",
|
||||
"System.Guid" => "string",
|
||||
"System.Object" => "object",
|
||||
_ => "object",
|
||||
},
|
||||
JsonItemType = jsonItemType,
|
||||
Type = parameter.Type!.ToString(),
|
||||
Description = description,
|
||||
IsOptional = parameter.Default != null,
|
||||
// if Default is null or "null", then DefaultValue is null
|
||||
DefaultValue = parameter.Default?.ToString() == "null" ? null : parameter.Default?.Value.ToString(),
|
||||
};
|
||||
});
|
||||
|
||||
return new SourceGeneratorFunctionContract
|
||||
{
|
||||
ClassName = className,
|
||||
Namespace = namespaceName,
|
||||
Name = functionName,
|
||||
Description = functionDescription?.Trim() ?? functionName,
|
||||
Parameters = parameters.ToArray(),
|
||||
ReturnType = method.ReturnType.ToString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,40 +1,39 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SourceGeneratorFunctionContract.cs
|
||||
|
||||
namespace AutoGen.SourceGenerator
|
||||
namespace AutoGen.SourceGenerator;
|
||||
|
||||
internal class SourceGeneratorFunctionContract
|
||||
{
|
||||
internal class SourceGeneratorFunctionContract
|
||||
{
|
||||
public string? Namespace { get; set; }
|
||||
public string? Namespace { get; set; }
|
||||
|
||||
public string? ClassName { get; set; }
|
||||
public string? ClassName { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
public string? Description { get; set; }
|
||||
|
||||
public string? ReturnDescription { get; set; }
|
||||
public string? ReturnDescription { get; set; }
|
||||
|
||||
public SourceGeneratorParameterContract[]? Parameters { get; set; }
|
||||
public SourceGeneratorParameterContract[]? Parameters { get; set; }
|
||||
|
||||
public string? ReturnType { get; set; }
|
||||
}
|
||||
|
||||
internal class SourceGeneratorParameterContract
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public string? JsonType { get; set; }
|
||||
|
||||
public string? JsonItemType { get; set; }
|
||||
|
||||
public string? Type { get; set; }
|
||||
|
||||
public bool IsOptional { get; set; }
|
||||
|
||||
public string? DefaultValue { get; set; }
|
||||
|
||||
public string? ReturnType { get; set; }
|
||||
}
|
||||
|
||||
internal class SourceGeneratorParameterContract
|
||||
{
|
||||
public string? Name { get; set; }
|
||||
|
||||
public string? Description { get; set; }
|
||||
|
||||
public string? JsonType { get; set; }
|
||||
|
||||
public string? JsonItemType { get; set; }
|
||||
|
||||
public string? Type { get; set; }
|
||||
|
||||
public bool IsOptional { get; set; }
|
||||
|
||||
public string? DefaultValue { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user