/// <summary>
        /// Get the dictionary for the specified buffer
        /// </summary>
        /// <param name="buffer">The buffer for which to get a dictionary</param>
        /// <returns>The spelling dictionary for the buffer or null if one is not provided</returns>
        public SpellingDictionary GetDictionary(ITextBuffer buffer)
        {
            SpellingDictionary service = null;

            if (buffer != null && !buffer.Properties.TryGetProperty(typeof(SpellingDictionary), out service))
            {
                // Get the configuration and create the dictionary based on the configuration
                var config = this.GetConfiguration(buffer);

                if (config != null)
                {
                    // Create or get the existing global dictionary for the default language
                    var globalDictionary = GlobalDictionary.CreateGlobalDictionary(config.DefaultLanguage,
                                                                                   globalServiceProvider, config.AdditionalDictionaryFolders, config.RecognizedWords);

                    if (globalDictionary != null)
                    {
                        service = new SpellingDictionary(globalDictionary, config.IgnoredWords);
                        buffer.Properties[typeof(SpellingDictionary)] = service;
                    }
                }
            }

            return(service);
        }
예제 #2
0
        //=====================================================================

        /// <inheritdoc />
        public ISpellingDictionary GetDictionary(ITextBuffer buffer)
        {
            ISpellingDictionary service = null;

            if (buffer.Properties.TryGetProperty(typeof(SpellingDictionaryService), out service))
            {
                return(service);
            }

            List <ISpellingDictionary> bufferSpecificDictionaries = new List <ISpellingDictionary>();

            foreach (var provider in bufferSpecificDictionaryProviders)
            {
                var dictionary = provider.Value.GetDictionary(buffer);

                if (dictionary != null)
                {
                    bufferSpecificDictionaries.Add(dictionary);
                }
            }

            // Create or get the existing global dictionary for the default language
            var globalDictionary = GlobalDictionary.CreateGlobalDictionary(null);

            if (globalDictionary != null)
            {
                service = new SpellingDictionaryService(bufferSpecificDictionaries, globalDictionary);
                buffer.Properties[typeof(SpellingDictionaryService)] = service;
            }

            return(service);
        }
예제 #3
0
        /// <summary>
        /// Get the dictionary for the specified buffer
        /// </summary>
        /// <param name="buffer">The buffer for which to get a dictionary</param>
        /// <returns>The spelling dictionary for the buffer or null if one is not provided</returns>
        public SpellingDictionary GetDictionary(ITextBuffer buffer)
        {
            SpellingDictionary service = null;

            if (buffer != null && !buffer.Properties.TryGetProperty(typeof(SpellingDictionary), out service))
            {
                // Get the configuration and create the dictionary based on the configuration
                var config = this.GetConfiguration(buffer);

                if (config != null)
                {
                    // Create a dictionary for each configuration dictionary language ignoring any that are
                    // invalid and duplicates caused by missing languages which return the en-US dictionary.
                    var globalDictionaries = config.DictionaryLanguages.Select(l =>
                                                                               GlobalDictionary.CreateGlobalDictionary(l, globalServiceProvider,
                                                                                                                       config.AdditionalDictionaryFolders, config.RecognizedWords)).Where(
                        d => d != null).Distinct().ToList();

                    if (globalDictionaries.Any())
                    {
                        service = new SpellingDictionary(globalDictionaries, config.IgnoredWords);
                        buffer.Properties[typeof(SpellingDictionary)] = service;
                    }
                }
            }

            return(service);
        }
예제 #4
0
        //=====================================================================

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="globalDictionary">The global dictionary</param>
        /// <param name="ignoredWords">An optional enumerable list of ignored words</param>
        public SpellingDictionary(GlobalDictionary globalDictionary, IEnumerable <string> ignoredWords)
        {
            this.globalDictionary = globalDictionary;
            this.ignoredWords     = (ignoredWords ?? Enumerable.Empty <string>());

            // Register to receive events when the global dictionary is updated
            globalDictionary.RegisterSpellingDictionaryService(this);
        }
