Skip to content
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
17 changes: 8 additions & 9 deletions AutoEntityGenerator/AppSettings.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
using AutoEntityGenerator.Common.Interfaces;
using Microsoft.Extensions.Logging;

namespace AutoEntityGenerator
namespace AutoEntityGenerator;

internal class AppSettings : IAppSettings
{
internal class AppSettings : IAppSettings
{
public LogLevel MinimumLogLevel { get; set; }
public string DestinationFolder { get; set; }
public string RequestSuffix { get; set; }
public string ResponseSuffix { get; set; }
public bool OpenGeneratedFiles { get; set; }
}
public LogLevel MinimumLogLevel { get; set; }
public string DestinationFolder { get; set; }
public string RequestSuffix { get; set; }
public string ResponseSuffix { get; set; }
public bool OpenGeneratedFiles { get; set; }
}
2 changes: 1 addition & 1 deletion AutoEntityGenerator/AutoEntityGenerator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
<LangVersion>latests</LangVersion>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
75 changes: 37 additions & 38 deletions AutoEntityGenerator/AutoEntityGeneratorCodeRefactoringProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,50 @@
using System.Threading.Tasks;


namespace AutoEntityGenerator
namespace AutoEntityGenerator;

[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(AutoEntityGeneratorCodeRefactoringProvider)), Shared]
internal class AutoEntityGeneratorCodeRefactoringProvider : CodeRefactoringProvider
{
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(AutoEntityGeneratorCodeRefactoringProvider)), Shared]
internal class AutoEntityGeneratorCodeRefactoringProvider : CodeRefactoringProvider
private readonly ILogger<AutoEntityGeneratorCodeRefactoringProvider> _logger;
private readonly ICodeActionFactory _codeActionFactory;
private readonly IServices _services;

public AutoEntityGeneratorCodeRefactoringProvider()
{
_services = Services.Instance;
_logger = _services.GetService<ILogger<AutoEntityGeneratorCodeRefactoringProvider>>();
_codeActionFactory = _services.GetService<ICodeActionFactory>();
_logger.LogInformation("AutoEntityGenerator started.");
}

public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
private readonly ILogger<AutoEntityGeneratorCodeRefactoringProvider> _logger;
private readonly ICodeActionFactory _codeActionFactory;
private readonly IServices _services;
_logger.LogDebug("ComputeRefactoringsAsync called.");
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var node = root.FindNode(context.Span);

public AutoEntityGeneratorCodeRefactoringProvider()
if (!(node is ClassDeclarationSyntax || node is RecordDeclarationSyntax))
{
_services = Services.Instance;
_logger = _services.GetService<ILogger<AutoEntityGeneratorCodeRefactoringProvider>>();
_codeActionFactory = _services.GetService<ICodeActionFactory>();
_logger.LogInformation("AutoEntityGenerator started.");
return;
}
var typeDecleration = node as TypeDeclarationSyntax;
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
if (semanticModel.GetDeclaredSymbol(typeDecleration) is not INamedTypeSymbol typeSymbol)
{
return;
}

public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
var hasPublicProperties = typeSymbol.GetMembers().OfType<IPropertySymbol>().Any(p => p.DeclaredAccessibility == Accessibility.Public);

if (!hasPublicProperties)
{
_logger.LogDebug("ComputeRefactoringsAsync called.");
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var node = root.FindNode(context.Span);

if (!(node is ClassDeclarationSyntax || node is RecordDeclarationSyntax))
{
return;
}
var typeDecleration = node as TypeDeclarationSyntax;
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
if (!(semanticModel.GetDeclaredSymbol(typeDecleration) is INamedTypeSymbol typeSymbol))
{
return;
}

var hasPublicProperties = typeSymbol.GetMembers().OfType<IPropertySymbol>().Any(p => p.DeclaredAccessibility == Accessibility.Public);

if (!hasPublicProperties)
{
return;
}

_logger.LogDebug("attempting to register refactoring.");
var action = _codeActionFactory.CreateEntityGeneratorCodeAction(context.Document, typeDecleration, typeSymbol);
context.RegisterRefactoring(action);
_logger.LogDebug("refactoring registered.");
return;
}

