public void Execute(IDataContext context, DelegateExecute nextExecute)
        {
            var solution = context.GetData(ProjectModelDataConstants.SOLUTION);

            if (solution == null)
            {
                return;
            }

            var wordsPerAssembly = new OneToSetMap <string, string>(valueComparer: StringComparer.InvariantCultureIgnoreCase);
            var abbreviations    = new JetHashSet <string>();

            // TODO: This should be in a background task
            var psiModules  = solution.GetComponent <IPsiModules>();
            var symbolCache = solution.GetComponent <ISymbolCache>();

            foreach (var assemblyPsiModule in psiModules.GetAssemblyModules().OrderBy(m => m.DisplayName))
            {
                if (!ShouldProcessAssembly(assemblyPsiModule))
                {
                    continue;
                }

                var symbolScope  = symbolCache.GetSymbolScope(assemblyPsiModule, false, true);
                var typeElements = symbolScope.GetAllTypeElementsGroupedByName();
                foreach (var typeElement in typeElements)
                {
                    if (!typeElement.CanBeVisibleToSolution())
                    {
                        continue;
                    }

                    AddWords(wordsPerAssembly, abbreviations, assemblyPsiModule, typeElement.ShortName);

                    foreach (var namespaceName in typeElement.GetClrName().NamespaceNames)
                    {
                        AddWords(wordsPerAssembly, abbreviations, assemblyPsiModule, namespaceName);
                    }

                    // TODO: Should we skip enum values?
                    // It's unlikely that a user would name a method or variable after a value such as AdvertisingNetwork.Aarki,
                    // but on the other hand, if it's used in a comment, it'll show up as a typo.

                    foreach (var typeMember in typeElement.GetMembers())
                    {
                        if (!ShouldProcessTypeMember(typeMember))
                        {
                            continue;
                        }

                        if (typeElement is IEnum)
                        {
                            // Don't use any enum values as abbreviations. Avoids false positives with ALL_UPPER_CASE style
                            AddWords(wordsPerAssembly, new JetHashSet <string>(), assemblyPsiModule,
                                     typeMember.ShortName);
                        }
                        else
                        {
                            AddWords(wordsPerAssembly, abbreviations, assemblyPsiModule, typeMember.ShortName);
                        }

                        // TODO: Include parameter names?
                        // Respeller will not check parameter names of overriding or implementing functions, so this is
                        // probably unnecessary
                    }
                }
            }

            // Case insensitive. There will not be duplicates
            var allWords = (from pair in wordsPerAssembly
                            from word in pair.Value
                            select word).ToJetHashSet(StringComparer.InvariantCultureIgnoreCase);

            var spellService = solution.GetComponent <ISpellService>();

            var unknownWords = (from word in allWords
                                where !spellService.CheckWordSpelling(word)
                                select word).ToJetHashSet(StringComparer.InvariantCultureIgnoreCase);

            // Add abbreviations separately. If added to the dictionary, we don't get typo warnings, but we can get
            // naming standard inspection warnings. E.g. BlahBlahAABB is converted to BlahBlahAabb. Don't add anything
            // that's already known, but the spell checker will only check for words of 4 characters or more.
            // TODO: Ideally, we should disable AbbreviationsSettingsProvider. But just merge results in for now
            var unknownAbbreviations = (from word in abbreviations
                                        where (word.Length > 1 && word.Length < 4) || !spellService.CheckWordSpelling(word)
                                        select word).ToJetHashSet();

            // Remove the non-abbreviations
            unknownAbbreviations.Remove("TEXTMESHPRO");
            unknownAbbreviations.Remove("GRAIDENT");

            // Remove known typos or exclusions. Yes, this is all done by hand
            RemoveTyposAndExclusions(unknownWords);

            // Dump all words for diagnostics
            Dumper.DumpToNotepad(w =>
            {
                w.WriteLine("All words");
                w.WriteLine();

                foreach (var(assembly, words) in wordsPerAssembly)
                {
                    w.WriteLine("Words for assembly: " + assembly);
                    w.WriteLine();

                    foreach (var word in words.OrderBy(IdentityFunc <string> .Instance))
                    {
                        w.WriteLine(word.ToLowerInvariant());
                    }
                    w.WriteLine();
                }
            });

            // Dump all abbreviations, so we can avoid naming standards inspections when an abbreviation is used.
            // E.g. BlahBlahAABB is accepted instead of converted to BlahBlahAabb
            Dumper.DumpToNotepad(w =>
            {
                w.WriteLine("Abbreviations");
                w.WriteLine();

                foreach (var abbreviation in unknownAbbreviations.OrderBy(IdentityFunc <string> .Instance))
                {
                    w.WriteLine(abbreviation.ToUpperInvariant());
                }
            });

            // Dump all unknown words, minus abbreviations, for use as a dictionary
            Dumper.DumpToNotepad(w =>
            {
                var dictionary = new JetHashSet <string>(unknownWords, StringComparer.InvariantCultureIgnoreCase);
                dictionary.ExceptWith(abbreviations);

                w.WriteLine("Dictionary (unknown words minus abbreviations)");
                w.WriteLine();
                w.WriteLine(dictionary.Count);    // Hunspell dictionaries start with the number of words

                foreach (var word in dictionary.OrderBy(IdentityFunc <string> .Instance))
                {
                    w.WriteLine(word.ToLowerInvariant());
                }
            });
        }