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