/// <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;
                }
            }
Exemple #2
0
        //=====================================================================

        /// <summary>
        /// This is used to determine the containing project and settings filename when adding a new spell
        /// checker configuration file.
        /// </summary>
        /// <param name="containingProject">On return, this contains the containing project or null if adding
        /// a solution configuration file.</param>
        /// <param name="settingsFilename">On return, this contains the name of the settings file to be added</param>
        /// <returns>True if a settings file can be added for the item selected in the Solution Explorer window
        /// or false if not.</returns>
        private static bool DetermineContainingProjectAndSettingsFile(out Project containingProject,
                                                                      out string settingsFilename)
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            string folderName = null;

            containingProject = null;
            settingsFilename  = null;

            // Only add if a single file is selected
            if (!(Package.GetGlobalService(typeof(SDTE)) is DTE2 dte2) || dte2.SelectedItems.Count != 1)
            {
                return(false);
            }

            SelectedItem item = dte2.SelectedItems.Item(1);

            if (item.Project != null && item.Project.Kind != EnvDTE.Constants.vsProjectKindSolutionItems &&
                item.Project.Kind != EnvDTE.Constants.vsProjectKindUnmodeled &&
                item.Project.Kind != EnvDTE.Constants.vsProjectKindMisc)
            {
                string path = null;

                // Looks like a project.  Not all of them implement properties though.
                if (!String.IsNullOrWhiteSpace(item.Project.FullName) && item.Project.FullName.EndsWith(
                        "proj", StringComparison.OrdinalIgnoreCase))
                {
                    path = item.Project.FullName;
                }

                if (path == null && item.Project.Properties != null)
                {
                    Property fullPath;

                    try
                    {
                        fullPath = item.Project.Properties.Item("FullPath");
                    }
                    catch
                    {
                        // C++ projects use a different property name and throw an exception above
                        try
                        {
                            fullPath = item.Project.Properties.Item("ProjectFile");
                        }
                        catch
                        {
                            // If that fails, give up
                            fullPath = null;
                        }
                    }

                    if (fullPath != null && fullPath.Value != null)
                    {
                        path = (string)fullPath.Value;
                    }
                }

                if (!String.IsNullOrWhiteSpace(path))
                {
#pragma warning disable VSTHRD010
                    var project = dte2.Solution.EnumerateProjects().FirstOrDefault(p => p.Name == item.Name);
#pragma warning restore VSTHRD010

                    if (project != null)
                    {
                        containingProject = project;
                        settingsFilename  = project.FullName;

                        // Website projects are named after the folder rather than a file
                        if (settingsFilename.Length > 1 && settingsFilename[settingsFilename.Length - 1] == '\\')
                        {
                            folderName        = settingsFilename;
                            settingsFilename += item.Name;
                        }
                    }
                }
            }
            else
            if (item.ProjectItem == null || item.ProjectItem.ContainingProject == null)
            {
                // Looks like a solution
                if (Path.GetFileNameWithoutExtension(dte2.Solution.FullName) == item.Name)
                {
                    settingsFilename = dte2.Solution.FullName;
                }
            }
            else
            if (item.ProjectItem.Properties != null)
            {
                // Looks like a folder or file item
                Property fullPath;

                try
                {
                    fullPath = item.ProjectItem.Properties.Item("FullPath");
                }
                catch
                {
                    fullPath = null;
                }

                if (fullPath != null && fullPath.Value != null)
                {
                    string path = (string)fullPath.Value;

                    if (!String.IsNullOrWhiteSpace(path))
                    {
                        containingProject = item.ProjectItem.ContainingProject;

                        // Folder items have a trailing backslash in some project systems, others don't.
                        // We'll put the configuration file in the folder using its name as the filename.
                        if (path[path.Length - 1] == '\\' || (!File.Exists(path) && Directory.Exists(path)))
                        {
                            if (path[path.Length - 1] != '\\')
                            {
                                path += @"\";
                            }

                            folderName       = path;
                            settingsFilename = path + item.Name;
                        }
                        else
                        {
                            settingsFilename = path;
                        }
                    }
                }
            }
            else
            if (item.ProjectItem.Kind == EnvDTE.Constants.vsProjectItemKindSolutionItems)
            {
                // Looks like a solution item
                settingsFilename = item.ProjectItem.get_FileNames(1);
            }

            if (settingsFilename != null)
            {
                if (settingsFilename.EndsWith(".vsspell", StringComparison.OrdinalIgnoreCase) ||
                    ((folderName == null && !File.Exists(settingsFilename)) ||
                     (folderName != null && !Directory.Exists(folderName))))
                {
                    settingsFilename = null;
                }
                else
                if (folderName == null)
                {
                    if (SpellCheckFileInfo.IsBinaryFile(settingsFilename))
                    {
                        settingsFilename = null;
                    }
                    else
                    {
                        settingsFilename += ".vsspell";
                    }
                }
                else
                {
                    settingsFilename += ".vsspell";
                }
            }

            return(settingsFilename != null);
        }
