Skip to content

Commit

Permalink
Pkg updates (#18)
Browse files Browse the repository at this point in the history
* bumping nupkgs

* Updating to Azure OpenAI v2

Breaking changes in the API

* linting

* nupkg updates

* don't build test project in release mode
  • Loading branch information
bc3tech authored Dec 13, 2024
1 parent ad71798 commit 46ca68f
Show file tree
Hide file tree
Showing 23 changed files with 534 additions and 532 deletions.
13 changes: 13 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>

<PropertyGroup>
<RepoRoot>$(MSBuildThisFileDirectory)</RepoRoot>
</PropertyGroup>
</Project>
4 changes: 2 additions & 2 deletions DocGpt.Roslyn/DocGpt.Roslyn.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" Version="1.0.0-beta.17" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.11.0" />
<PackageReference Include="Azure.AI.OpenAI" Version="2.1.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.12.0" />
</ItemGroup>

<ItemGroup>
Expand Down
115 changes: 57 additions & 58 deletions DocGpt.Roslyn/DocGptAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -1,76 +1,75 @@
namespace DocGpt
{
using System.Collections.Immutable;
namespace DocGpt;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

using static Helpers;
using System.Collections.Immutable;

/// <summary>
/// Represents a diagnostic analyzer for the DocGptAnalyzer class.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class DocGptAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "DGPT001";
using static Helpers;

// You can change these strings in the AnalyzerResources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat.
// See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Localizing%20Analyzers.md for more on localization
private static readonly LocalizableString Title = new LocalizableResourceString(nameof(AnalyzerResources.AnalyzerTitle), AnalyzerResources.ResourceManager, typeof(AnalyzerResources));
private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(AnalyzerResources.AnalyzerMessageFormat), AnalyzerResources.ResourceManager, typeof(AnalyzerResources));
private static readonly LocalizableString Description = new LocalizableResourceString(nameof(AnalyzerResources.AnalyzerDescription), AnalyzerResources.ResourceManager, typeof(AnalyzerResources));
private const string Category = "Documentation";
/// <summary>
/// Represents a diagnostic analyzer for the DocGptAnalyzer class.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class DocGptAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "DGPT001";

internal static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Info, isEnabledByDefault: true, description: Description);
// You can change these strings in the AnalyzerResources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat.
// See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Localizing%20Analyzers.md for more on localization
private static readonly LocalizableString Title = new LocalizableResourceString(nameof(AnalyzerResources.AnalyzerTitle), AnalyzerResources.ResourceManager, typeof(AnalyzerResources));
private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(AnalyzerResources.AnalyzerMessageFormat), AnalyzerResources.ResourceManager, typeof(AnalyzerResources));
private static readonly LocalizableString Description = new LocalizableResourceString(nameof(AnalyzerResources.AnalyzerDescription), AnalyzerResources.ResourceManager, typeof(AnalyzerResources));
private const string Category = "Documentation";

/// <inheritdoc />
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
internal static readonly DiagnosticDescriptor Rule = new(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Info, isEnabledByDefault: true, description: Description);

/// <inheritdoc />
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
/// <inheritdoc />
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

foreach (SyntaxKind k in DocGptExecutor.SupportedSyntaxes)
{
context.RegisterSyntaxNodeAction(AnalyzeNode, k);
}
}
/// <inheritdoc />
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();

/// <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 void AnalyzeNode(SyntaxNodeAnalysisContext context)
foreach (SyntaxKind k in DocGptExecutor.SupportedSyntaxes)
{
SyntaxNode node = context.Node;
context.RegisterSyntaxNodeAction(AnalyzeNode, k);
}
}

if (ShouldSkip(node))
{
return;
}
/// <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 void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
SyntaxNode node = context.Node;

// Create and report the diagnostic
(Location loc, var name) = getLocationAndName();
var diagnostic = Diagnostic.Create(Rule, loc, node.Kind(), name);
context.ReportDiagnostic(diagnostic);
if (ShouldSkip(node))
{
return;
}

