// We add scenes, assets and prefabs to the Misc Files project in Rider. This is so that: // * The Find Usages results list expects project files and throws if any occurrence is a project file // * Only project files are included in SWEA, and we want that for the usage count Code Vision metric // Unfortunately, ReSharper keeps the Misc Files project in sync with Visual Studio's idea of the Misc Files // project (i.e. files open in the editor that aren't part of a project). This means ReSharper will remove our // files from Misc Files and we end up with invalid PSI source files and loads of exceptions. // Fortunately, ReSharper doesn't require project files for find usages or rename, and doesn't have Code Vision, // so we don't need to worry about a usage count (usage suppression is already handled in the suppressor). So // for ReSharper, we just treat all of our files as PSI source files private void AddExternalFiles(ExternalFiles externalFiles) { var builder = new PsiModuleChangeBuilder(); AddExternalPsiSourceFiles(externalFiles.MetaFiles, builder); #if RESHARPER AddExternalPsiSourceFiles(externalFiles.AssetFiles, builder); #endif FlushChanges(builder); #if RIDER AddExternalProjectFiles(externalFiles.AssetFiles); #endif // We should only start watching for file system changes after adding the files we know about foreach (var directory in externalFiles.Directories) { myFileSystemTracker.AdviseDirectoryChanges(myLifetime, directory, true, OnProjectDirectoryChange); } }
private void FlushChanges(PsiModuleChangeBuilder builder) { if (builder.IsEmpty) { return; } myLocks.ExecuteOrQueueEx(myLifetime, GetType().Name + ".FlushChanges", () => { var module = myModuleFactory.PsiModule; Assertion.AssertNotNull(module, "module != null"); myLocks.AssertMainThread(); using (myLocks.UsingWriteLock()) { foreach (var fileChange in builder.Result.FileChanges) { var location = fileChange.Item.GetLocation(); if (location.IsEmpty) { continue; } switch (fileChange.Type) { case PsiModuleChange.ChangeType.Added: module.Add(location, fileChange.Item, null); break; case PsiModuleChange.ChangeType.Removed: module.Remove(location); break; } } myChangeManager.OnProviderChanged(this, builder.Result, SimpleTaskExecutor.Instance); } }); }
private static void ModifyFile([NotNull] PsiModuleChangeBuilder changeBuilder, ModuleWrapper moduleWrapper) { changeBuilder.AddFileChange(moduleWrapper.Module.SourceFile, PsiModuleChange.ChangeType.MODIFIED); }
/// <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) 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.LanguageType.Is<T4ProjectFileType>() && !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.LanguageType.Is<T4ProjectFileType>() && !projectFile.IsPreprocessedT4Template()) { AddFile(projectFile, changeBuilder); changeType = PsiModuleChange.ChangeType.Removed; return false; } break; } return false; }
/// <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)) ) ); }
public override void OnProjectFileChanged(IProjectFile projectFile, FileSystemPath oldLocation, PsiModuleChange.ChangeType changeType, PsiModuleChangeBuilder changeBuilder) { if (!_t4PsiModuleProvider.OnProjectFileChanged(projectFile, ref changeType, changeBuilder)) { base.OnProjectFileChanged(projectFile, oldLocation, changeType, changeBuilder); } }
private void ProcessFileSystemChangeDelta(FileSystemChangeDelta delta, PsiModuleChangeBuilder builder, List <FileSystemPath> projectFilesToAdd) { var module = myModuleFactory.PsiModule; if (module == null) { return; } IPsiSourceFile sourceFile; switch (delta.ChangeType) { case FileSystemChangeType.ADDED: if (delta.NewPath.IsInterestingAsset()) { if (!IsKnownBinaryAsset(delta.NewPath) && !IsAssetExcludedByName(delta.NewPath)) { projectFilesToAdd.Add(delta.NewPath); } } else if (delta.NewPath.IsInterestingMeta()) { AddMetaPsiSourceFile(builder, delta.NewPath); } break; case FileSystemChangeType.DELETED: sourceFile = GetYamlPsiSourceFile(module, delta.OldPath); if (sourceFile != null) { builder.AddFileChange(sourceFile, PsiModuleChange.ChangeType.Removed); } break; case FileSystemChangeType.CHANGED: sourceFile = GetYamlPsiSourceFile(module, delta.NewPath); if (sourceFile != null) { // Make sure we update the cached file system data, or all of our files will have stale // timestamps and never get updated by ICache implementations! if (sourceFile is PsiSourceFileFromPath psiSourceFileFromPath) { psiSourceFileFromPath.GetCachedFileSystemData().Refresh(delta.NewPath); } // Has the file flipped from binary back to text? if (myBinaryUnityFileCache.IsBinaryFile(sourceFile) && sourceFile.GetLocation().SniffYamlHeader()) { myBinaryUnityFileCache.Invalidate(sourceFile); } builder.AddFileChange(sourceFile, PsiModuleChange.ChangeType.Modified); } break; case FileSystemChangeType.SUBTREE_CHANGED: case FileSystemChangeType.RENAMED: case FileSystemChangeType.UNKNOWN: break; } foreach (var child in delta.GetChildren()) { ProcessFileSystemChangeDelta(child, builder, projectFilesToAdd); } }
/// <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)) ) ); }
public void OnProjectFileChanged(IProjectFile projectFile, PsiModuleChange.ChangeType changeType, PsiModuleChangeBuilder changeBuilder, FileSystemPath oldLocation) { _t4PsiModuleProvider.OnProjectFileChanged(projectFile, ref changeType, changeBuilder); }
/// <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, PsiModuleChange.ChangeType.MODIFIED); if (hasFileChanges) GetPsiServices().MarkAsDirty(_sourceFile); _shellLocks.ExecuteOrQueue("T4PsiModuleChange", () => _changeManager.ExecuteAfterChange( () => _shellLocks.ExecuteWithWriteLock( () => _changeManager.OnProviderChanged(this, changeBuilder.Result, SimpleTaskExecutor.Instance)) ) ); }
public void OnProjectFileChanged(IProjectFile projectFile, PsiModuleChange.ChangeType changeType, PsiModuleChangeBuilder changeBuilder) { _t4PsiModuleProvider.OnProjectFileChanged(projectFile, ref changeType, changeBuilder); }
/// <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)) ) ); }
public void OnProjectFileChanged( [NotNull] IProjectFile projectFile, PsiModuleChange.ChangeType changeType, [NotNull] PsiModuleChangeBuilder changeBuilder, [NotNull] FileSystemPath oldLocation ) => _t4PsiModuleProvider.OnProjectFileChanged(projectFile, changeType, changeBuilder);
/// <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 override void OnProjectFileChanged(IProjectFile projectFile, FileSystemPath oldLocation, PsiModuleChange.ChangeType changeType, PsiModuleChangeBuilder changeBuilder) { if (!_t4PsiModuleProvider.OnProjectFileChanged(projectFile, ref changeType, changeBuilder)) base.OnProjectFileChanged(projectFile, oldLocation, changeType, changeBuilder); }