Exemple #1
0
        // 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);
            }
        }
Exemple #2
0
        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);
                }
            });
        }
Exemple #3
0
 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;
        }
Exemple #5
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))
                )
            );
        }
 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);
     }
 }
Exemple #7
0
        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);
            }
        }
Exemple #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;

            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))
                                           )
                                       );
        }
Exemple #9
0
 public void OnProjectFileChanged(IProjectFile projectFile, PsiModuleChange.ChangeType changeType, PsiModuleChangeBuilder changeBuilder, FileSystemPath oldLocation)
 {
     _t4PsiModuleProvider.OnProjectFileChanged(projectFile, ref changeType, changeBuilder);
 }
Exemple #10
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, 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);
 }
		public void OnProjectFileChanged(IProjectFile projectFile, PsiModuleChange.ChangeType changeType, PsiModuleChangeBuilder changeBuilder, FileSystemPath oldLocation) {
			_t4PsiModuleProvider.OnProjectFileChanged(projectFile, ref changeType, changeBuilder);
		}
Exemple #13
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);
        }
Exemple #14
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))
                                           )
                                       );
        }
 public void OnProjectFileChanged(
     [NotNull] IProjectFile projectFile,
     PsiModuleChange.ChangeType changeType,
     [NotNull] PsiModuleChangeBuilder changeBuilder,
     [NotNull] FileSystemPath oldLocation
     ) => _t4PsiModuleProvider.OnProjectFileChanged(projectFile, changeType, changeBuilder);
Exemple #16
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);
        }
 public void OnProjectFileChanged(IProjectFile projectFile, PsiModuleChange.ChangeType changeType, PsiModuleChangeBuilder changeBuilder)
 {
     _t4PsiModuleProvider.OnProjectFileChanged(projectFile, ref changeType, changeBuilder);
 }
 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);
 }