예제 #5
0
        //=====================================================================

        /// <summary>
        /// Create a global dictionary for the specified culture
        /// </summary>
        /// <param name="culture">The language to use for the dictionary.</param>
        /// <param name="additionalDictionaryFolders">An enumerable list of additional folders to search for
        /// other dictionaries.</param>
        /// <param name="recognizedWords">An optional list of recognized words that will be added to the
        /// dictionary (i.e. from a code analysis dictionary).</param>
        /// <returns>The global dictionary to use or null if one could not be created.</returns>
        public static GlobalDictionary CreateGlobalDictionary(CultureInfo culture,
                                                              IEnumerable <string> additionalDictionaryFolders, IEnumerable <string> recognizedWords)
        {
            GlobalDictionary globalDictionary = null;

            try
            {
                if (globalDictionaries == null)
                {
                    globalDictionaries = new Dictionary <string, GlobalDictionary>();
                }

                if (spellEngine == null)
                {
                    Hunspell.NativeDllPath = Path.Combine(Path.GetDirectoryName(
                                                              Assembly.GetExecutingAssembly().Location), "NHunspell");
                    spellEngine = new SpellEngine();
                }

                // The configuration editor should disallow creating a configuration without at least one
                // language but if someone edits the file manually, they could remove them all.  If that
                // happens, just use the English-US dictionary.
                if (culture == null)
                {
                    culture = new CultureInfo("en-US");
                }

                // If not already loaded, create the dictionary and the thread-safe spell factory instance for
                // the given culture.
                if (!globalDictionaries.TryGetValue(culture.Name, out globalDictionary))
                {
                    globalDictionary = new GlobalDictionary(culture, recognizedWords);

                    // Initialize the dictionaries asynchronously.  We don't care about the result here.
                    _ = Task.Run(() => globalDictionary.InitializeDictionary(additionalDictionaryFolders));

                    globalDictionaries.Add(culture.Name, globalDictionary);
                }
                else
                {
                    // Add recognized words that are not already there
                    globalDictionary.AddRecognizedWords(recognizedWords);
                }
            }
            catch (Exception ex)
            {
                // Ignore exceptions.  Not much we can do, we just won't spell check anything in this language.
                System.Diagnostics.Debug.WriteLine(ex);
            }

            return(globalDictionary);
        }
예제 #6
0
        //=====================================================================

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="bufferSpecificDictionaries">A list of buffer-specific dictionaries</param>
        /// <param name="globalDictionary">The global dictionary</param>
        public SpellingDictionaryService(IList <ISpellingDictionary> bufferSpecificDictionaries,
                                         GlobalDictionary globalDictionary)
        {
            this.globalDictionary           = globalDictionary;
            this.bufferSpecificDictionaries = bufferSpecificDictionaries;

            // TODO: This never gets disconnected and would probably keep this instance alive, right?  Probably
            // should switch to something like RegisterSpellingDictionaryService used by global dictionary.
            // Perhaps make that method part of the ISpellingDictionary interface?  Need to test it once a
            // buffer-specific class is actually implemented.
            foreach (var dictionary in bufferSpecificDictionaries)
            {
                dictionary.DictionaryUpdated += this.BufferSpecificDictionaryUpdated;
            }

            // Register to receive events when the global dictionary is updated
            globalDictionary.RegisterSpellingDictionaryService(this);
        }
예제 #7
0
        /// <summary>
        /// Add the given word to the dictionary so that it will no longer show up as an incorrect spelling
        /// </summary>
        /// <param name="word">The word to add to the dictionary.</param>
        /// <param name="culture">The culture of the dictionary to which the word is added or null to add it to
        /// the first dictionary.</param>
        /// <returns><c>true</c> if the word was successfully added to the dictionary, even if it was already in
        /// the dictionary.</returns>
        public bool AddWordToDictionary(string word, CultureInfo culture)
        {
            GlobalDictionary dictionary = null;

            if (String.IsNullOrWhiteSpace(word))
            {
                return(false);
            }

            if (culture != null)
            {
                dictionary = this.Dictionaries.FirstOrDefault(d => d.Culture == culture);
            }

            if (dictionary == null)
            {
                dictionary = this.Dictionaries.First();
            }

            return(this.ShouldIgnoreWord(word) || dictionary.AddWordToDictionary(word));
        }
예제 #8
0
        //=====================================================================

        /// <summary>
        /// This is used to clear the global dictionary cache whenever a solution is closed
        /// </summary>
        /// <remarks>The spelling service factory also contains code to clear the dictionary cache when it
        /// detects a change in solution.  This package will not load unless a configuration is edited.  This is
        /// needed to consistently clear the cache if editing configurations in different solutions without
        /// opening any spell checked files.</remarks>
        private static void solutionEvents_AfterClosing()
        {
            WpfTextBox.WpfTextBoxSpellChecker.ClearCache();
            GlobalDictionary.ClearDictionaryCache();
        }