(Location, string) getLocationAndName()
{
if (node is FieldDeclarationSyntax fs)
{
SyntaxToken vi = fs.Declaration.Variables.First().Identifier;
return (vi.GetLocation(), vi.ValueText);
}
// Create and report the diagnostic
(Location loc, var name) = getLocationAndName();
var diagnostic = Diagnostic.Create(Rule, loc, node.Kind(), name);
context.ReportDiagnostic(diagnostic);

ISymbol symbol = context.SemanticModel.GetDeclaredSymbol(node);
return (symbol.Locations[0], symbol.Name);
(Location, string) getLocationAndName()
{
if (node is FieldDeclarationSyntax fs)
{
SyntaxToken vi = fs.Declaration.Variables.First().Identifier;
return (vi.GetLocation(), vi.ValueText);
}

ISymbol symbol = context.SemanticModel.GetDeclaredSymbol(node);

Check warning on line 71 in DocGpt.Roslyn/DocGptAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build-and-test / run-tests

Converting null literal or possible null value to non-nullable type.
return (symbol.Locations[0], symbol.Name);

Check warning on line 72 in DocGpt.Roslyn/DocGptAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build-and-test / run-tests

Dereference of a possibly null reference.
}
}
}
130 changes: 63 additions & 67 deletions DocGpt.Roslyn/DocGptCodeAction.cs
Original file line number Diff line number Diff line change
@@ -1,88 +1,84 @@
namespace DocGpt
namespace DocGpt;

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 implementation 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.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="DocGptCodeAction"/> class.
/// </remarks>
/// <param name="doc">The document associated with the action.</param>
/// <param name="location">The location within the document where the action is applied.</param>
internal class DocGptCodeAction(Document doc, Location location) : CodeAction
{
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Formatting;
/// <inheritdoc />
public override string EquivalenceKey { get; } = nameof(CodeFixResources.CodeFixTitle);

using Document = Microsoft.CodeAnalysis.Document;
/// <inheritdoc />
public override string Title { get; } = CodeFixResources.CodeFixTitle;

/// <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.
/// </summary>
internal class DocGptCodeAction : CodeAction
/// <inheritdoc />
protected override async Task<IEnumerable<CodeActionOperation>> ComputePreviewOperationsAsync(CancellationToken cancellationToken)
{
private readonly Document _doc;
private readonly Location _location;

/// <summary>
/// Initializes a new instance of the <see cref="DocGptCodeAction"/> class.
/// </summary>
/// <param name="doc">The document associated with the action.</param>
/// <param name="location">The location within the document where the action is applied.</param>
public DocGptCodeAction(Document doc, Location location)
{
_doc = doc;
_location = location;
}
Microsoft.CodeAnalysis.Text.TextSpan diagnosticSpan = location.SourceSpan;
SyntaxNode? root = await doc.GetSyntaxRootAsync(cancellationToken);
SyntaxNode? node = root?.FindNode(diagnosticSpan);

/// <inheritdoc />
public override string EquivalenceKey { get; } = nameof(CodeFixResources.CodeFixTitle);

/// <inheritdoc />
public override string Title { get; } = CodeFixResources.CodeFixTitle;

/// <inheritdoc />
protected override async Task<IEnumerable<CodeActionOperation>> ComputePreviewOperationsAsync(CancellationToken cancellationToken)
{
Microsoft.CodeAnalysis.Text.TextSpan diagnosticSpan = _location.SourceSpan;
SyntaxNode root = await _doc.GetSyntaxRootAsync(cancellationToken);
SyntaxNode node = root.FindNode(diagnosticSpan);
return node is not null && DocGptExecutor.NodeTriggersGpt(node)
? DocGptCodeActionPreviewOperation.InstanceArray
: await base.ComputePreviewOperationsAsync(cancellationToken);
}

return DocGptExecutor.NodeTriggersGpt(node)
? DocGptCodeActionPreviewOperation.InstanceArray
: await base.ComputePreviewOperationsAsync(cancellationToken);
}
/// <inheritdoc />
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);

/// <inheritdoc />
protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
if (node is not null)
{
// 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);

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

SyntaxNode newRoot = root.ReplaceNode(nodeToReplace, 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));
return newRoot is null ? doc : doc.WithSyntaxRoot(Formatter.Format(newRoot, doc.Project.Solution.Workspace));
}

return doc;
}

/// <summary>
/// Represents an operation that sends the entire member's definition to the defined OpenAI endpoint for summary text generation and applies the result.
/// This operation provides a preview of such operation in its title property. The preview can be asynchronously obtained via GetPreviewAsync method.
/// </summary>
private class DocGptCodeActionPreviewOperation : PreviewOperation
{
public static readonly DocGptCodeActionPreviewOperation Instance = new();
public static readonly IEnumerable<CodeActionOperation> InstanceArray = [Instance];

/// <summary>
/// Represents an operation that sends the entire member's definition to the defined OpenAI endpoint for summary text generation and applies the result.
/// This operation provides a preview of such operation in its title property. The preview can be asynchronously obtained via GetPreviewAsync method.
/// Initializes a new instance of the DocGptCodeActionPreviewOperation class.
/// </summary>
private class DocGptCodeActionPreviewOperation : PreviewOperation
{
public static readonly DocGptCodeActionPreviewOperation Instance = new DocGptCodeActionPreviewOperation();
public static readonly IEnumerable<CodeActionOperation> InstanceArray = new CodeActionOperation[] { Instance };

/// <summary>
/// Initializes a new instance of the DocGptCodeActionPreviewOperation class.
/// </summary>
private DocGptCodeActionPreviewOperation() { }
private DocGptCodeActionPreviewOperation() { }

/// <inheritdoc />
public override string Title { get; } = "Sends this entire member's definition (and body) to the defined OpenAI endpoint for summary text generation and applies the result.";
/// <inheritdoc />
public override string Title { get; } = "Sends this entire member's definition (and body) to the defined OpenAI endpoint for summary text generation and applies the result.";

/// <inheritdoc />
public override Task<object> GetPreviewAsync(CancellationToken cancellationToken) => Task.FromResult<object>(this.Title);
}
/// <inheritdoc />
public override Task<object?> GetPreviewAsync(CancellationToken cancellationToken) => Task.FromResult<object?>(this.Title);
}
}
Loading

0 comments on commit 46ca68f

Please sign in to comment.