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);
        }
Beispiel #2
0
        /// <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);
        }
Beispiel #3
0
        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);
        }
Beispiel #6
0
        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);
        }
Beispiel #7
0
        /// <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);
        }
Beispiel #8
0
        /// <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))
                                           )
                                       );
        }
Beispiel #9
0
        /// <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))
                                           )
                                       );
        }