Exemple #3
0
        //=====================================================================

        /// <summary>
        /// This is used to determine the containing project and settings filename when adding a new spell
        /// checker configuration file.
        /// </summary>
        /// <param name="containingProject">On return, this contains the containing project or null if adding
        /// a solution configuration file.</param>
        /// <param name="settingsFilename">On return, this contains the name of the settings file to be added</param>
        /// <returns>True if a settings file can be added for the item selected in the Solution Explorer window
        /// or false if not.</returns>
        private static bool DetermineContainingProjectAndSettingsFile(out Project containingProject,
                                                                      out string settingsFilename)
        {
            string folderName = null;

            containingProject = null;
            settingsFilename  = null;

            var dte2 = Utility.GetServiceFromPackage <DTE2, SDTE>(true);

            // Only add if a single file is selected
            if (dte2 == null || dte2.SelectedItems.Count != 1)
            {
                return(false);
            }

            SelectedItem item = dte2.SelectedItems.Item(1);

            if (item.Project != null && item.Project.Kind != EnvDTE.Constants.vsProjectKindSolutionItems &&
                item.Project.Kind != EnvDTE.Constants.vsProjectKindUnmodeled &&
                item.Project.Kind != EnvDTE.Constants.vsProjectKindMisc)
            {
                // Looks like a project
                Property fullPath = item.Project.Properties.Item("FullPath");

                if (fullPath != null && fullPath.Value != null)
                {
                    string path = (string)fullPath.Value;

                    if (!String.IsNullOrWhiteSpace(path))
                    {
                        var project = dte2.Solution.EnumerateProjects().FirstOrDefault(p => p.Name == item.Name);

                        if (project != null)
                        {
                            containingProject = project;
                            settingsFilename  = project.FullName;

                            // Website projects are named after the folder rather than a file
                            if (settingsFilename.Length > 1 && settingsFilename[settingsFilename.Length - 1] == '\\')
                            {
                                folderName        = settingsFilename;
                                settingsFilename += item.Name;
                            }
                        }
                    }
                }
            }
            else
            if (item.ProjectItem == null || item.ProjectItem.ContainingProject == null)
            {
                // Looks like a solution
                if (Path.GetFileNameWithoutExtension(dte2.Solution.FullName) == item.Name)
                {
                    settingsFilename = dte2.Solution.FullName;
                }
            }
            else
            if (item.ProjectItem.Properties != null)
            {
                // Looks like a folder or file item
                Property fullPath = item.ProjectItem.Properties.Item("FullPath");

                if (fullPath != null && fullPath.Value != null)
                {
                    string path = (string)fullPath.Value;

                    if (!String.IsNullOrWhiteSpace(path))
                    {
                        containingProject = item.ProjectItem.ContainingProject;

                        // Folder items have a trailing backslash.  We'll put the configuration file in
                        // the folder using its name as the filename.
                        if (path[path.Length - 1] == '\\')
                        {
                            folderName       = path;
                            settingsFilename = path + item.Name;
                        }
                        else
                        {
                            settingsFilename = path;
                        }
                    }
                }
            }
            else
            if (item.ProjectItem.Kind == EnvDTE.Constants.vsProjectItemKindSolutionItems)
            {
                // Looks like a solution item
                settingsFilename = item.ProjectItem.get_FileNames(1);
            }

            if (settingsFilename != null)
            {
                if (settingsFilename.EndsWith(".vsspell", StringComparison.OrdinalIgnoreCase) ||
                    ((folderName == null && !File.Exists(settingsFilename)) ||
                     (folderName != null && !Directory.Exists(folderName))))
                {
                    settingsFilename = null;
                }
                else
                if (folderName == null)
                {
                    if (SpellCheckFileInfo.IsBinaryFile(settingsFilename))
                    {
                        settingsFilename = null;
                    }
                    else
                    {
                        settingsFilename += ".vsspell";
                    }
                }
                else
                {
                    settingsFilename += ".vsspell";
                }
            }

            return(settingsFilename != null);
        }