public PrefabImportCache(Lifetime lifetime, ISolution solution, ISettingsStore store, MetaFileGuidCache metaFileGuidCache, UnityExternalFilesModuleFactory unityExternalFilesModuleFactory, IShellLocks shellLocks) { myMetaFileGuidCache = metaFileGuidCache; myShellLocks = shellLocks; metaFileGuidCache.GuidChanged.Advise(lifetime, e => { myShellLocks.AssertWriteAccessAllowed(); var set = new HashSet <Guid>(); if (e.oldGuid != null) { InvalidateImportCache(e.oldGuid.Value, set); } if (e.newGuid != null) { InvalidateImportCache(e.newGuid.Value, set); } }); myUnityExternalFilesPsiModule = unityExternalFilesModuleFactory.PsiModule; var boundSettingsStoreLive = store.BindToContextLive(lifetime, ContextRange.Smart(solution.ToDataContext())); myCacheEnabled = boundSettingsStoreLive.GetValueProperty(lifetime, (UnitySettings key) => key.IsPrefabCacheEnabled); }
/// <summary> /// Processes changes for specific project file and returns a list of corresponding source file changes. /// </summary> /// <param name="projectFile">The project file.</param> /// <param name="changeType">Type of the change.</param> /// <param name="changeBuilder">The change builder used to populate changes.</param> /// <returns>Whether the provider has handled the file change.</returns> internal bool OnProjectFileChanged(IProjectFile projectFile, ref PsiModuleChange.ChangeType changeType, PsiModuleChangeBuilder changeBuilder) { if (!_t4Environment.IsSupported || !projectFile.LanguageType.Is <T4ProjectFileType>()) { return(false); } _shellLocks.AssertWriteAccessAllowed(); ModuleWrapper moduleWrapper; switch (changeType) { case PsiModuleChange.ChangeType.ADDED: // Preprocessed .tt files should be handled by R# itself as if it's a normal project file, // so that it has access to the current project types. if (!projectFile.IsPreprocessedT4Template()) { AddFile(projectFile, changeBuilder); return(true); } break; case PsiModuleChange.ChangeType.REMOVED: if (_modules.TryGetValue(projectFile, out moduleWrapper)) { RemoveFile(projectFile, changeBuilder, moduleWrapper); return(true); } break; case PsiModuleChange.ChangeType.MODIFIED: if (_modules.TryGetValue(projectFile, out moduleWrapper)) { if (!projectFile.IsPreprocessedT4Template()) { ModifyFile(changeBuilder, moduleWrapper); return(true); } // The T4 file went from Transformed to Preprocessed, it doesn't need a T4PsiModule anymore. RemoveFile(projectFile, changeBuilder, moduleWrapper); changeType = PsiModuleChange.ChangeType.ADDED; return(false); } // The T4 file went from Preprocessed to Transformed, it now needs a T4PsiModule. if (!projectFile.IsPreprocessedT4Template()) { AddFile(projectFile, changeBuilder); changeType = PsiModuleChange.ChangeType.REMOVED; return(false); } break; } return(false); }
public void Merge(IPsiSourceFile sourceFile, object builtPart) { myShellLocks.AssertWriteAccessAllowed(); // remove old annotations from all tries foreach (var node in mySourceFileToNodes[sourceFile]) { node.Clear(sourceFile); } mySourceFileToNodes.RemoveKey(sourceFile); myFilesWithoutAnnotations.Remove(sourceFile); // add built annotations. var part = (AttributedTypesTrieCollection)builtPart; if (!myRoot.Merge(part, mySourceFileToNodes)) { myFilesWithoutAnnotations.Add(sourceFile); } myDirtyFiles.Remove(sourceFile); }
public void InvalidateCacheFor(IPsiSourceFile sourceFile) { myShellLocks.AssertWriteAccessAllowed(); var guid = myMetaFileGuidCache.GetAssetGuid(sourceFile); if (guid == null) // we have already clear content due to advice on GuidChanged in consructor { return; } var visited = new HashSet <Guid>(); foreach (var deps in myDependencies.GetValuesSafe(guid.Value)) { InvalidateImportCache(deps, visited); } InvalidateImportCache(guid.Value, visited); }
public void Remove(IPsiSourceFile sourceFile, AssetDocumentHierarchyElement assetDocumentHierarchyElement) { myShellLocks.AssertWriteAccessAllowed(); var guid = myMetaFileGuidCache.GetAssetGuid(sourceFile); if (guid == null) // we have already clear content due to advice on GuidChanged in consructor { return; } var visited = new HashSet <string>(); foreach (var deps in myDependencies.GetValuesSafe(guid)) { InvalidateImportCache(deps, visited); } InvalidateImportCache(guid, visited); }
public void MergeLoaded(object data) { var node = (Pair <AttributedTypesTrieCollection, JetHashSet <IPsiSourceFile> >)data; if (node.First == null || node.Second == null) { return; } myLocks.AssertWriteAccessAllowed(); //optimization here: replace roots because it constains no data yet. myRoot = node.First; myFilesWithoutAnnotations = node.Second; // add new nodes to file -> node index. UpdateIndexAfterLoad(myRoot.Roots); myPsiFiles.PsiChanged(null, PsiChangedElementType.ContentsChanged); }
/// <summary> /// Processes changes for specific project file and sets up a list of corresponding source file changes. /// </summary> /// <param name="projectFile">The project file.</param> /// <param name="changeType">Type of the change.</param> /// <param name="changeBuilder">The change builder used to populate changes.</param> /// <returns><see cref="PsiModuleChange.ChangeType"/> if further changing is required, null otherwise</returns> public PsiModuleChange.ChangeType?OnProjectFileChanged( [NotNull] IProjectFile projectFile, PsiModuleChange.ChangeType changeType, [NotNull] PsiModuleChangeBuilder changeBuilder ) { if (!_t4Environment.IsSupported) { // The plugin does not operate in // old versions of Visual Studio // and unknown environments. return(changeType); } // It would be logical to check the file type here and return if it's not T4. // However, this is impossible because calculating file type // requires the file to be attached to a project hierarchy, // which sometimes isn't true. _shellLocks.AssertWriteAccessAllowed(); ModuleWrapper moduleWrapper; switch (changeType) { case PsiModuleChange.ChangeType.Added: if (!projectFile.LanguageType.Is <T4ProjectFileType>()) { // We only handle T4 files and do not affect other project files. break; } if (TemplateDataManager.IsPreprocessedTemplate(projectFile)) { // This is a new preprocessed file. // We don't create modules for preprocessed files // so that to let R# add them into the project, // so that the template will have access // to the types defined in the current project. break; } if (projectFile.IsFlaggedAsPreprocessed()) { throw new InvalidOperationException("Did not expect preprocessed flag to appear this early"); } // This is a new executable file, so we need to create a module for it. AddFile(projectFile, changeBuilder); // After the module is created, the request to add the file has been handled, // so there's no need to create any other modules, so pass null as RequestedChange. return(null); case PsiModuleChange.ChangeType.Removed: if (!_modules.TryGetValue(projectFile, out moduleWrapper)) { // The file wasn't handled by us in the first place, // so there's no way we can handle its removal. break; } RemoveFile(projectFile, changeBuilder, moduleWrapper); // Since we handled the module removal, there's nothing else to be done. return(null); case PsiModuleChange.ChangeType.Modified: if (_modules.TryGetValue(projectFile, out moduleWrapper)) { if (TemplateDataManager.IsPreprocessedTemplate(projectFile) || projectFile.IsFlaggedAsPreprocessed()) { // The T4 file has a module but it shouldn't because it's preprocessed. // This can happen when an executable file becomes preprocessed. // We no longer need the module for it. RemoveFile(projectFile, changeBuilder, moduleWrapper); // After the module has been removed, // the file needs to be added to the project it resides in. // Requesting this change does exactly that. return(PsiModuleChange.ChangeType.Added); } // This is the ordinary change in an executable T4 file. // We can handle it. ModifyFile(changeBuilder, moduleWrapper); // Since we've handled the change, no need to delegate it to R#. return(null); } // We don't know about this file. Maybe it is a T4 file we should become interested in? if (!projectFile.LanguageType.Is <T4ProjectFileType>()) { // No it's not. break; } if (TemplateDataManager.IsPreprocessedTemplate(projectFile) || projectFile.IsFlaggedAsPreprocessed()) { // It is still a preprocessed file, we still don't want a module for it. // Let R# continue managing this file. break; } // The T4 is executable but has no module. // This can happen if it used to be preprocessed but became executable. // It now needs a T4PsiModule. AddFile(projectFile, changeBuilder); // After we've created this module, // we need to let R# know that this file is now our business. // That's why we request this file to be removed from the project it resides in. return(PsiModuleChange.ChangeType.Removed); } // If there's nothing we can do about this file, let R# work. return(changeType); }
/// <summary> /// Called when the associated data file changed: added/removed assemblies or includes. /// </summary> /// <param name="dataDiff">The difference between the old and new data.</param> private void OnDataFileChanged([NotNull] T4FileDataDiff dataDiff) { _shellLocks.AssertWriteAccessAllowed(); bool hasFileChanges = ResolveMacros(dataDiff.AddedMacros); bool hasChanges = hasFileChanges; ITextTemplatingComponents components = _t4Environment.Components.CanBeNull; using (components.With(TryGetVsHierarchy(), _projectFile.Location)) { // removes the assembly references from the old assembly directives foreach (string removedAssembly in dataDiff.RemovedAssemblies) { string assembly = removedAssembly; if (components != null) { assembly = components.Host.ResolveAssemblyReference(assembly); } IAssemblyCookie cookie; if (!_assemblyReferences.TryGetValue(assembly, out cookie)) { continue; } _assemblyReferences.Remove(assembly); hasChanges = true; cookie.Dispose(); } // adds assembly references from the new assembly directives foreach (string addedAssembly in dataDiff.AddedAssemblies) { string assembly = addedAssembly; if (components != null) { assembly = components.Host.ResolveAssemblyReference(assembly); } if (assembly == null) { continue; } if (_assemblyReferences.ContainsKey(assembly)) { continue; } IAssemblyCookie cookie = TryAddReference(assembly); if (cookie != null) { hasChanges = true; } } } if (!hasChanges) { return; } // tells the world the module has changed var changeBuilder = new PsiModuleChangeBuilder(); changeBuilder.AddModuleChange(this, PsiModuleChange.ChangeType.Modified); if (hasFileChanges) { GetPsiServices().MarkAsDirty(SourceFile); } _shellLocks.ExecuteOrQueue("T4PsiModuleChange", () => _changeManager.ExecuteAfterChange( () => _shellLocks.ExecuteWithWriteLock( () => _changeManager.OnProviderChanged(this, changeBuilder.Result, SimpleTaskExecutor.Instance)) ) ); }
/// <summary> /// Called when the associated data file changed: added/removed assemblies or includes. /// </summary> /// <param name="dataDiff">The difference between the old and new data.</param> private void OnDataFileChanged([NotNull] T4FileDataDiff dataDiff) { _shellLocks.AssertWriteAccessAllowed(); bool hasFileChanges = ResolveMacros(dataDiff.AddedMacros); bool hasChanges = hasFileChanges; IDictionary <string, string> resolvedMacros = GetResolvedMacros(); // removes the assembly references from the old assembly directives foreach (string removedAssembly in dataDiff.RemovedAssemblies) { string assembly = VsBuildMacroHelper.ResolveMacros(removedAssembly, resolvedMacros); IAssemblyCookie cookie; if (!_assemblyReferences.TryGetValue(assembly, out cookie)) { continue; } _assemblyReferences.Remove(assembly); hasChanges = true; cookie.Dispose(); } // adds assembly references from the new assembly directives foreach (string addedAssembly in dataDiff.AddedAssemblies) { string assembly = VsBuildMacroHelper.ResolveMacros(addedAssembly, resolvedMacros); if (_assemblyReferences.ContainsKey(assembly)) { continue; } IAssemblyCookie cookie = TryAddReference(assembly); if (cookie != null) { hasChanges = true; } } if (!hasChanges) { return; } // tells the world the module has changed var changeBuilder = new PsiModuleChangeBuilder(); changeBuilder.AddModuleChange(this, ModifiedChangeType); if (hasFileChanges) { GetPsiServices().MarkAsDirty(_sourceFile); } _shellLocks.ExecuteOrQueue("T4PsiModuleChange", () => _changeManager.ExecuteAfterChange( () => _shellLocks.ExecuteWithWriteLock( () => _changeManager.OnProviderChanged(this, changeBuilder.Result, SimpleTaskExecutor.Instance)) ) ); }