예제 #9
0
        //=====================================================================

        /// <summary>
        /// Generate the configuration to use when spell checking the given text buffer
        /// </summary>
        /// <param name="buffer">The text buffer for which to generate a configuration</param>
        /// <returns>The generated configuration to use</returns>
        /// <remarks>The configuration is a merger of the global settings plus any solution, project, folder, and
        /// file settings related to the text buffer.</remarks>
        private SpellCheckerConfiguration GenerateConfiguration(ITextBuffer buffer)
        {
            ProjectItem projectItem, fileItem;
            string      bufferFilename, filename, projectPath, projectFilename = null;

            // Start with the global configuration
            var config = new SpellCheckerConfiguration();

            try
            {
                config.Load(SpellingConfigurationFile.GlobalConfigurationFilename);

                var dte2 = (globalServiceProvider == null) ? null :
                           globalServiceProvider.GetService(typeof(SDTE)) as DTE2;

                if (dte2 != null && dte2.Solution != null && !String.IsNullOrWhiteSpace(dte2.Solution.FullName))
                {
                    var solution = dte2.Solution;

                    // Clear the global dictionary cache when a change in solution is detected.  This handles
                    // cases where only the MEF components are loaded and not the package (i.e. a configuration
                    // has not been edited).  See VSSpellCheckerPackage.solutionEvents_AfterClosing().
                    if (lastSolutionName == null || !lastSolutionName.Equals(solution.FullName,
                                                                             StringComparison.OrdinalIgnoreCase))
                    {
                        GlobalDictionary.ClearDictionaryCache();
                        lastSolutionName = solution.FullName;
                    }

                    // See if there is a solution configuration
                    filename    = solution.FullName + ".vsspell";
                    projectItem = solution.FindProjectItem(filename);

                    if (projectItem != null)
                    {
                        config.Load(filename);
                    }

                    // Find the project item for the file we are opening
                    bufferFilename = buffer.GetFilename();
                    projectItem    = (bufferFilename != null) ? solution.FindProjectItem(bufferFilename) : null;

                    if (projectItem != null)
                    {
                        fileItem = projectItem;

                        // If we have a project (we should), see if it has settings
                        if (projectItem.ContainingProject != null &&
                            !String.IsNullOrWhiteSpace(projectItem.ContainingProject.FullName))
                        {
                            projectFilename = projectItem.ContainingProject.FullName;
                            filename        = projectFilename + ".vsspell";
                            projectItem     = solution.FindProjectItem(filename);

                            if (projectItem != null)
                            {
                                config.Load(filename);
                            }

                            // Get the full path based on the project.  The buffer filename will refer to the actual
                            // path which may be to a linked file outside the project's folder structure.
                            projectPath = Path.GetDirectoryName(filename);
                            filename    = Path.GetDirectoryName((string)fileItem.Properties.Item("FullPath").Value);

                            // Search for folder-specific configuration files
                            if (filename.StartsWith(projectPath, StringComparison.OrdinalIgnoreCase))
                            {
                                // Then check subfolders.  No need to check the root folder as the project
                                // settings cover it.
                                if (filename.Length > projectPath.Length)
                                {
                                    foreach (string folder in filename.Substring(projectPath.Length + 1).Split('\\'))
                                    {
                                        projectPath = Path.Combine(projectPath, folder);
                                        filename    = Path.Combine(projectPath, folder + ".vsspell");
                                        projectItem = solution.FindProjectItem(filename);

                                        if (projectItem != null)
                                        {
                                            config.Load(filename);
                                        }
                                    }
                                }
                            }

                            // If the item looks like a dependent file item, look for a settings file related to
                            // the parent file item.
                            if (fileItem.Collection != null && fileItem.Collection.Parent != null)
                            {
                                projectItem = fileItem.Collection.Parent as ProjectItem;

                                if (projectItem != null && projectItem.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFile)
                                {
                                    filename    = (string)projectItem.Properties.Item("FullPath").Value + ".vsspell";
                                    projectItem = solution.FindProjectItem(filename);

                                    if (projectItem != null)
                                    {
                                        config.Load(filename);
                                    }
                                }
                            }

                            // And finally, look for file-specific settings for the item itself
                            filename    = (string)fileItem.Properties.Item("FullPath").Value + ".vsspell";
                            projectItem = solution.FindProjectItem(filename);

                            if (projectItem != null)
                            {
                                config.Load(filename);
                            }
                        }
                        else
                        if (projectItem.Kind == EnvDTE.Constants.vsProjectItemKindSolutionItems)
                        {
                            // Looks like a solution item, see if a related setting file exists
                            filename = bufferFilename + ".vsspell";

                            projectItem = solution.FindProjectItem(filename);

                            if (projectItem != null)
                            {
                                config.Load(filename);
                            }
                        }
                    }

                    // Load code analysis dictionaries if wanted
                    if (projectFilename != null && config.CadOptions.ImportCodeAnalysisDictionaries)
                    {
                        // I'm not sure if there's a better way to do this but it does seem to work.  We need to
                        // find one or more arbitrary files with an item type of "CodeAnalysisDictionary".  We
                        // do so by getting the MSBuild project from the global project collection and using its
                        // GetItems() method to find them.
                        var loadedProject = Microsoft.Build.Evaluation.ProjectCollection.GlobalProjectCollection.GetLoadedProjects(
                            projectFilename).FirstOrDefault();

                        if (loadedProject != null)
                        {
                            // Typically there is only one but multiple files are supported
                            foreach (var cad in loadedProject.GetItems("CodeAnalysisDictionary"))
                            {
                                filename = Path.Combine(Path.GetDirectoryName(projectFilename), cad.EvaluatedInclude);

                                if (File.Exists(filename))
                                {
                                    config.ImportCodeAnalysisDictionary(filename);
                                }
                            }
                        }
                    }

                    if (bufferFilename != null && config.DetermineResourceFileLanguageFromName &&
                        Path.GetExtension(bufferFilename).Equals(".resx", StringComparison.OrdinalIgnoreCase))
                    {
                        // Localized resource files are expected to have filenames in the format
                        // BaseName.Language.resx (i.e. LocalizedForm.de-DE.resx).
                        bufferFilename = Path.GetExtension(Path.GetFileNameWithoutExtension(bufferFilename));

                        if (bufferFilename.Length > 1)
                        {
                            bufferFilename = bufferFilename.Substring(1);

                            SpellCheckerDictionary match;

                            if (SpellCheckerDictionary.AvailableDictionaries(
                                    config.AdditionalDictionaryFolders).TryGetValue(bufferFilename, out match))
                            {
                                // Clear any existing dictionary languages and use just the one that matches the
                                // file's language.
                                config.DictionaryLanguages.Clear();
                                config.DictionaryLanguages.Add(match.Culture);
                            }
                        }
                    }
                }
                else
                if (lastSolutionName != null)
                {
                    // A solution was closed and a file has been opened outside of a solution so clear the
                    // cache and use the global dictionaries.
                    GlobalDictionary.ClearDictionaryCache();
                    lastSolutionName = null;
                }
            }
            catch (Exception ex)
            {
                // Ignore errors, we just won't load the configurations after the point of failure
                System.Diagnostics.Debug.WriteLine(ex);
            }

            return(config);
        }