_logger.LogDebug("attempting to register refactoring.");
var action = _codeActionFactory.CreateEntityGeneratorCodeAction(context.Document, typeDecleration, typeSymbol);
context.RegisterRefactoring(action);
_logger.LogDebug("refactoring registered.");
}
}
93 changes: 46 additions & 47 deletions AutoEntityGenerator/CodeActionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,54 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Extensions.Logging;

namespace AutoEntityGenerator
namespace AutoEntityGenerator;

internal interface ICodeActionFactory
{
internal interface ICodeActionFactory
{
EntityGeneratorCodeAction CreateEntityGeneratorCodeAction(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol);
GenerateCodeOperation CreateGenerateCodeOperation(IUIResultProvider uiResultProvider, IEntityProvider entityProvider, Document document);
GetEntityInfoOperation CreateGetEntityInfoOperation(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol);
GetUserInputOperation CreateGetUserInputOperation(IEntityProvider entityProvider);
}
EntityGeneratorCodeAction CreateEntityGeneratorCodeAction(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol);
GenerateCodeOperation CreateGenerateCodeOperation(IUIResultProvider uiResultProvider, IEntityProvider entityProvider, Document document);
GetEntityInfoOperation CreateGetEntityInfoOperation(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol);
GetUserInputOperation CreateGetUserInputOperation(IEntityProvider entityProvider);
}

internal class CodeActionFactory : ICodeActionFactory
{
private readonly ILogger<EntityGeneratorCodeAction> _entityGeneratorCodeActionLogger;
private readonly ILogger<GenerateCodeOperation> _generateCodeOperationLogger;
private readonly ILogger<GetEntityInfoOperation> _getEntityInfoOperationLogger;
private readonly IEntityGenerator _entityGenerator;
private readonly IUserInteraction _userInteraction;
private readonly ICodeFileGenerator _codeGenerator;
private readonly IDocumentOpener _documentOpener;

internal class CodeActionFactory : ICodeActionFactory
public CodeActionFactory(ILogger<EntityGeneratorCodeAction> entityGeneratorCodeActionLogger, ILogger<GenerateCodeOperation> generateCodeOperationLogger, ILogger<GetEntityInfoOperation> getEntityInfoOperationLogger, IEntityGenerator entityGenerator, IUserInteraction userInteraction, ICodeFileGenerator codeGenerator, IDocumentOpener documentOpener)
{
private readonly ILogger<EntityGeneratorCodeAction> _entityGeneratorCodeActionLogger;
private readonly ILogger<GenerateCodeOperation> _generateCodeOperationLogger;
private readonly ILogger<GetEntityInfoOperation> _getEntityInfoOperationLogger;
private readonly IEntityGenerator _entityGenerator;
private readonly IUserInteraction _userInteraction;
private readonly ICodeFileGenerator _codeGenerator;
private readonly IDocumentOpener _documentOpener;

public CodeActionFactory(ILogger<EntityGeneratorCodeAction> entityGeneratorCodeActionLogger, ILogger<GenerateCodeOperation> generateCodeOperationLogger, ILogger<GetEntityInfoOperation> getEntityInfoOperationLogger, IEntityGenerator entityGenerator, IUserInteraction userInteraction, ICodeFileGenerator codeGenerator, IDocumentOpener documentOpener)
{
_entityGeneratorCodeActionLogger = entityGeneratorCodeActionLogger;
_generateCodeOperationLogger = generateCodeOperationLogger;
_getEntityInfoOperationLogger = getEntityInfoOperationLogger;
_entityGenerator = entityGenerator;
_userInteraction = userInteraction;
_codeGenerator = codeGenerator;
_documentOpener = documentOpener;
}

public EntityGeneratorCodeAction CreateEntityGeneratorCodeAction(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol)
=> new EntityGeneratorCodeAction(_entityGeneratorCodeActionLogger, document, typeDeclaration, this, typeSymbol);

public GetUserInputOperation CreateGetUserInputOperation(IEntityProvider entityProvider)
=> new GetUserInputOperation(entityProvider, _userInteraction);

public GenerateCodeOperation CreateGenerateCodeOperation(IUIResultProvider uiResultProvider, IEntityProvider entityProvider, Document document)
=> new GenerateCodeOperation(
uiResultProvider,
_entityGenerator,
_codeGenerator,
entityProvider,
document,
_generateCodeOperationLogger,
_documentOpener
);

public GetEntityInfoOperation CreateGetEntityInfoOperation(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol)
=> new GetEntityInfoOperation(_getEntityInfoOperationLogger, document, typeDeclaration, typeSymbol, _entityGenerator);
_entityGeneratorCodeActionLogger = entityGeneratorCodeActionLogger;
_generateCodeOperationLogger = generateCodeOperationLogger;
_getEntityInfoOperationLogger = getEntityInfoOperationLogger;
_entityGenerator = entityGenerator;
_userInteraction = userInteraction;
_codeGenerator = codeGenerator;
_documentOpener = documentOpener;
}

public EntityGeneratorCodeAction CreateEntityGeneratorCodeAction(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol)
=> new(_entityGeneratorCodeActionLogger, document, typeDeclaration, this, typeSymbol);

public GetUserInputOperation CreateGetUserInputOperation(IEntityProvider entityProvider)
=> new(entityProvider, _userInteraction);

public GenerateCodeOperation CreateGenerateCodeOperation(IUIResultProvider uiResultProvider, IEntityProvider entityProvider, Document document)
=> new(
uiResultProvider,
_entityGenerator,
_codeGenerator,
entityProvider,
document,
_generateCodeOperationLogger,
_documentOpener
);

public GetEntityInfoOperation CreateGetEntityInfoOperation(Document document, TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol)
=> new(_getEntityInfoOperationLogger, document, typeDeclaration, typeSymbol, _entityGenerator);
}
139 changes: 69 additions & 70 deletions AutoEntityGenerator/CodeOperations/GenerateCodeOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,96 +8,95 @@
using System.Runtime.InteropServices;
using System.Threading;

