Skip to content

Commit

Permalink
Fixing auto-exec that got squashed by using external libs.
Browse files Browse the repository at this point in the history
Had to re-think how to "block" execution when Options aren't filled out. Not popping the Analyzer didn't work; affected firing
  • Loading branch information
bc3tech committed Jan 25, 2024
1 parent 39e4480 commit 5544ebc
Show file tree
Hide file tree
Showing 22 changed files with 96 additions and 144 deletions.
11 changes: 0 additions & 11 deletions DocGpt.Options/DocGpt.Options.csproj

This file was deleted.

5 changes: 0 additions & 5 deletions DocGpt.Options/Properties/AssemblyInfo.cs

This file was deleted.

13 changes: 12 additions & 1 deletion DocGpt.Roslyn/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
; Shipped analyzer releases
; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md

## Release 0.1

### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
DGPT001 | Documentation | Warning | Fires on any .NET member or type that lacks XML documentation comments.
DGPT001 | Documentation | Warning | Fires on any .NET member or type that lacks XML documentation comments.


## Release 0.6.5

### Changed Rules

Rule ID | New Category | New Severity | Old Category | Old Severity | Notes
--------|--------------|--------------|--------------|--------------|-------
DGPT001 | Documentation | Info | Documentation | Warning | Downgraded
2 changes: 0 additions & 2 deletions DocGpt.Roslyn/DocGpt.Roslyn.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,4 @@
</EmbeddedResource>
</ItemGroup>

<Import Project="..\DocGpt.Shared\DocGpt.Shared.projitems" Label="Shared" />

</Project>
14 changes: 4 additions & 10 deletions DocGpt.Roslyn/DocGptAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
namespace DocGpt
{
using DocGpt.Options;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
Expand All @@ -26,20 +24,14 @@ public class DocGptAnalyzer : DiagnosticAnalyzer
private static readonly LocalizableString Description = new LocalizableResourceString(nameof(AnalyzerResources.AnalyzerDescription), AnalyzerResources.ResourceManager, typeof(AnalyzerResources));
private const string Category = "Documentation";

internal static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
internal static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Info, isEnabledByDefault: true, description: Description);

/// <inheritdoc />
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }

/// <inheritdoc />
public override void Initialize(AnalysisContext context)
{
if (DocGptOptions.Instance.ApiKey is null
|| string.IsNullOrWhiteSpace(DocGptOptions.Instance.ApiKey))
{
return;
}

context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();

Expand All @@ -49,12 +41,14 @@ public override void Initialize(AnalysisContext context)
}
}

private void AnalyzeNode(SyntaxNodeAnalysisContext context) => AnalyzeNode(context, false);

/// <summary>
/// Analyzes the given syntax node.
/// If XML documentation exists for the node, no diagnostic is reported.
/// If no XML documentation is found, a diagnostic for missing XML documentation is created and reported.
/// </summary>
private static void AnalyzeNode(SyntaxNodeAnalysisContext context)
private void AnalyzeNode(SyntaxNodeAnalysisContext context, bool evented)
{
SyntaxNode node = context.Node;

Expand Down
18 changes: 17 additions & 1 deletion DocGpt.Roslyn/DocGptCodeAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
{
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Formatting;

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

using Document = Microsoft.CodeAnalysis.Document;

/// <summary>
/// This is an internal implementaion of CodeAction, that uses OpenAI technology for generating summary text for a member's definition.
/// Preview and Change operations are overridden to add XML documentation to a document at a specified location. This happens asynchronously.
Expand Down Expand Up @@ -46,7 +49,20 @@ protected override async Task<IEnumerable<CodeActionOperation>> ComputePreviewOp
}

/// <inheritdoc />
protected override Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken) => DocGptExecutor.AddXmlDocumentationAsync(_doc, _location, cancellationToken);
protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
{
// Find the node at the diagnostic span
Microsoft.CodeAnalysis.Text.TextSpan diagnosticSpan = _location.SourceSpan;
SyntaxNode root = await _doc.GetSyntaxRootAsync(cancellationToken);
SyntaxNode node = root.FindNode(diagnosticSpan);

var newNode = await DocGptExecutor.AddXmlDocumentationAsync(node, cancellationToken);

SyntaxNode newRoot = root.ReplaceNode(node, newNode);

// return a document with the new syntax root
return _doc.WithSyntaxRoot(Formatter.Format(newRoot, _doc.Project.Solution.Workspace));
}