예제 #10
0
        //=====================================================================

        /// <summary>
        /// Create a global dictionary for the specified culture
        /// </summary>
        /// <param name="culture">The language to use for the dictionary.</param>
        /// <param name="serviceProvider">A service provider used to interact with the solution/project</param>
        /// <param name="additionalDictionaryFolders">An enumerable list of additional folders to search for
        /// other dictionaries.</param>
        /// <param name="recognizedWords">An optional list of recognized words that will be added to the
        /// dictionary (i.e. from a code analysis dictionary).</param>
        /// <returns>The global dictionary to use or null if one could not be created.</returns>
        public static GlobalDictionary CreateGlobalDictionary(CultureInfo culture, IServiceProvider serviceProvider,
                                                              IEnumerable <string> additionalDictionaryFolders, IEnumerable <string> recognizedWords)
        {
            GlobalDictionary globalDictionary = null;
            string           affixFile, dictionaryFile, userWordsFile;

            try
            {
                // The configuration editor should disallow creating a configuration without at least one
                // language but if someone edits the file manually, they could remove them all.  If that
                // happens, just use the English-US dictionary.
                if (culture == null)
                {
                    culture = new CultureInfo("en-US");
                }

                if (globalDictionaries == null)
                {
                    globalDictionaries = new Dictionary <string, GlobalDictionary>();
                }

                // If not already loaded, create the dictionary and the thread-safe spell factory instance for
                // the given culture.
                if (!globalDictionaries.ContainsKey(culture.Name))
                {
                    string dllPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
                                                  "NHunspell");

                    if (spellEngine == null)
                    {
                        Hunspell.NativeDllPath = dllPath;
                        spellEngine            = new SpellEngine();
                    }

                    // Look for all available dictionaries and get the one for the requested culture
                    var dictionaries = SpellCheckerDictionary.AvailableDictionaries(additionalDictionaryFolders);
                    SpellCheckerDictionary userDictionary;

                    if (dictionaries.TryGetValue(culture.Name, out userDictionary))
                    {
                        affixFile      = userDictionary.AffixFilePath;
                        dictionaryFile = userDictionary.DictionaryFilePath;
                        userWordsFile  = userDictionary.UserDictionaryFilePath;
                    }
                    else
                    {
                        affixFile = dictionaryFile = userWordsFile = null;
                    }

                    // If not found, default to the English dictionary supplied with the package.  This can at
                    // least clue us in that it didn't find the language-specific dictionary when the suggestions
                    // are in English.
                    if (affixFile == null || dictionaryFile == null || !File.Exists(affixFile) ||
                        !File.Exists(dictionaryFile))
                    {
                        affixFile      = Path.Combine(dllPath, "en_US.aff");
                        dictionaryFile = Path.ChangeExtension(affixFile, ".dic");
                        userWordsFile  = Path.Combine(SpellingConfigurationFile.GlobalConfigurationFilePath,
                                                      "en-US_User.dic");
                    }

                    spellEngine.AddLanguage(new LanguageConfig
                    {
                        LanguageCode     = culture.Name,
                        HunspellAffFile  = affixFile,
                        HunspellDictFile = dictionaryFile
                    });

                    globalDictionaries.Add(culture.Name, new GlobalDictionary(culture, spellEngine[culture.Name],
                                                                              dictionaryFile, userWordsFile, serviceProvider));
                }

                globalDictionary = globalDictionaries[culture.Name];

                // Add recognized words that are not already there
                globalDictionary.AddRecognizedWords(recognizedWords);
            }
            catch (Exception ex)
            {
                // Ignore exceptions.  Not much we can do, we just won't spell check anything.
                System.Diagnostics.Debug.WriteLine(ex);
            }

            return(globalDictionary);
        }
        /// <summary>
        /// Generate the configuration to use when spell checking the given text buffer
        /// </summary>
        /// <param name="buffer">The text buffer for which to generate a configuration</param>
        /// <returns>The generated configuration to use</returns>
        /// <remarks>The configuration is a merger of the global settings plus any solution, project, folder, and
        /// file settings related to the text buffer.</remarks>
        private static SpellCheckerConfiguration GenerateConfiguration(ITextBuffer buffer)
        {
            Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();

            ProjectItem projectItem, fileItem;
            string      bufferFilename, filename, projectPath, projectFilename = null;

            // Start with the global configuration
            var config = new SpellCheckerConfiguration();

            try
            {
                config.Load(SpellingConfigurationFile.GlobalConfigurationFilename);

                if (Package.GetGlobalService(typeof(SDTE)) is DTE2 dte2 && dte2.Solution != null &&
                    !String.IsNullOrWhiteSpace(dte2.Solution.FullName))
                {
                    var solution = dte2.Solution;

                    // Clear the global dictionary cache when a change in solution is detected.  This handles
                    // cases where only the MEF components are loaded and not the package (i.e. a configuration
                    // has not been edited).  See VSSpellCheckerPackage.solutionEvents_AfterClosing().
                    if (LastSolutionName == null || !LastSolutionName.Equals(solution.FullName,
                                                                             StringComparison.OrdinalIgnoreCase))
                    {
                        WpfTextBox.WpfTextBoxSpellChecker.ClearCache();
                        GlobalDictionary.ClearDictionaryCache();
                        LastSolutionName = solution.FullName;
                    }

                    // See if there is a solution configuration
                    filename    = solution.FullName + ".vsspell";
                    projectItem = solution.FindProjectItemForFile(filename);

                    if (projectItem != null)
                    {
                        config.Load(filename);
                    }

                    // Find the project item for the file we are opening
                    bufferFilename = buffer.GetFilename();
                    projectItem    = solution.FindProjectItemForFile(bufferFilename);

                    if (projectItem != null)
                    {
                        fileItem = projectItem;

                        // If we have a project (we should), see if it has settings
                        if (projectItem.ContainingProject != null &&
                            !String.IsNullOrWhiteSpace(projectItem.ContainingProject.FullName))
                        {
                            projectFilename = projectItem.ContainingProject.FullName;

                            // Website projects are named after the folder
                            if (projectFilename.Length > 1 && projectFilename[projectFilename.Length - 1] == '\\')
                            {
                                filename = Path.GetFileName(projectFilename.Substring(0, projectFilename.Length - 1));
                                filename = projectFilename + filename + ".vsspell";
                            }
                            else
                            {
                                filename = projectFilename + ".vsspell";
                            }

                            projectItem = solution.FindProjectItemForFile(filename);

                            if (projectItem != null)
                            {
                                config.Load(filename);
                            }

                            // Get the full path based on the project.  The buffer filename will refer to the actual
                            // path which may be to a linked file outside the project's folder structure.
                            projectPath = Path.GetDirectoryName(filename);
                            filename    = Path.GetDirectoryName((string)fileItem.Properties.Item("FullPath").Value);

                            // Search for folder-specific configuration files
                            if (filename.StartsWith(projectPath, StringComparison.OrdinalIgnoreCase))
                            {
                                // Then check subfolders.  No need to check the root folder as the project
                                // settings cover it.
                                if (filename.Length > projectPath.Length)
                                {
                                    foreach (string folder in filename.Substring(projectPath.Length + 1).Split('\\'))
                                    {
                                        projectPath = Path.Combine(projectPath, folder);
                                        filename    = Path.Combine(projectPath, folder + ".vsspell");
                                        projectItem = solution.FindProjectItemForFile(filename);

                                        if (projectItem != null)
                                        {
                                            config.Load(filename);
                                        }
                                    }
                                }
                            }

                            // If the item looks like a dependent file item, look for a settings file related to
                            // the parent file item.
                            if (fileItem.Collection != null && fileItem.Collection.Parent != null)
                            {
                                projectItem = fileItem.Collection.Parent as ProjectItem;

                                if (projectItem != null && projectItem.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFile)
                                {
                                    filename    = (string)projectItem.Properties.Item("FullPath").Value + ".vsspell";
                                    projectItem = solution.FindProjectItemForFile(filename);

                                    if (projectItem != null)
                                    {
                                        config.Load(filename);
                                    }
                                }
                            }

                            // And finally, look for file-specific settings for the item itself
                            filename    = (string)fileItem.Properties.Item("FullPath").Value + ".vsspell";
                            projectItem = solution.FindProjectItemForFile(filename);

                            if (projectItem != null)
                            {
                                config.Load(filename);
                            }
                        }
                        else
                        if (projectItem.Kind == EnvDTE.Constants.vsProjectItemKindSolutionItems)
                        {
                            // Looks like a solution item, see if a related setting file exists
                            filename = bufferFilename + ".vsspell";

                            projectItem = solution.FindProjectItemForFile(filename);

                            if (projectItem != null)
                            {
                                config.Load(filename);
                            }
                        }
                    }

                    // Load code analysis dictionaries if wanted
                    if (projectFilename != null && config.CadOptions.ImportCodeAnalysisDictionaries)
                    {
                        // Typically there is only one but multiple files are supported
                        foreach (var cad in SpellCheckFileInfo.ProjectCodeAnalysisDictionaries(projectFilename))
                        {
                            if (File.Exists(cad.CanonicalName))
                            {
                                config.ImportCodeAnalysisDictionary(cad.CanonicalName);
                            }
                        }
                    }

                    if (bufferFilename != null && config.DetermineResourceFileLanguageFromName &&
                        Path.GetExtension(bufferFilename).Equals(".resx", StringComparison.OrdinalIgnoreCase))
                    {
                        // Localized resource files are expected to have filenames in the format
                        // BaseName.Language.resx (i.e. LocalizedForm.de-DE.resx).
                        bufferFilename = Path.GetExtension(Path.GetFileNameWithoutExtension(bufferFilename));

                        if (bufferFilename.Length > 1)
                        {
                            bufferFilename = bufferFilename.Substring(1);

                            if (SpellCheckerDictionary.AvailableDictionaries(
                                    config.AdditionalDictionaryFolders).TryGetValue(bufferFilename,
                                                                                    out SpellCheckerDictionary match))
                            {
                                // Clear any existing dictionary languages and use just the one that matches the
                                // file's language.
                                config.DictionaryLanguages.Clear();
                                config.DictionaryLanguages.Add(match.Culture);
                            }
                        }
                    }
                }
                else
                if (LastSolutionName != null)
                {
                    // A solution was closed and a file has been opened outside of a solution so clear the
                    // cache and use the global dictionaries.
                    WpfTextBox.WpfTextBoxSpellChecker.ClearCache();
                    GlobalDictionary.ClearDictionaryCache();
                    LastSolutionName = null;
                }
            }
        //=====================================================================

        /// <summary>
        /// Create a global dictionary for the specified culture
        /// </summary>
        /// <param name="culture">The language to use for the dictionary</param>
        /// <returns>The spell factory to use or null if one could not be created</returns>
        public static GlobalDictionary CreateGlobalDictionary(CultureInfo culture)
        {
            GlobalDictionary globalDictionary = null;
            string           affixFile, dictionaryFile;

            try
            {
                if (globalDictionaries == null)
                {
                    globalDictionaries = new Dictionary <string, GlobalDictionary>();
                }

                // If no culture is specified, use the default culture
                if (culture == null)
                {
                    culture = SpellCheckerConfiguration.DefaultLanguage;
                }

                // If not already loaded, create the dictionary and the thread-safe spell factory instance for
                // the given culture.
                if (!globalDictionaries.ContainsKey(culture.Name))
                {
                    string dllPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
                                                  "NHunspell");

                    if (spellEngine == null)
                    {
                        Hunspell.NativeDllPath = dllPath;
                        spellEngine            = new SpellEngine();
                    }

                    // Look in the configuration folder first for user-supplied dictionaries
                    affixFile = Path.Combine(SpellCheckerConfiguration.ConfigurationFilePath,
                                             culture.Name + ".aff");
                    dictionaryFile = Path.ChangeExtension(affixFile, ".dic");

                    // Dictionary file names may use a dash or an underscore as the separator.  Try both ways
                    // in case they aren't named consistently which does happen.
                    if (!File.Exists(affixFile))
                    {
                        affixFile = Path.Combine(SpellCheckerConfiguration.ConfigurationFilePath,
                                                 culture.Name.Replace('-', '_') + ".aff");
                    }

                    if (!File.Exists(dictionaryFile))
                    {
                        dictionaryFile = Path.Combine(SpellCheckerConfiguration.ConfigurationFilePath,
                                                      culture.Name.Replace('-', '_') + ".dic");
                    }

                    // If not found, default to the English dictionary supplied with the package.  This can at
                    // least clue us in that it didn't find the language-specific dictionary when the suggestions
                    // are in English.
                    if (!File.Exists(affixFile) || !File.Exists(dictionaryFile))
                    {
                        affixFile      = Path.Combine(dllPath, "en_US.aff");
                        dictionaryFile = Path.ChangeExtension(affixFile, ".dic");
                    }

                    spellEngine.AddLanguage(new LanguageConfig
                    {
                        LanguageCode     = culture.Name,
                        HunspellAffFile  = affixFile,
                        HunspellDictFile = dictionaryFile
                    });

                    globalDictionaries.Add(culture.Name, new GlobalDictionary(culture, spellEngine[culture.Name]));
                }

                globalDictionary = globalDictionaries[culture.Name];
            }
            catch (Exception ex)
            {
                // Ignore exceptions.  Not much we can do, we'll just not spell check anything.
                System.Diagnostics.Debug.WriteLine(ex);
            }

            return(globalDictionary);
        }
