<commit_before>using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Application.Threading;
using JetBrains.Collections;
using JetBrains.Diagnostics;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.Caches;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.AssetHierarchy;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.AssetHierarchy.Elements;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.AssetHierarchy.References;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.Utils;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.Resolve;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.Search;
using JetBrains.ReSharper.Plugins.Yaml.Psi;
using JetBrains.ReSharper.Plugins.Yaml.Psi.Tree;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Resolve;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Resolve.Filters;
using JetBrains.ReSharper.Psi.Modules;
using JetBrains.ReSharper.Psi.Resolve;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.Util;
using JetBrains.Util.Collections;

namespace JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.UnityEvents
{
    // UnityEventsElementContainer stores whole index in memory, it is not expected to have millions of methods. In case of high memory usage
    // with millions of methods, only pointers to AssetMethodData should be stored. AssetMethodData will be deserialized only in find usages,
    // strings should be replaced by int hashes.
    // Information about imported/prefab modifications could be stored in memory, it should not allocate a lot of memory ever.
    [SolutionComponent]
    public partial class UnityEventsElementContainer : IUnityAssetDataElementContainer
    {
        private readonly ISolution mySolution;
        private readonly IShellLocks myShellLocks;
        private readonly MetaFileGuidCache myGuidCache;
        private readonly AssetDocumentHierarchyElementContainer myAssetDocumentHierarchyElementContainer;
        private readonly ILogger myLogger;

        public UnityEventsElementContainer(ISolution solution, IShellLocks shellLocks, MetaFileGuidCache guidCache,
            AssetDocumentHierarchyElementContainer elementContainer, ILogger logger)
        {
            mySolution = solution;
            myShellLocks = shellLocks;
            myGuidCache = guidCache;
            myAssetDocumentHierarchyElementContainer = elementContainer;
            myLogger = logger;
        }

        private static readonly StringSearcher ourMethodNameSearcher = new StringSearcher("m_MethodName", false);

        #region FIND_USAGES_METHODS

        private readonly OneToSetMap<IPsiSourceFile, UnityEventData> myPsiSourceFileToEventData = new OneToSetMap<IPsiSourceFile, UnityEventData>();
        private readonly OneToCompactCountingSet<string, IPsiSourceFile> myMethodNameToFilesWithUsages = new OneToCompactCountingSet<string, IPsiSourceFile>();

        // for counter, could be remove in case of memory optimization and replaced by low-priority background task, do not forget to
        // add files to `myFilesWithUsages` instead of `myFilesWithPossibleUsages`.
        private readonly OneToCompactCountingSet<string, IPsiSourceFile> myMethodNameToFilesWithPossibleUsages = new OneToCompactCountingSet<string, IPsiSourceFile>();
        private readonly OneToCompactCountingSet<string, AssetMethodUsages> myLocalMethodUsages = new OneToCompactCountingSet<string, AssetMethodUsages>();

        #endregion

        // Inspector values for unity event support
        #region UNITY_EVENTS_FIND_USAGES

        // for estimated counter check
        private readonly OneToCompactCountingSet<int, Guid> myUnityEventNameHashToScriptGuids = new OneToCompactCountingSet<int, Guid>();
        private readonly CountingSet<string> myUnityEventsWithModifications = new CountingSet<string>();
        private readonly Dictionary<(LocalReference, string), UnityEventData> myUnityEventDatas = new Dictionary<(LocalReference, string), UnityEventData>();

        // for count check & find usages
        private readonly CountingSet<(string eventName, Guid scriptOwner)> myUnityEventUsageCount = new CountingSet<(string eventName, Guid scriptOwner)>();
        private readonly OneToSetMap<string, IPsiSourceFile> myUnityEventNameToSourceFiles = new OneToSetMap<string, IPsiSourceFile>();

        #endregion

        public IUnityAssetDataElement CreateDataElement(IPsiSourceFile sourceFile)
        {
            return new UnityEventsDataElement();
        }

        public bool IsApplicable(IPsiSourceFile currentAssetSourceFile)
        {
            return true;
        }

        public object Build(IPsiSourceFile currentAssetSourceFile, AssetDocument assetDocument)
        {
            var modifications = ProcessPrefabModifications(currentAssetSourceFile, assetDocument);
            var buffer = assetDocument.Buffer;
            if (ourMethodNameSearcher.Find(buffer) < 0)
                return new UnityEventsBuildResult(modifications, new LocalList<UnityEventData>());

            var anchorRaw = AssetUtils.GetAnchorFromBuffer(assetDocument.Buffer);
            if (!anchorRaw.HasValue)
                return new UnityEventsBuildResult(modifications, new LocalList<UnityEventData>());

            var anchor = anchorRaw.Value;

