Backend/RiderPlugin/ForTea.RiderPlugin/TemplateProcessing/Managing/Impl/T4TargetFileManager.cs (213 lines of code) (raw):
using System.IO;
using System.Linq;
using GammaJul.ForTea.Core.TemplateProcessing;
using GammaJul.ForTea.Core.TemplateProcessing.Services;
using GammaJul.ForTea.Core.Tree;
using JetBrains.Annotations;
using JetBrains.Application.Parts;
using JetBrains.Application.Threading;
using JetBrains.Diagnostics;
using JetBrains.DocumentManagers.Transactions;
using JetBrains.ForTea.RiderPlugin.Psi.Resolve.Macros;
using JetBrains.ForTea.RiderPlugin.TemplateProcessing.Services;
using JetBrains.ProjectModel;
using JetBrains.RdBackend.Common.Features.ProjectModel;
using JetBrains.ReSharper.Psi;
using JetBrains.Util;
namespace JetBrains.ForTea.RiderPlugin.TemplateProcessing.Managing.Impl
{
[SolutionComponent(Instantiation.DemandAnyThreadSafe)]
public sealed class T4TargetFileManager : IT4TargetFileManager
{
[NotNull] private ISolution Solution { get; }
[NotNull] private IShellLocks Locks { get; }
[NotNull] private IT4ProjectModelTemplateMetadataManager TemplateMetadataManager { get; }
[NotNull] private IT4OutputFileRefresher OutputFileRefresher { get; }
public T4TargetFileManager(
[NotNull] ISolution solution,
[NotNull] IShellLocks locks,
[NotNull] IT4ProjectModelTemplateMetadataManager templateMetadataManager,
[NotNull] IT4OutputFileRefresher outputFileRefresher
)
{
Solution = solution;
Locks = locks;
TemplateMetadataManager = templateMetadataManager;
OutputFileRefresher = outputFileRefresher;
}
public VirtualFileSystemPath GetTemporaryExecutableLocation(IT4File file)
{
var projectFile = file.PhysicalPsiSourceFile.ToProjectFile();
var project = projectFile?.GetProject();
if (project == null) return VirtualFileSystemPath.GetEmptyPathFor(InteractionContext.SolutionContext);
var relativePath = projectFile.Location.MakeRelativeTo(project.Location.Parent);
var ttLocation = VirtualFileSystemDefinition.GetTempPath(InteractionContext.SolutionContext)
.Combine("TextTemplating")
.Combine(relativePath);
return ttLocation.Parent.Combine(ttLocation.Name.WithoutExtension()).Combine("GeneratedTransformation.exe");
}
public VirtualFileSystemPath GetExpectedTemporaryTargetFileLocation(IT4File file) =>
GetTemporaryExecutableLocation(file).Parent.Combine(GetExpectedTargetFileName(file));
[NotNull]
private string GetExpectedTargetFileName([NotNull] IT4File file)
{
Locks.AssertReadAccessAllowed();
var sourceFile = file.PhysicalPsiSourceFile.NotNull();
string name = sourceFile.Name;
string targetExtension = file.GetTargetExtension() ?? T4CSharpCodeGenerationUtils.DefaultTargetExtension;
return name.WithOtherExtension(targetExtension);
}
[NotNull]
private VirtualFileSystemPath GetTemporaryTargetFileFolder([NotNull] IT4File file) =>
GetTemporaryExecutableLocation(file).Parent;
[CanBeNull]
private VirtualFileSystemPath TryFindTemporaryTargetFile([NotNull] IT4File file)
{
string name = file.PhysicalPsiSourceFile.NotNull().Name.WithOtherExtension(".*");
var candidates = GetTemporaryTargetFileFolder(file).GetChildFiles(name);
return candidates.FirstOrDefault();
}
[NotNull]
private string GetPreprocessingTargetFileName([NotNull] IT4File file)
{
Locks.AssertReadAccessAllowed();
var sourceFile = file.PhysicalPsiSourceFile.NotNull();
string name = sourceFile.Name;
return name.WithOtherExtension("cs");
}
[NotNull]
private IProjectFile GetOrCreateSameDestinationFile(
[NotNull] IProjectModelTransactionCookie cookie,
[NotNull] IT4File file,
[NotNull] VirtualFileSystemPath temporary
) => GetOrCreateSameDestinationFile(cookie, file, temporary.Name);
[NotNull]
private IProjectFile GetOrCreateSameDestinationFile(
[NotNull] IProjectModelTransactionCookie cookie,
[NotNull] IT4File file,
[NotNull] string destinationName
)
{
Locks.AssertWriteAccessAllowed();
var existingFile = GetSameDestinationFile(file, destinationName);
if (existingFile != null) return existingFile;
return CreateSameDestinationFile(cookie, file, destinationName);
}
[NotNull]
private IProjectFile CreateSameDestinationFile(
[NotNull] IProjectModelTransactionCookie cookie,
[NotNull] IT4File file,
[NotNull] string destinationName
)
{
Locks.AssertWriteAccessAllowed();
var projectFile = file.PhysicalPsiSourceFile.ToProjectFile().NotNull();
var folder = projectFile.ParentFolder.NotNull();
var targetLocation = folder.Location.Combine(destinationName);
var parameters = T4MSBuildProjectUtil.CreateTemplateMetadata(projectFile);
return cookie.AddFile(folder, targetLocation, parameters);
}
[CanBeNull]
private IProjectFile GetSameDestinationFile([NotNull] IT4File file, [NotNull] string temporaryName)
{
Locks.AssertWriteAccessAllowed();
var sourceFile = file.PhysicalPsiSourceFile.NotNull();
var candidates = sourceFile
.ToProjectFile()
?.ParentFolder
?.GetSubItems(temporaryName)
.ToList()
.OfType<IProjectFile>()
.AsList();
Assertion.AssertNotNull(candidates, "candidates != null");
Assertion.Assert(candidates.Count <= 1, "candidates.Value.Length <= 1");
return candidates.SingleOrDefault();
}
[NotNull]
private VirtualFileSystemPath GetDestinationLocation([NotNull] IT4File file, [NotNull] string temporaryName)
{
Locks.AssertReadAccessAllowed();
var sourceFile = file.PhysicalPsiSourceFile.NotNull();
return sourceFile.ToProjectFile().NotNull().Location.Parent.Combine(temporaryName);
}
public void TryProcessExecutionResults(IT4File file)
{
Locks.AssertReadAccessAllowed();
Locks.AssertWriteAccessAllowed();
var temporary = TryFindTemporaryTargetFile(file);
if (temporary == null) return;
var destinationLocation = GetDestinationLocation(file, temporary.Name);
destinationLocation.DeleteFile();
File.Move(temporary.FullPath, destinationLocation.FullPath);
var sourceFile = file.PhysicalPsiSourceFile.NotNull();
var projectFile = sourceFile.ToProjectFile().NotNull();
IProjectFile destination = null;
Solution.InvokeUnderTransaction(cookie =>
{
destination = UpdateProjectModel(file, destinationLocation, cookie);
RemoveLastGenOutputIfDifferent(file, cookie, destinationLocation);
TemplateMetadataManager.UpdateTemplateMetadata(
cookie,
projectFile,
T4TemplateKind.Executable,
destinationLocation
);
TemplateMetadataManager.UpdateGeneratedFileMetadata(cookie, destination, projectFile);
});
OutputFileRefresher.Refresh(destination);
}
private void RemoveLastGenOutputIfDifferent(
[NotNull] IT4File file,
[NotNull] IProjectModelTransactionCookie cookie,
[NotNull] VirtualFileSystemPath destinationLocation
)
{
var projectFile = file.PhysicalPsiSourceFile?.ToProjectFile();
if (projectFile == null) return;
foreach (var suspect in TemplateMetadataManager
.FindLastGenOutput(projectFile)
.Where(it => it.Location != destinationLocation)
)
{
cookie.Remove(suspect);
}
}
[NotNull]
private IProjectFile UpdateProjectModel(
[NotNull] IT4File file,
[NotNull] VirtualFileSystemPath result,
[NotNull] IProjectModelTransactionCookie cookie
)
{
Locks.AssertReadAccessAllowed();
Locks.AssertWriteAccessAllowed();
var destination = GetOrCreateSameDestinationFile(cookie, file, result);
return destination;
}
public void SavePreprocessResults(IT4File file, string text)
{
Locks.AssertReadAccessAllowed();
var sourceFile = file.PhysicalPsiSourceFile.NotNull();
var projectFile = sourceFile.ToProjectFile().NotNull();
Locks.AssertWriteAccessAllowed();
VirtualFileSystemPath destinationLocation = null;
IProjectFile destination = null;
Solution.InvokeUnderTransaction(cookie =>
{
string destinationName = GetPreprocessingTargetFileName(file);
destination = GetOrCreateSameDestinationFile(cookie, file, destinationName);
destinationLocation = destination.Location;
RemoveLastGenOutputIfDifferent(file, cookie, destinationLocation);
TemplateMetadataManager.UpdateTemplateMetadata(
cookie,
projectFile,
T4TemplateKind.Preprocessed,
destinationLocation
);
TemplateMetadataManager.UpdateGeneratedFileMetadata(cookie, destination, projectFile);
});
destinationLocation.WriteAllText(text);
OutputFileRefresher.Refresh(destination);
}
}
}