예제 #13
0
        //=====================================================================

        /// <summary>
        /// This is used to clear the global dictionary cache whenever a solution is closed
        /// </summary>
        /// <remarks>The spelling service factory also contains code to clear the dictionary cache when it
        /// detects a change in solution.  This package will not load unless a configuration is edited.  This is
        /// needed to consistently clear the cache if editing configurations in different solutions without
        /// opening any spell checked files.</remarks>
        void solutionEvents_AfterClosing()
        {
            GlobalDictionary.ClearDictionaryCache();
        }
예제 #14
0
        //=====================================================================

        /// <summary>
        /// Generate the configuration to use when spell checking the given text buffer
        /// </summary>
        /// <param name="buffer">The text buffer for which to generate a configuration</param>
        /// <returns>The generated configuration to use</returns>
        /// <remarks>The configuration is a merger of the global settings plus any solution, project, folder, and
        /// file settings related to the text buffer.</remarks>
        private SpellCheckerConfiguration GenerateConfiguration(ITextBuffer buffer)
        {
            ProjectItem projectItem, fileItem;
            string      bufferFilename, filename, projectPath;

            // Start with the global configuration
            var config = new SpellCheckerConfiguration();

            try
            {
                config.Load(SpellingConfigurationFile.GlobalConfigurationFilename);

                var dte2 = (globalServiceProvider == null) ? null :
                           globalServiceProvider.GetService(typeof(SDTE)) as DTE2;

                if (dte2 != null && dte2.Solution != null && !String.IsNullOrWhiteSpace(dte2.Solution.FullName))
                {
                    var solution = dte2.Solution;

                    // Clear the global dictionary cache when a change in solution is detected
                    if (lastSolutionName == null || !lastSolutionName.Equals(solution.FullName,
                                                                             StringComparison.OrdinalIgnoreCase))
                    {
                        GlobalDictionary.ClearDictionaryCache();
                        lastSolutionName = solution.FullName;
                    }

                    // See if there is a solution configuration
                    filename    = solution.FullName + ".vsspell";
                    projectItem = solution.FindProjectItem(filename);

                    if (projectItem != null)
                    {
                        config.Load(filename);
                    }

                    // Find the project item for the file we are opening
                    bufferFilename = buffer.GetFilename();
                    projectItem    = (bufferFilename != null) ? solution.FindProjectItem(bufferFilename) : null;

                    if (projectItem != null)
                    {
                        fileItem = projectItem;

                        // If we have a project (we should), see if it has settings
                        if (projectItem.ContainingProject != null &&
                            !String.IsNullOrWhiteSpace(projectItem.ContainingProject.FullName))
                        {
                            filename    = projectItem.ContainingProject.FullName + ".vsspell";
                            projectItem = solution.FindProjectItem(filename);

                            if (projectItem != null)
                            {
                                config.Load(filename);
                            }

                            // Get the full path based on the project.  The buffer filename will refer to the actual
                            // path which may be to a linked file outside the project's folder structure.
                            projectPath = Path.GetDirectoryName(filename);
                            filename    = Path.GetDirectoryName((string)fileItem.Properties.Item("FullPath").Value);

                            // Search for folder-specific configuration files
                            if (filename.StartsWith(projectPath, StringComparison.OrdinalIgnoreCase))
                            {
                                // Then check subfolders.  No need to check the root folder as the project
                                // settings cover it.
                                if (filename.Length > projectPath.Length)
                                {
                                    foreach (string folder in filename.Substring(projectPath.Length + 1).Split('\\'))
                                    {
                                        projectPath = Path.Combine(projectPath, folder);
                                        filename    = Path.Combine(projectPath, folder + ".vsspell");
                                        projectItem = solution.FindProjectItem(filename);

                                        if (projectItem != null)
                                        {
                                            config.Load(filename);
                                        }
                                    }
                                }
                            }

                            // If the item looks like a dependent file item, look for a settings file related to
                            // the parent file item.
                            if (fileItem.Collection != null && fileItem.Collection.Parent != null)
                            {
                                projectItem = fileItem.Collection.Parent as ProjectItem;

                                if (projectItem != null && projectItem.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFile)
                                {
                                    filename    = (string)projectItem.Properties.Item("FullPath").Value + ".vsspell";
                                    projectItem = solution.FindProjectItem(filename);

                                    if (projectItem != null)
                                    {
                                        config.Load(filename);
                                    }
                                }
                            }

                            // And finally, look for file-specific settings for the item itself
                            filename    = (string)fileItem.Properties.Item("FullPath").Value + ".vsspell";
                            projectItem = solution.FindProjectItem(filename);

                            if (projectItem != null)
                            {
                                config.Load(filename);
                            }
                        }
                    }

                    if (bufferFilename != null && config.DetermineResourceFileLanguageFromName &&
                        Path.GetExtension(bufferFilename).Equals(".resx", StringComparison.OrdinalIgnoreCase))
                    {
                        // Localized resource files are expected to have filenames in the format
                        // BaseName.Language.resx (i.e. LocalizedForm.de-DE.resx).
                        bufferFilename = Path.GetExtension(Path.GetFileNameWithoutExtension(bufferFilename));

                        if (bufferFilename.Length > 1)
                        {
                            bufferFilename = bufferFilename.Substring(1);

                            SpellCheckerDictionary match;

                            if (SpellCheckerDictionary.AvailableDictionaries(
                                    config.AdditionalDictionaryFolders).TryGetValue(bufferFilename, out match))
                            {
                                config.DefaultLanguage = match.Culture;
                            }
                        }
                    }
                }
                else
                if (lastSolutionName != null)
                {
                    // A solution was closed and a file has been opened outside of a solution so clear the
                    // cache and use the global dictionaries.
                    GlobalDictionary.ClearDictionaryCache();
                    lastSolutionName = null;
                }
            }
            catch (Exception ex)
            {
                // Ignore errors, we just won't load the configurations after the point of failure
                System.Diagnostics.Debug.WriteLine(ex);
            }

            return(config);
        }