/// <summary>
/// Represents an operation that sends the entire member's definition to the defined OpenAI endpoint for summary text generation and applies the result.
Expand Down
72 changes: 35 additions & 37 deletions DocGpt.Roslyn/DocGptExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Formatting;

using System;
using System.Threading;
Expand Down Expand Up @@ -40,41 +39,38 @@ internal static class DocGptExecutor
/// <summary>
/// Asynchronously adds XML documentation to a given document based on a provided diagnostic, using GPT for generating the documentation.
/// </summary>
/// <param name="document">The document to which XML documentation should be added.</param>
/// <param name="diagnostic">The diagnostic information used to generate the XML documentation.</param>
/// <param name="node">The node to add XML documentation to.</param>
/// <param name="cancellationToken">A cancellation token for the operation.</param>
/// <returns>A Task returning a Document with the new XML documentation added.</returns>
public static async Task<Document> AddXmlDocumentationAsync(Document document, Location location, CancellationToken cancellationToken)
public static async Task<SyntaxNode> AddXmlDocumentationAsync(SyntaxNode node, CancellationToken cancellationToken)
{
OpenAIClient client = DocGptOptions.Instance.GetClient();

ChatCompletionsOptions completionOptions = new ChatCompletionsOptions();
if (!string.IsNullOrWhiteSpace(DocGptOptions.Instance.ModelDeploymentName))
{
completionOptions.DeploymentName = DocGptOptions.Instance.ModelDeploymentName;
}

// Find the node at the diagnostic span
Microsoft.CodeAnalysis.Text.TextSpan diagnosticSpan = location.SourceSpan;
SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken);
SyntaxNode node = root.FindNode(diagnosticSpan);