namespace AutoEntityGenerator.CodeOperations
namespace AutoEntityGenerator.CodeOperations;

internal class GenerateCodeOperation : CodeActionOperation
{
internal class GenerateCodeOperation : CodeActionOperation
private readonly ILogger<GenerateCodeOperation> _logger;
private readonly IUIResultProvider _resultProvider;
private readonly IEntityGenerator _entityGenerator;
private readonly IEntityProvider _entityProvider;
private readonly ICodeFileGenerator _codeGenerator;
private readonly IDocumentOpener _documentOpener;
private readonly Document _document;

public GenerateCodeOperation(IUIResultProvider getUserInputOperation, IEntityGenerator entityGenerator, ICodeFileGenerator codeGenerator, IEntityProvider entityProvider, Document document, ILogger<GenerateCodeOperation> logger, IDocumentOpener documentOpener)
{
private readonly ILogger<GenerateCodeOperation> _logger;
private readonly IUIResultProvider _resultProvider;
private readonly IEntityGenerator _entityGenerator;
private readonly IEntityProvider _entityProvider;
private readonly ICodeFileGenerator _codeGenerator;
private readonly IDocumentOpener _documentOpener;
private readonly Document _document;
_resultProvider = getUserInputOperation;
_codeGenerator = codeGenerator;
_entityProvider = entityProvider;
_document = document;
_entityGenerator = entityGenerator;
_logger = logger;
_documentOpener = documentOpener;
}

public GenerateCodeOperation(IUIResultProvider getUserInputOperation, IEntityGenerator entityGenerator, ICodeFileGenerator codeGenerator, IEntityProvider entityProvider, Document document, ILogger<GenerateCodeOperation> logger, IDocumentOpener documentOpener)
public override void Apply(Workspace workspace, CancellationToken cancellationToken)
{
var result = _resultProvider.UserInteractionResult;
if (!result.IsOk)
{
_resultProvider = getUserInputOperation;
_codeGenerator = codeGenerator;
_entityProvider = entityProvider;
_document = document;
_entityGenerator = entityGenerator;
_logger = logger;
_documentOpener = documentOpener;
_logger.LogInformation("The user cancelled the operation.");
return;
}

public override void Apply(Workspace workspace, CancellationToken cancellationToken)
{
var result = _resultProvider.UserInteractionResult;
if (!result.IsOk)
{
_logger.LogInformation("The user cancelled the operation.");
return;
}

_logger.LogInformation("Attempting to generate dto {dtoName} and mapping extension.", result.EntityName);
_logger.LogInformation("Attempting to generate dto {dtoName} and mapping extension.", result.EntityName);

var sourceEntity = _entityProvider.Entity;
var dtoEntity = _entityGenerator.GenerateFromUIResult(result, sourceEntity);
var sourceEntity = _entityProvider.Entity;
var dtoEntity = _entityGenerator.GenerateFromUIResult(result, sourceEntity);

var (from, to) = result.MappingDirection == MappingDirection.FromDtoToModel
? (dtoEntity, sourceEntity)
: (sourceEntity, dtoEntity);
var (from, to) = result.MappingDirection == MappingDirection.FromDtoToModel
? (dtoEntity, sourceEntity)
: (sourceEntity, dtoEntity);

var dto = _codeGenerator.GenerateEntityCodeFile(dtoEntity);
var mapping = _codeGenerator.GenerateMappingCodeFile(from, to);
_logger.LogDebug("Dto and Mapping generated.");
var dtoDocument = AddDocument(_document, dto.FileName, dto.Content, result.TargetDirectory, sourceEntity);
var mappingDocument = AddDocument(dtoDocument, mapping.FileName, mapping.Content, result.TargetDirectory, sourceEntity);
var dto = _codeGenerator.GenerateEntityCodeFile(dtoEntity);
var mapping = _codeGenerator.GenerateMappingCodeFile(from, to);
_logger.LogDebug("Dto and Mapping generated.");
var dtoDocument = AddDocument(_document, dto.FileName, dto.Content, result.TargetDirectory, sourceEntity);
var mappingDocument = AddDocument(dtoDocument, mapping.FileName, mapping.Content, result.TargetDirectory, sourceEntity);

_logger.LogDebug("Attempting to save changes.");
_logger.LogDebug("Attempting to save changes.");

try
try
{
if (workspace.TryApplyChanges(mappingDocument.Project.Solution))
{
if (workspace.TryApplyChanges(mappingDocument.Project.Solution))
{
_logger.LogInformation("Dto and mapping extension class saved to {TargetDirectory}.", result.TargetDirectory);
if (result.OpenFiles)
{
_documentOpener.OpenDocument(mappingDocument.FilePath);
_documentOpener.OpenDocument(dtoDocument.FilePath);
}
}
else
_logger.LogInformation("Dto and mapping extension class saved to {TargetDirectory}.", result.TargetDirectory);
if (result.OpenFiles)
{
_logger.LogWarning("TryApplyChanges returned false. Changes was not saved.");
_documentOpener.OpenDocument(mappingDocument.FilePath);
_documentOpener.OpenDocument(dtoDocument.FilePath);
}
}
// This will be thrown if the Apply method doesn't run on the UI thread.
catch (COMException ex)
else
{
_logger.LogError(ex, "Failed to open documents.");
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to apply changes.");
throw;
_logger.LogWarning("TryApplyChanges returned false. Changes was not saved.");
}
}

private Document AddDocument(Document document, string fileName, string code, string targetFolder, Entity sourceEntity)
// This will be thrown if the Apply method doesn't run on the UI thread.
catch (COMException ex)
{
_logger.LogDebug("Attempting to add document {Filename}.", fileName);
var filePath = Path.Combine(targetFolder, fileName);
string[] folders = targetFolder == Path.GetDirectoryName(sourceEntity.SourceFilePath)
? null
: new[] { targetFolder };

var newDocument = document.Project.AddDocument(fileName, code, folders, filePath);
_logger.LogDebug("document {Filename} Added.", fileName);
return newDocument;
_logger.LogError(ex, "Failed to open documents.");
throw;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to apply changes.");
throw;
}
}

private Document AddDocument(Document document, string fileName, string code, string targetFolder, Entity sourceEntity)
{
_logger.LogDebug("Attempting to add document {Filename}.", fileName);
var filePath = Path.Combine(targetFolder, fileName);
string[] folders = targetFolder == Path.GetDirectoryName(sourceEntity.SourceFilePath)
? null
: new[] { targetFolder };

var newDocument = document.Project.AddDocument(fileName, code, folders, filePath);
_logger.LogDebug("document {Filename} Added.", fileName);
return newDocument;
}

}
Loading