            var properties = assetDocument.Document.GetUnityObjectProperties();
            var entries = properties?.Entries;
            if (entries == null)
                return new UnityEventsBuildResult(modifications, new LocalList<UnityEventData>());

            var scriptReference = properties.GetMapEntryValue<INode>(UnityYamlConstants.ScriptProperty)
                    .ToHierarchyReference(currentAssetSourceFile) as ExternalReference?;
            if (scriptReference == null)
                return new UnityEventsBuildResult(modifications, new LocalList<UnityEventData>());

            var location = new LocalReference(currentAssetSourceFile.PsiStorage.PersistentIndex.NotNull("owningPsiPersistentIndex != null"), anchor);

            var result = new LocalList<UnityEventData>();
            foreach (var entry in entries)
            {
                if (ourMethodNameSearcher.Find(entry.Content.GetTextAsBuffer()) >= 0)
                {
                    var name = entry.Key.GetPlainScalarText();
                    if (name == null)
                        continue;

                    var rootMap = entry.Content.Value as IBlockMappingNode;
                    var persistentCallsMap = rootMap.GetMapEntryValue<IBlockMappingNode>("m_PersistentCalls");
                    var mCalls = persistentCallsMap.GetMapEntryValue<IBlockSequenceNode>("m_Calls");
                    if (mCalls == null)
                        continue;

                    var eventTypeName = rootMap.GetMapEntryPlainScalarText("m_TypeName");
                    var calls = GetCalls(currentAssetSourceFile, assetDocument, mCalls, name, eventTypeName);

                    result.Add(new UnityEventData(name, location, scriptReference.Value, calls.ToArray()));
                }
            }

            return new UnityEventsBuildResult(modifications, result);
        }

