diff --git a/DocGpt.Roslyn/DocGptCodeAction.cs b/DocGpt.Roslyn/DocGptCodeAction.cs index 9b21aaf..c8c48ee 100644 --- a/DocGpt.Roslyn/DocGptCodeAction.cs +++ b/DocGpt.Roslyn/DocGptCodeAction.cs @@ -56,9 +56,9 @@ protected override async Task GetChangedDocumentAsync(CancellationToke SyntaxNode root = await _doc.GetSyntaxRootAsync(cancellationToken); SyntaxNode node = root.FindNode(diagnosticSpan); - var newNode = await DocGptExecutor.AddXmlDocumentationAsync(node, cancellationToken); + var (newNode, nodeToReplace) = await DocGptExecutor.AddXmlDocumentationAsync(node, cancellationToken); - SyntaxNode newRoot = root.ReplaceNode(node, newNode); + SyntaxNode newRoot = root.ReplaceNode(nodeToReplace, newNode); // return a document with the new syntax root return _doc.WithSyntaxRoot(Formatter.Format(newRoot, _doc.Project.Solution.Workspace)); diff --git a/DocGpt.Roslyn/DocGptExecutor.cs b/DocGpt.Roslyn/DocGptExecutor.cs index f5dd6f1..0e21beb 100644 --- a/DocGpt.Roslyn/DocGptExecutor.cs +++ b/DocGpt.Roslyn/DocGptExecutor.cs @@ -42,35 +42,34 @@ internal static class DocGptExecutor /// The node to add XML documentation to. /// A cancellation token for the operation. /// A Task returning a Document with the new XML documentation added. - public static async Task AddXmlDocumentationAsync(SyntaxNode node, CancellationToken cancellationToken) + public static async Task<(SyntaxNode newNode, SyntaxNode nodeToReplace)> AddXmlDocumentationAsync(SyntaxNode node, CancellationToken cancellationToken) { - // Find the node at the diagnostic span - if (node != null) + _ = node ?? throw new ArgumentNullException(nameof(node)); + + if (HasOverrideModifier(node) && DocGptOptions.Instance.OverridesBehavior is OverrideBehavior.UseInheritDoc) { - if (HasOverrideModifier(node) && DocGptOptions.Instance.OverridesBehavior is OverrideBehavior.UseInheritDoc) - { - return DecorateWithInheritDoc(node); - } + return (DecorateWithInheritDoc(node), node); + } - if (IsConstantLiteral(node, out var parentField) && DocGptOptions.Instance.UseValueForLiteralConstants is true) - { - return DecorateWithValueAsSummary(parentField); - } + if (IsConstantLiteral(node, out var parentField) && DocGptOptions.Instance.UseValueForLiteralConstants is true) + { + return (DecorateWithValueAsSummary(parentField), parentField); + } - // Get the body of the method - string code = node.GetText().ToString(); + // Get the body of the method + string code = node.GetText().ToString(); - try - { - OpenAIClient client = DocGptOptions.Instance.GetClient(); + try + { + OpenAIClient client = DocGptOptions.Instance.GetClient(); - ChatCompletionsOptions completionOptions = new ChatCompletionsOptions(); - if (!string.IsNullOrWhiteSpace(DocGptOptions.Instance.ModelDeploymentName)) - { - completionOptions.DeploymentName = DocGptOptions.Instance.ModelDeploymentName; - } + ChatCompletionsOptions completionOptions = new ChatCompletionsOptions(); + if (!string.IsNullOrWhiteSpace(DocGptOptions.Instance.ModelDeploymentName)) + { + completionOptions.DeploymentName = DocGptOptions.Instance.ModelDeploymentName; + } - completionOptions.Messages.Add(new ChatRequestUserMessage($@"You are to take the C# code below and create a valid XML Documentation summary block for it according to .NET specifications. Use the following steps to determine what you compute for the answer: + completionOptions.Messages.Add(new ChatRequestUserMessage($@"You are to take the C# code below and create a valid XML Documentation summary block for it according to .NET specifications. Use the following steps to determine what you compute for the answer: 1. If the given code is not a complete C# type or member declaration, stop computing and return nothing. 2. If you're not able to discern the purpose of the code with reasonable certainty, just return `/// ` @@ -81,33 +80,30 @@ public static async Task AddXmlDocumentationAsync(SyntaxNode node, C You are to give back only the XML documentation wrapped in a code block (```), do not respond with any other text.")); - try - { - Azure.Response completion = await client.GetChatCompletionsAsync(completionOptions, cancellationToken); - string comment = completion.Value.Choices[0].Message.Content; - ExtractXmlDocComment(ref comment); - - SyntaxTriviaList commentTrivia = SyntaxFactory.ParseLeadingTrivia(comment).InsertRange(0, node.GetLeadingTrivia()); - // Add the comment to the start of the node found by the analyzer - return node.WithLeadingTrivia(commentTrivia); - } - catch (Exception e) - { - if (!(e is TaskCanceledException) && !(e is OperationCanceledException)) - { - System.Diagnostics.Debugger.Break(); - } + try + { + Azure.Response completion = await client.GetChatCompletionsAsync(completionOptions, cancellationToken); + string comment = completion.Value.Choices[0].Message.Content; + ExtractXmlDocComment(ref comment); - throw; - } + SyntaxTriviaList commentTrivia = SyntaxFactory.ParseLeadingTrivia(comment).InsertRange(0, node.GetLeadingTrivia()); + // Add the comment to the start of the node found by the analyzer + return (node.WithLeadingTrivia(commentTrivia), node); } - catch (ArgumentNullException e) + catch (Exception e) { - return node.WithLeadingTrivia(SyntaxFactory.Comment($"// Missing {e.ParamName} - Make sure you've entered the necessary information in Tools | Options | Doc GPT and try again.\r\n")); + if (!(e is TaskCanceledException) && !(e is OperationCanceledException)) + { + System.Diagnostics.Debugger.Break(); + } + + throw; } } - - return node; + catch (ArgumentNullException e) + { + return (node.WithLeadingTrivia(SyntaxFactory.Comment($"// Missing {e.ParamName} - Make sure you've entered the necessary information in Tools | Options | Doc GPT and try again.\r\n")), node); + } } internal static bool NodeTriggersGpt(SyntaxNode node) diff --git a/DocGpt.Test/AnalyzerTests.cs b/DocGpt.Test/AnalyzerTests.cs index 1192c01..c433e0a 100644 --- a/DocGpt.Test/AnalyzerTests.cs +++ b/DocGpt.Test/AnalyzerTests.cs @@ -84,7 +84,7 @@ class MyClass /// /// A Task. [TestMethod] - public async Task AnalyzerThrows_ConstLiteralMember() + public async Task AnalyzerThrows_ConstLiteralMember_UseValueForComment() { DocGptOptions.Instance.UseValueForLiteralConstants = true; @@ -114,7 +114,7 @@ internal class MyClass /// /// A Task. [TestMethod] - public async Task AnalyzerThrows_ConstLiteralMember_DoNotUseInheritDoc() + public async Task AnalyzerThrows_ConstLiteralMember_DoNotUseValueForComment() { DocGptOptions.Instance.UseValueForLiteralConstants = false; diff --git a/DocGpt.Test/CodeFixTests.cs b/DocGpt.Test/CodeFixTests.cs index c5e0f24..1a0d330 100644 --- a/DocGpt.Test/CodeFixTests.cs +++ b/DocGpt.Test/CodeFixTests.cs @@ -26,6 +26,8 @@ public static void AsmInit(TestContext _) DocGptOptions.Instance.ModelDeploymentName = "foo"; } + private const string ApiKey = "foo"; + /// /// Analyzers the throws class decl. /// diff --git a/DocGpt.Vsix/source.extension.vsixmanifest b/DocGpt.Vsix/source.extension.vsixmanifest index ce1860e..d493e78 100644 --- a/DocGpt.Vsix/source.extension.vsixmanifest +++ b/DocGpt.Vsix/source.extension.vsixmanifest @@ -1,7 +1,7 @@  - + Doc GPT Adds XML Documentation derived from an OpenAI GPT endpoint to .NET members. Can be used with either Azure OpenAI service or OpenAI.com account. https://github.com/bc3tech/docgpt