Backend/ReSharperPlugin/ForTea.ReSharperPlugin/Psi/Resolve/Assemblies/Impl/T4LightWeightAssemblyResolutionCache.cs (112 lines of code) (raw):
using System;
using System.Collections.Generic;
using System.Linq;
using GammaJul.ForTea.Core.Psi.Cache;
using GammaJul.ForTea.Core.Psi.Resolve.Macros.Impl;
using GammaJul.ForTea.Core.Tree;
using JetBrains.Annotations;
using JetBrains.Application.Components;
using JetBrains.Application.Parts;
using JetBrains.Application.Threading;
using JetBrains.DataFlow;
using JetBrains.Diagnostics;
using JetBrains.Lifetimes;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.Caches;
using JetBrains.Util;
using JetBrains.VsIntegration.Interop;
using JetBrains.VsIntegration.Util;
using Microsoft.VisualStudio.TextTemplating.VSHost;
namespace JetBrains.ForTea.ReSharperPlugin.Psi.Resolve.Assemblies.Impl
{
/// <summary>
/// In R#, it is only possible to perform T4-specific assembly resolution on the main thread,
/// so we have to cache them to be able to access them from the daemon
/// </summary>
[PsiComponent(Instantiation.DemandAnyThreadSafe)]
public sealed class T4LightWeightAssemblyResolutionCache :
T4PsiAwareCacheBase<T4LightWeightAssemblyResolutionRequest, T4LightWeightAssemblyResolutionData>
{
[NotNull] private readonly Lazy<Optional<ITextTemplatingComponents>> Components;
public T4LightWeightAssemblyResolutionCache(
Lifetime lifetime,
[NotNull] IShellLocks locks,
[NotNull] IPersistentIndexManager persistentIndexManager,
[NotNull] RawVsServiceProvider provider
) : base(lifetime, locks, persistentIndexManager, T4LightWeightAssemblyResolutionDataMarshaller.Instance)
{
Components = Lazy.Of(() => new Optional<ITextTemplatingComponents>(
provider.Value.GetService<STextTemplating, ITextTemplatingComponents>()
), true);
}
[NotNull]
protected override T4LightWeightAssemblyResolutionRequest Build(IT4File file)
{
var assembliesToResolve = file
.GetThisAndChildrenOfType<IT4AssemblyDirective>()
.Select(directive => directive.ResolvedPath)
.Distinct();
return new T4LightWeightAssemblyResolutionRequest(assembliesToResolve);
}
public override void Merge(IPsiSourceFile sourceFile, object builtPart)
{
var request = (T4LightWeightAssemblyResolutionRequest)builtPart;
var projectFile = sourceFile.ToProjectFile().NotNull();
using (Prepare(projectFile))
{
var response = new Dictionary<string, VirtualFileSystemPath>();
foreach (var path in request.NotNull().AssembliesToResolve)
{
var resolved = TryResolve(path);
if (resolved == null) continue;
response[path.ResolvedPath] = resolved;
}
var data = new T4LightWeightAssemblyResolutionData(response);
base.Merge(sourceFile, data);
}
}
[CanBeNull]
public T4LightWeightAssemblyResolutionData TryGetValue(IPsiSourceFile sourceFile)
{
base.TryGetValue(sourceFile, out var value);
return value;
}
[CanBeNull]
public VirtualFileSystemPath ResolveWithoutCaching([NotNull] T4ResolvedPath path)
{
using (Prepare(path.ProjectFile))
{
return TryResolve(path);
}
}
[CanBeNull]
private VirtualFileSystemPath TryResolve([NotNull] T4ResolvedPath resolvedPath)
{
var asAbsolute = resolvedPath.TryResolveAbsolutePath();
if (asAbsolute != null) return asAbsolute;
var resolved = Components.Value.CanBeNull?.Host?.ResolveAssemblyReference(resolvedPath.ResolvedPath);
if (resolved == null) return null;
var path = VirtualFileSystemPath.Parse(resolved, InteractionContext.SolutionContext);
if (path.IsAbsolute) return path;
return null;
}
[NotNull]
private IDisposable Prepare([NotNull] IProjectFile file)
{
var hierarchy = T4ResolutionUtils.TryGetVsHierarchy(file);
var components = Components.Value.CanBeNull;
if (components == null) return Disposable.Empty;
var oldHierarchy = components.Hierarchy;
var oldInputFileName = components.InputFile;
return Disposable.CreateBracket(
() =>
{
components.Hierarchy = hierarchy;
components.InputFile = file.Location.IsNullOrEmpty() ? null : file.Location.FullPath;
},
() =>
{
components.Hierarchy = oldHierarchy;
components.InputFile = oldInputFileName;
},
trapExceptions: false);
}
}
}