/// <summary> /// This is used to generate the configuration for the instance /// </summary> /// <returns>The configuration to use or null if the file should not be spell checked (disabled or not a /// type of file that can be spell checked such as a binary file).</returns> public SpellCheckerConfiguration GenerateConfiguration(IEnumerable<string> codeAnalysisFiles) { string configFile; var config = new SpellCheckerConfiguration(); try { // Start with the global configuration and work on down config.Load(SpellingConfigurationFile.GlobalConfigurationFilename); if(this.HasSolutionConfigurationFile) { configFile = this.SolutionFile + ".vsspell"; if(File.Exists(configFile)) config.Load(configFile); } if(this.HasProjectConfigurationFile) { configFile = this.ProjectFile + ".vsspell"; if(File.Exists(configFile)) config.Load(configFile); } if(this.FolderConfigurationFiles.Any()) foreach(string cf in this.FolderConfigurationFiles) if(File.Exists(cf)) config.Load(cf); if(this.DependencyConfigurationFile != null && File.Exists(this.DependencyConfigurationFile)) config.Load(this.DependencyConfigurationFile); if(this.HasRelatedConfigurationFile) { configFile = this.CanonicalName + ".vsspell"; if(File.Exists(configFile)) config.Load(configFile); } // Merge any code analysis dictionary settings if(codeAnalysisFiles != null) foreach(string cad in codeAnalysisFiles) if(File.Exists(cad)) config.ImportCodeAnalysisDictionary(cad); // If wanted, set the language based on the resource filename if(config.DetermineResourceFileLanguageFromName && Path.GetExtension(this.Filename).Equals(".resx", StringComparison.OrdinalIgnoreCase)) { // Localized resource files are expected to have filenames in the format // BaseName.Language.resx (i.e. LocalizedForm.de-DE.resx). string ext = Path.GetExtension(Path.GetFileNameWithoutExtension(this.Filename)); if(ext.Length > 1) { ext = ext.Substring(1); SpellCheckerDictionary match; if(SpellCheckerDictionary.AvailableDictionaries( config.AdditionalDictionaryFolders).TryGetValue(ext, 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); } } } } catch(Exception ex) { // Ignore errors, we just won't load the configurations after the point of failure System.Diagnostics.Debug.WriteLine(ex); } return (!config.IncludeInProjectSpellCheck || config.IsExcludedByExtension(Path.GetExtension(this.Filename)) || IsBinaryFile(this.CanonicalName)) ? null : config; }
//===================================================================== /// <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; }
/// <summary> /// Generate the configuration for all parent items /// </summary> /// <returns>The generated configuration to use</returns> /// <remarks>The configuration is a merger of the global settings plus any solution, project, and folder /// settings related to but excluding the current configuration file. This allows us to determine the /// inherited additional dictionary folders to use.</remarks> private SpellCheckerConfiguration GenerateParentConfiguration() { ProjectItem projectItem, fileItem; string filename, projectPath; // Start with the global configuration var config = new SpellCheckerConfiguration(); if(isGlobal) return config; try { config.Load(SpellingConfigurationFile.GlobalConfigurationFilename); if(configType == ConfigurationType.Solution) return config; var dte2 = Utility.GetServiceFromPackage<DTE2, SDTE>(true); if(dte2 != null && dte2.Solution != null && !String.IsNullOrWhiteSpace(dte2.Solution.FullName)) { var solution = dte2.Solution; // See if there is a solution configuration filename = solution.FullName + ".vsspell"; projectItem = solution.FindProjectItem(filename); if(projectItem != null) config.Load(filename); if(configType == ConfigurationType.Project) return config; // Find the project item for the file we are opening if(configType != ConfigurationType.Folder) projectItem = solution.FindProjectItem(relatedFilename); else projectItem = solution.FindProjectItem(Path.Combine(relatedFilename, relatedFilename + ".vsspell")); 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 configuration 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"); if(configType == ConfigurationType.Folder && Path.GetFileNameWithoutExtension(filename) == relatedFilename) return config; 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); } } } } } } catch(Exception ex) { // Ignore errors, we just won't load the configurations after the point of failure System.Diagnostics.Debug.WriteLine(ex); } return config; }