diff --git a/sandbox/ConsoleApp1/ConsoleApp1.csproj b/sandbox/ConsoleApp1/ConsoleApp1.csproj
index 0364815..8935c24 100644
--- a/sandbox/ConsoleApp1/ConsoleApp1.csproj
+++ b/sandbox/ConsoleApp1/ConsoleApp1.csproj
@@ -7,7 +7,7 @@
enable
false
1591
- true
+ false
diff --git a/src/Claudia.FunctionGenerator/DiagnosticDescriptors.cs b/src/Claudia.FunctionGenerator/DiagnosticDescriptors.cs
index c818298..7fc8eaf 100644
--- a/src/Claudia.FunctionGenerator/DiagnosticDescriptors.cs
+++ b/src/Claudia.FunctionGenerator/DiagnosticDescriptors.cs
@@ -41,7 +41,7 @@ internal static class DiagnosticDescriptors
public static readonly DiagnosticDescriptor MethodNeedsDocumentationCommentXml = new(
id: "CLFG005",
title: "Method needs documentation comment xml",
- messageFormat: "The '{0}' method has no documentation comment, define it and generate it(true in PropertyGroup of csproj)",
+ messageFormat: "The '{0}' method has no documentation comment",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
diff --git a/src/Claudia.FunctionGenerator/Emitter.cs b/src/Claudia.FunctionGenerator/Emitter.cs
index b4c8545..ab14b25 100644
--- a/src/Claudia.FunctionGenerator/Emitter.cs
+++ b/src/Claudia.FunctionGenerator/Emitter.cs
@@ -96,18 +96,17 @@ public static class PromptXml
void EmitToolDescription(Method method)
{
- var docComment = method.Symbol.GetDocumentationCommentXml();
- var xml = XElement.Parse(docComment);
+ var docComment = method.Syntax.GetDocumentationCommentTriviaSyntax()!;
- var description = ((string)xml.Element("summary")).Replace("\"", "'").Trim();
+ var description = docComment.GetSummary().Replace("\"", "'");
var parameters = new List();
- foreach (var p in xml.Elements("param"))
+ foreach (var p in docComment.GetParams())
{
- var paramDescription = ((string)p).Replace("\"", "'").Trim();
+ var paramDescription = p.Description.Replace("\"", "'");
// type retrieve from method symbol
- var name = p.Attribute("name").Value.Trim();
+ var name = p.Name;
var paramType = method.Symbol.Parameters.First(x => x.Name == name).Type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
parameters.Add(new XElement("parameter",
diff --git a/src/Claudia.FunctionGenerator/Parser.cs b/src/Claudia.FunctionGenerator/Parser.cs
index 4c31088..64959ce 100644
--- a/src/Claudia.FunctionGenerator/Parser.cs
+++ b/src/Claudia.FunctionGenerator/Parser.cs
@@ -64,16 +64,15 @@ internal ParseResult[] Parse()
// source.TargetNode
var method = (IMethodSymbol)source.TargetSymbol;
- var docXml = method.GetDocumentationCommentXml();
- if (string.IsNullOrWhiteSpace(docXml))
+ var docComment = source.TargetNode.GetDocumentationCommentTriviaSyntax();
+ if (docComment == null)
{
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.MethodNeedsDocumentationCommentXml, method.Locations[0], method.Name));
continue;
}
else
{
- var xml = XElement.Parse(docXml);
- var description = ((string)xml.Element("summary")).Replace("\"", "'").Trim();
+ var description = docComment.GetSummary().Replace("\"", "'");
if (string.IsNullOrWhiteSpace(description))
{
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.MethodNeedsSummary, method.Locations[0], method.Name));
@@ -83,16 +82,16 @@ internal ParseResult[] Parse()
var parameterNames = new HashSet(method.Parameters.Select(x => x.Name));
if (parameterNames.Count != 0)
{
- foreach (var p in xml.Elements("param"))
+ foreach (var p in docComment.GetParams())
{
- var desc = (string)p;
+ var desc = p.Description;
if (string.IsNullOrWhiteSpace(desc))
{
context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.ParameterNeedsDescription, method.Locations[0], method.Name, p.Name));
continue;
}
- var name = p.Attribute("name").Value.Trim();
+ var name = p.Name;
parameterNames.Remove(name);
}
diff --git a/src/Claudia.FunctionGenerator/RoslynExtensions.cs b/src/Claudia.FunctionGenerator/RoslynExtensions.cs
new file mode 100644
index 0000000..dfb0f9d
--- /dev/null
+++ b/src/Claudia.FunctionGenerator/RoslynExtensions.cs
@@ -0,0 +1,86 @@
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.CodeAnalysis.CSharp;
+
+namespace Claudia.FunctionGenerator;
+
+internal static class RoslynExtensions
+{
+ public static DocumentationCommentTriviaSyntax? GetDocumentationCommentTriviaSyntax(this SyntaxNode node)
+ {
+ // Hack note:
+ // ISymbol.GetDocumentationCommtentXml requirestrue >.
+ // However, getting the DocumentationCommentTrivia of a SyntaxNode also requires the same condition.
+ // It can only be obtained when DocumentationMode is Parse or Diagnostic, but whenfalse >,
+ // it becomes None, and the necessary Trivia cannot be obtained.
+ // Therefore, we will attempt to reparse and retrieve it.
+
+ // About DocumentationMode and Trivia: https://github.com/dotnet/roslyn/issues/58210
+ if (node.SyntaxTree.Options.DocumentationMode == DocumentationMode.None)
+ {
+ var withDocumentationComment = node.SyntaxTree.Options.WithDocumentationMode(DocumentationMode.Parse);
+ var code = node.ToFullString();
+ var newTree = CSharpSyntaxTree.ParseText(code, (CSharpParseOptions)withDocumentationComment);
+ node = newTree.GetRoot();
+ }
+
+ foreach (var leadingTrivia in node.GetLeadingTrivia())
+ {
+ if (leadingTrivia.GetStructure() is DocumentationCommentTriviaSyntax structure)
+ {
+ return structure;
+ }
+ }
+
+ return null;
+ }
+
+ static IEnumerable GetXmlElements(this SyntaxList 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 string GetSummary(this DocumentationCommentTriviaSyntax docComment)
+ {
+ var summary = docComment.Content.GetXmlElements("summary").FirstOrDefault() as XmlElementSyntax;
+ if (summary == null) return "";
+
+ return summary.Content.ToString().Replace("///", "").Trim();
+ }
+
+ public static IEnumerable<(string Name, string Description)> GetParams(this DocumentationCommentTriviaSyntax docComment)
+ {
+ foreach (var item in docComment.Content.GetXmlElements("param").OfType())
+ {
+ var name = item.StartTag.Attributes.OfType().FirstOrDefault()?.Identifier.Identifier.ValueText.Replace("///", "").Trim() ?? "";
+ var desc = item.Content.ToString().Replace("///", "").Trim() ?? "";
+ yield return (name, desc);
+ }
+
+ yield break;
+ }
+}