Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pkg updates #18

Merged
merged 6 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 / 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 / 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
Loading