Example #1
0
        //=====================================================================

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="filename">The filename to load</param>
        /// <param name="spellCheckConfiguration">The spell checker configuration for the file</param>
        public XmlClassifier(string filename, SpellCheckerConfiguration spellCheckConfiguration) :
          base(filename, spellCheckConfiguration)
        {
            try
            {
                // If an encoding is specified, re-read it using the correct encoding
                Match m = reXmlEncoding.Match(this.Text);

                if(m.Success)
                {
                    var encoding = Encoding.GetEncoding(m.Groups["Encoding"].Value);

                    if(encoding != Encoding.Default)
                    {
                        using(StreamReader sr = new StreamReader(filename, encoding, true))
                        {
                            this.SetText(sr.ReadToEnd());
                        }
                    }
                }
            }
            catch(Exception ex)
            {
                // Ignore errors for invalid encodings.  We'll just use the default
                System.Diagnostics.Debug.WriteLine(ex);
            }
        }
        //=====================================================================
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="filename">The filename to load</param>
        /// <param name="spellCheckConfiguration">The spell checker configuration for the file</param>
        /// <param name="classifierConfiguration">The configuration element containing the classification
        /// expressions and their range types.</param>
        public CodeClassifier(string filename, SpellCheckerConfiguration spellCheckConfiguration,
            XElement classifierConfiguration)
            : base(filename, spellCheckConfiguration, classifierConfiguration)
        {
            xmlDocCommentDelimiter = (string)classifierConfiguration.Attribute("XmlDocCommentDelimiter");
            quadSlashDelimiter = (string)classifierConfiguration.Attribute("QuadSlashDelimiter");
            oldStyleDocCommentDelimiter = (string)classifierConfiguration.Attribute("OldStyleDocCommentDelimiter");

            isCSharp = filename.EndsWith(".cs", StringComparison.OrdinalIgnoreCase);
        }
Example #3
0
        //=====================================================================

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="filename">The filename to use</param>
        /// <param name="defaultConfig">A default configuration to use for missing properties</param>
        public SpellingConfigurationFile(string filename, SpellCheckerConfiguration defaultConfig)
        {
            if (String.IsNullOrWhiteSpace(filename))
            {
                throw new ArgumentNullException("filename", "Filename cannot be null or empty");
            }

            try
            {
                // Get the property cache for finding current and default values
                propertyCache = new Dictionary <string, PropertyInfo>();
                configCache   = TypeDescriptor.GetProperties(typeof(SpellCheckerConfiguration));
                csoCache      = TypeDescriptor.GetProperties(typeof(CSharpOptions));

                foreach (PropertyInfo property in typeof(SpellCheckerConfiguration).GetProperties(
                             BindingFlags.Public | BindingFlags.Instance))
                {
                    propertyCache.Add(property.Name, property);
                }

                foreach (PropertyInfo property in typeof(CSharpOptions).GetProperties(
                             BindingFlags.Public | BindingFlags.Instance))
                {
                    propertyCache.Add(property.Name, property);
                }
            }
            catch
            {
                // Ignore exceptions
            }

            this.Filename      = filename;
            this.defaultConfig = defaultConfig;

            if (File.Exists(filename))
            {
                document = XDocument.Load(filename);
                root     = document.Root;

                // If it's an older configuration file, upgrade it to the new format
                if (root.Attribute("Format") == null || root.Attribute("Format").Value != AssemblyInfo.ConfigSchemaVersion)
                {
                    this.UpgradeConfiguration();
                }
            }
            else
            {
                root = new XElement("SpellCheckerConfiguration", new XAttribute("Format",
                                                                                AssemblyInfo.ConfigSchemaVersion));

                document = new XDocument(new XComment(" Visual Studio Spell Checker configuration file - " +
                                                      "[https://github.com/EWSoftware/VSSpellChecker]\r\n     Do not edit the XML.  Use the " +
                                                      "configuration file editor in Visual Studio to modify the settings. "), root);
            }
        }
        //=====================================================================
        /// <summary>
        /// This is used to get the classifier for the given file
        /// </summary>
        /// <param name="filename">The file for which to get a classifier</param>
        /// <param name="spellCheckConfiguration">The spell checker configuration that the classifier can use to
        /// determine what elements to return for spell checking if needed.</param>
        /// <returns>The classifier to use or null if the file should not be processed</returns>
        public static TextClassifier GetClassifier(string filename, SpellCheckerConfiguration spellCheckConfiguration)
        {
            ClassifierDefinition definition;
            TextClassifier classifier = null;
            string id, extension = Path.GetExtension(filename);

            if(extensionMap == null)
                LoadClassifierConfiguration();

            if(!ignoredFilePatterns.Any(p => p.IsMatch(filename)))
            {
                if(!String.IsNullOrWhiteSpace(extension))
                    extension = extension.Substring(1);

                if(!extensionMap.TryGetValue(extension, out id))
                    id = FileIsXml(filename) ? "XML" : "PlainText";

                if(id != "None" && definitions.TryGetValue(id, out definition))
                    switch(definition.ClassifierType)
                    {
                        case "PlainTextClassifier":
                            classifier = new PlainTextClassifier(filename, spellCheckConfiguration);
                            break;

                        case "XmlClassifier":
                            classifier = new XmlClassifier(filename, spellCheckConfiguration);
                            break;

                        case "ReportingServicesClassifier":
                            classifier = new ReportingServicesClassifier(filename, spellCheckConfiguration);
                            break;

                        case "ResourceFileClassifier":
                            classifier = new ResourceFileClassifier(filename, spellCheckConfiguration);
                            break;

                        case "HtmlClassifier":
                            classifier = new HtmlClassifier(filename, spellCheckConfiguration);
                            break;

                        case "CodeClassifier":
                            classifier = new CodeClassifier(filename, spellCheckConfiguration, definition.Configuration);
                            break;

                        case "RegexClassifier":
                            classifier = new RegexClassifier(filename, spellCheckConfiguration, definition.Configuration);
                            break;

                        default:
                            break;
                    }
            }

            return classifier;
        }