        private LocalList<AssetMethodUsages> GetCalls(IPsiSourceFile currentAssetSourceFile,<commit_msg>support arrays in UnityEventsElementContainer<commit_after>using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Application.Threading;
using JetBrains.Collections;
using JetBrains.Diagnostics;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.Caches;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.AssetHierarchy;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.AssetHierarchy.Elements;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.AssetHierarchy.References;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.Utils;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.Resolve;
using JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.Search;
using JetBrains.ReSharper.Plugins.Yaml.Psi;
using JetBrains.ReSharper.Plugins.Yaml.Psi.Tree;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Resolve;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Resolve.Filters;
using JetBrains.ReSharper.Psi.Modules;
using JetBrains.ReSharper.Psi.Resolve;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.Util;
using JetBrains.Util.Collections;

namespace JetBrains.ReSharper.Plugins.Unity.Yaml.Psi.DeferredCaches.UnityEvents
{
    // UnityEventsElementContainer stores whole index in memory, it is not expected to have millions of methods. In case of high memory usage
    // with millions of methods, only pointers to AssetMethodData should be stored. AssetMethodData will be deserialized only in find usages,
    // strings should be replaced by int hashes.
    // Information about imported/prefab modifications could be stored in memory, it should not allocate a lot of memory ever.
    [SolutionComponent]
    public partial class UnityEventsElementContainer : IUnityAssetDataElementContainer
    {
        private readonly ISolution mySolution;
        private readonly IShellLocks myShellLocks;
        private readonly MetaFileGuidCache myGuidCache;
        private readonly AssetDocumentHierarchyElementContainer myAssetDocumentHierarchyElementContainer;
        private readonly ILogger myLogger;

        public UnityEventsElementContainer(ISolution solution, IShellLocks shellLocks, MetaFileGuidCache guidCache,
            AssetDocumentHierarchyElementContainer elementContainer, ILogger logger)
        {
            mySolution = solution;
            myShellLocks = shellLocks;
            myGuidCache = guidCache;
            myAssetDocumentHierarchyElementContainer = elementContainer;
            myLogger = logger;
        }

        private static readonly StringSearcher ourMethodNameSearcher = new StringSearcher("m_MethodName", false);

        #region FIND_USAGES_METHODS

        private readonly OneToSetMap<IPsiSourceFile, UnityEventData> myPsiSourceFileToEventData = new OneToSetMap<IPsiSourceFile, UnityEventData>();
        private readonly OneToCompactCountingSet<string, IPsiSourceFile> myMethodNameToFilesWithUsages = new OneToCompactCountingSet<string, IPsiSourceFile>();

        // for counter, could be remove in case of memory optimization and replaced by low-priority background task, do not forget to
        // add files to `myFilesWithUsages` instead of `myFilesWithPossibleUsages`.
        private readonly OneToCompactCountingSet<string, IPsiSourceFile> myMethodNameToFilesWithPossibleUsages = new OneToCompactCountingSet<string, IPsiSourceFile>();
        private readonly OneToCompactCountingSet<string, AssetMethodUsages> myLocalMethodUsages = new OneToCompactCountingSet<string, AssetMethodUsages>();

        #endregion

        // Inspector values for unity event support
        #region UNITY_EVENTS_FIND_USAGES

        // for estimated counter check
        private readonly OneToCompactCountingSet<int, Guid> myUnityEventNameHashToScriptGuids = new OneToCompactCountingSet<int, Guid>();
        private readonly CountingSet<string> myUnityEventsWithModifications = new CountingSet<string>();
        private readonly Dictionary<(LocalReference, string), UnityEventData> myUnityEventDatas = new Dictionary<(LocalReference, string), UnityEventData>();

        // for count check & find usages
        private readonly CountingSet<(string eventName, Guid scriptOwner)> myUnityEventUsageCount = new CountingSet<(string eventName, Guid scriptOwner)>();
        private readonly OneToSetMap<string, IPsiSourceFile> myUnityEventNameToSourceFiles = new OneToSetMap<string, IPsiSourceFile>();

        #endregion

        public IUnityAssetDataElement CreateDataElement(IPsiSourceFile sourceFile)
        {
            return new UnityEventsDataElement();
        }

        public bool IsApplicable(IPsiSourceFile currentAssetSourceFile)
        {
            return true;
        }

        public object Build(IPsiSourceFile currentAssetSourceFile, AssetDocument assetDocument)
        {
            var modifications = ProcessPrefabModifications(currentAssetSourceFile, assetDocument);
            var buffer = assetDocument.Buffer;
            if (ourMethodNameSearcher.Find(buffer) < 0)
                return new UnityEventsBuildResult(modifications, new LocalList<UnityEventData>());

            var anchorRaw = AssetUtils.GetAnchorFromBuffer(assetDocument.Buffer);
            if (!anchorRaw.HasValue)
                return new UnityEventsBuildResult(modifications, new LocalList<UnityEventData>());

            var anchor = anchorRaw.Value;

            var properties = assetDocument.Document.GetUnityObjectProperties();
            var entries = properties?.Entries;
            if (entries == null)
                return new UnityEventsBuildResult(modifications, new LocalList<UnityEventData>());

            var scriptReference = properties.GetMapEntryValue<INode>(UnityYamlConstants.ScriptProperty)
                    .ToHierarchyReference(currentAssetSourceFile) as ExternalReference?;
            if (scriptReference == null)
                return new UnityEventsBuildResult(modifications, new LocalList<UnityEventData>());

            var location = new LocalReference(currentAssetSourceFile.PsiStorage.PersistentIndex.NotNull("owningPsiPersistentIndex != null"), anchor);

            var result = new LocalList<UnityEventData>();
            foreach (var entry in entries)
            {
                if (ourMethodNameSearcher.Find(entry.Content.GetTextAsBuffer()) >= 0)
                {
                    var name = entry.Key.GetPlainScalarText();
                    if (name == null)
                        continue;
                    
                    BuildRootMappingNode(currentAssetSourceFile, assetDocument, entry.Content.Value, name, ref result, location, scriptReference);
                }
            }

            return new UnityEventsBuildResult(modifications, result);
        }

        private void BuildRootMappingNode(IPsiSourceFile currentAssetSourceFile, AssetDocument assetDocument,
            INode node, string name, ref LocalList<UnityEventData> result, LocalReference location,
            ExternalReference? scriptReference)
        {
            if (node is IBlockSequenceNode blockSequenceNode)
            {
                foreach (var entry in blockSequenceNode.Entries)
                {
                    BuildRootMappingNode(currentAssetSourceFile, assetDocument, entry.Value, name, ref result, location,
                        scriptReference);
                }
            }
            
            var rootMap = node as IBlockMappingNode;
            var persistentCallsMap = rootMap.GetMapEntryValue<IBlockMappingNode>("m_PersistentCalls");
            var mCalls = persistentCallsMap.GetMapEntryValue<IBlockSequenceNode>("m_Calls");
            if (mCalls == null)
                return;

            var eventTypeName = rootMap.GetMapEntryPlainScalarText("m_TypeName");
            var calls = GetCalls(currentAssetSourceFile, assetDocument, mCalls, name, eventTypeName);

            result.Add(new UnityEventData(name, location, scriptReference.Value, calls.ToArray()));
        }

        private LocalList<AssetMethodUsages> GetCalls(IPsiSourceFile currentAssetSourceFile,
{'fraction_non_alphanumeric': 0.06871854432830042, 'fraction_numerical': 0.0002580978190734288, 'mean_word_length': 3.4018744674808294, 'pattern_counts': {'":': 0, '<': 60, '<?xml version=': 0, '>': 60, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5837176', 'n_tokens_mistral': 4114, 'n_tokens_neox': 3939, 'n_words': 909}