if (node != null)
{
if (HasOverrideModifier(node) && DocGptOptions.Instance.OverridesBehavior is OverrideBehavior.UseInheritDoc)
{
return await DecorateWithInheritDocAsync(node, document, cancellationToken);
return DecorateWithInheritDoc(node);
}

if (IsConstantLiteral(node, out var parentField) && DocGptOptions.Instance.UseValueForLiteralConstants is true)
{
return await DecorateWithValueAsSummaryAsync(parentField, document, cancellationToken);
return DecorateWithValueAsSummary(parentField);
}

// Get the body of the method
string code = node.GetText().ToString();

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:
try
{
OpenAIClient client = DocGptOptions.Instance.GetClient();

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:
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 `/// <summary />`
Expand All @@ -85,31 +81,33 @@ public static async Task<Document> AddXmlDocumentationAsync(Document document, L
You are to give back only the XML documentation wrapped in a code block (```), do not respond with any other text."));

try
{
Azure.Response<ChatCompletions> completion = await client.GetChatCompletionsAsync(completionOptions, cancellationToken);
string comment = completion.Value.Choices[0].Message.Content;
ExtractXmlDocComment(ref comment);
try
{
Azure.Response<ChatCompletions> 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
SyntaxNode newRoot = root.ReplaceNode(node, node.WithLeadingTrivia(commentTrivia));
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();
}

// return a document with the new syntax root
document = document.WithSyntaxRoot(Formatter.Format(newRoot, document.Project.Solution.Workspace));
throw;
}
}
catch (Exception e)
catch (ArgumentNullException e)
{
if (!(e is TaskCanceledException) && !(e is OperationCanceledException))
{
System.Diagnostics.Debugger.Break();
}

throw;
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"));
}
}

return document;
return node;
}

internal static bool NodeTriggersGpt(SyntaxNode node)
Expand Down
16 changes: 4 additions & 12 deletions DocGpt.Roslyn/DocGptRefactoringProvider.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
namespace DocGpt
{
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp;

using System;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using DocGpt.Options;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp;

/// <summary>
/// The doc gpt code fix provider.
/// </summary>
Expand All @@ -20,12 +18,6 @@ public class DocGptRefactoringProvider : CodeRefactoringProvider
{
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
if (DocGptOptions.Instance.Endpoint is null
|| string.IsNullOrWhiteSpace(DocGptOptions.Instance.Endpoint.OriginalString))
{
return;
}

Document document = context.Document;
Microsoft.CodeAnalysis.Text.TextSpan textSpan = context.Span;
CancellationToken cancellationToken = context.CancellationToken;
Expand Down
13 changes: 4 additions & 9 deletions DocGpt.Roslyn/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,23 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;

using System.Linq;
using System.Threading;
using System.Threading.Tasks;

internal static class Helpers
{
public static bool HasOverrideModifier(SyntaxNode node) => node is MemberDeclarationSyntax m && m.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.OverrideKeyword));

public static Task<Document> DecorateWithInheritDocAsync(SyntaxNode node, Document document, CancellationToken cancellationToken = default) => DecorateWithXmlDocumentationAsync(node, document, @"/// <inheritdoc />", cancellationToken);
public static SyntaxNode DecorateWithInheritDoc(SyntaxNode node) => DecorateWithXmlDocumentation(node, @"/// <inheritdoc />");

public static Task<Document> DecorateWithValueAsSummaryAsync(FieldDeclarationSyntax node, Document document, CancellationToken cancellationToken) => DecorateWithXmlDocumentationAsync(node, document, $"/// <summary>{node.Declaration.Variables[0].Initializer.Value.ChildTokens().First().ValueText}</summary>", cancellationToken);
public static SyntaxNode DecorateWithValueAsSummary(FieldDeclarationSyntax node) => DecorateWithXmlDocumentation(node, $"/// <summary>{node.Declaration.Variables[0].Initializer.Value.ChildTokens().First().ValueText}</summary>");

public static async Task<Document> DecorateWithXmlDocumentationAsync(SyntaxNode node, Document document, string documentationContent, CancellationToken cancellationToken = default)
public static SyntaxNode DecorateWithXmlDocumentation(SyntaxNode node, string documentationContent)
{
SyntaxTriviaList commentTrivia = SyntaxFactory.ParseLeadingTrivia($@"{documentationContent}
").InsertRange(0, node.GetLeadingTrivia());

SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken);
SyntaxNode newRoot = root.ReplaceNode(node, node.WithLeadingTrivia(commentTrivia));
return document.WithSyntaxRoot(Formatter.Format(newRoot, document.Project.Solution.Workspace));
return node.WithLeadingTrivia(commentTrivia);
}

public static bool ShouldSkip(SyntaxNode node)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
/// This includes properties required for connection such as the endpoint URL and API key.
/// It also includes the name of the model or deployment to use.
/// </summary>
public class DocGptOptions
public sealed class DocGptOptions
{
public static readonly DocGptOptions Instance = new DocGptOptions();
/// <summary>
/// Initializes a new instance of the <see cref="DocGptOptions"/> class.
/// </summary>
protected DocGptOptions() { }
private DocGptOptions() { }

/// <summary>
/// Gets or Sets the endpoint.
Expand Down Expand Up @@ -68,8 +68,8 @@ public OpenAIClient GetClient()
{
_client =
_endpoint.Host.EndsWith("azure.com", StringComparison.OrdinalIgnoreCase)
? new OpenAIClient(_endpoint, new Azure.AzureKeyCredential(_apiKey))
: new OpenAIClient(_apiKey);
? new OpenAIClient(_endpoint ?? throw new ArgumentNullException(nameof(Endpoint)), new Azure.AzureKeyCredential(_apiKey ?? throw new ArgumentNullException(nameof(ApiKey))))
: new OpenAIClient(_apiKey ?? throw new ArgumentNullException(nameof(ApiKey)));
}

return _client;
Expand Down
File renamed without changes.
15 changes: 0 additions & 15 deletions DocGpt.Shared/DocGpt.Shared.projitems

This file was deleted.

13 changes: 0 additions & 13 deletions DocGpt.Shared/DocGpt.Shared.shproj

This file was deleted.

6 changes: 3 additions & 3 deletions DocGpt.Test/Verifiers/CSharpAnalyzerVerifier`1.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
namespace DocGpt.Test
{
using System.Threading;
using System.Threading.Tasks;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing;
using Microsoft.CodeAnalysis.Testing.Verifiers;

using System.Threading;
using System.Threading.Tasks;

public static partial class CSharpAnalyzerVerifier<TAnalyzer>
where TAnalyzer : DiagnosticAnalyzer, new()
{
Expand Down
Loading

0 comments on commit 5544ebc

Please sign in to comment.