Example #5
0
        //=====================================================================

        /// <summary>
        /// Constructor
        /// </summary>
        /// 
        /// <param name="filename">The filename to load</param>
        /// <param name="spellCheckConfiguration">The spell checker configuration for the file</param>
        /// <param name="classifierConfiguration">The configuration element containing the classification
        /// expressions and their range types.</param>
        public RegexClassifier(string filename, SpellCheckerConfiguration spellCheckConfiguration,
          XElement classifierConfiguration) : base(filename, spellCheckConfiguration)
        {
            string expression, options;
            RangeClassification classification;
            RegexOptions regexOptions;

            expressions = new List<RegexClassification>();

            if(classifierConfiguration != null)
                foreach(XElement match in classifierConfiguration.Elements("Match"))
                {
                    expression = (string)match.Attribute("Expression");
                
                    if(!String.IsNullOrWhiteSpace(expression))
                    {
                        options = (string)match.Attribute("Options");

                        if(String.IsNullOrWhiteSpace(options) || !Enum.TryParse<RegexOptions>(options, true,
                          out regexOptions))
                            regexOptions = RegexOptions.None;

                        if(!Enum.TryParse<RangeClassification>((string)match.Attribute("Classification"), out classification))
                            classification = RangeClassification.PlainText;

                        try
                        {
                            // Enforce a 1 second timeout on all expressions.  If we can't get a match within
                            // that amount of time, ignore it.  This can happen on some files with odd formatting.
                            expressions.Add(new RegexClassification(new Regex(expression, regexOptions,
                                TimeSpan.FromSeconds(1)), classification));
                        }
                        catch(ArgumentException ex)
                        {
                            // Ignore invalid regular expression entries
                            System.Diagnostics.Debug.WriteLine(ex);
                        }
                    }
                }
        }
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="filename">The filename to load</param>
 /// <param name="spellCheckConfiguration">The spell checker configuration for the file</param>
 public PlainTextClassifier(string filename, SpellCheckerConfiguration spellCheckConfiguration) :
   base(filename, spellCheckConfiguration)
 {
 }
        //=====================================================================
        /// <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>
        /// 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>
 /// Constructor
 /// </summary>
 /// <param name="filename">The filename to load</param>
 /// <param name="spellCheckConfiguration">The spell checker configuration for the file</param>
 public ResourceFileClassifier(string filename, SpellCheckerConfiguration spellCheckConfiguration)
     : base(filename, spellCheckConfiguration)
 {
 }
        /// <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;
        }
        //=====================================================================

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="filename">The filename to load</param>
        /// <param name="spellCheckConfiguration">The spell checker configuration for the file</param>
        public ReportingServicesClassifier(string filename, SpellCheckerConfiguration spellCheckConfiguration) :
          base(filename, spellCheckConfiguration)
        {
        }
Example #12
0
        //=====================================================================

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="filename">The filename to load</param>
        /// <param name="spellCheckConfiguration">The spell checker configuration for the file</param>
        protected TextClassifier(string filename, SpellCheckerConfiguration spellCheckConfiguration)
        {
            this.Filename = filename;
            this.SpellCheckConfiguration = spellCheckConfiguration;

            using(StreamReader sr = new StreamReader(filename, Encoding.Default, true))
            {
                this.SetText(sr.ReadToEnd());
            }
        }