/// <summary>
        /// This creates an instance of the WPF spell checker for the given text box
        /// </summary>
        /// <param name="textBox">The text box to initialize</param>
        private static void AddWpfSpellChecker(TextBox textBox)
        {
            // Don't do anything if the default spell checker is enabled or it's read-only
            if (!textBox.SpellCheck.IsEnabled && !textBox.IsReadOnly)
            {
                lock (syncRoot)
                {
                    // Create the shared configuration and dictionary on first use
                    if (configuration == null)
                    {
                        configuration = new SpellCheckerConfiguration();
                        configuration.Load(SpellingConfigurationFile.GlobalConfigurationFilename);

                        var globalDictionaries = configuration.DictionaryLanguages.Select(l =>
                                                                                          GlobalDictionary.CreateGlobalDictionary(l, configuration.AdditionalDictionaryFolders,
                                                                                                                                  configuration.RecognizedWords)).Where(d => d != null).Distinct().ToList();

                        dictionary = new SpellingDictionary(globalDictionaries, configuration.IgnoredWords);
                    }

                    // Ignore it if disabled or it's an excluded text box
                    string name = ElementName(textBox);

                    if (!configuration.EnableWpfTextBoxSpellChecking || configuration.VisualStudioExclusions.Any(
                            v => v.IsMatch(name)))
                    {
                        return;
                    }
                }

                var wsc = new WpfTextBoxSpellChecker(textBox);

                wpfSpellCheckers.AddOrUpdate(textBox, wsc, (k, v) => wsc);
            }
        }
Beispiel #2
0
        //=====================================================================

        /// <summary>
        /// Constructor for SpellDictionarySmartTagAction.
        /// </summary>
        /// <param name="word">The word to add or ignore.</param>
        /// <param name="dictionary">The dictionary used to ignore the word or add the word.</param>
        /// <param name="displayText">Text to show in the context menu for this action.</param>
        /// <param name="action">The action to take.</param>
        public SpellDictionarySmartTagAction(ITrackingSpan span, SpellingDictionary dictionary,
                                             string displayText, DictionaryAction action)
        {
            this.span        = span;
            this.dictionary  = dictionary;
            this.action      = action;
            this.DisplayText = displayText;
        }
Beispiel #3
0
 /// <summary>Constructor for multi-language spelling suggestions smart tag actions.</summary>
 /// <param name="trackingSpan">The tracking span.</param>
 /// <param name="replaceWith">The suggestion to replace misspelled word with</param>
 /// <param name="cultures">The cultures from which the suggested word was chosen.</param>
 /// <param name="dictionary">The dictionary used to perform the Replace All action</param>
 public MultiLanguageSpellSmartTagAction(ITrackingSpan trackingSpan,
                                         SpellingSuggestion replaceWith,
                                         IEnumerable <CultureInfo> cultures,
                                         SpellingDictionary dictionary)
     : base(trackingSpan, replaceWith, dictionary)
 {
     this.cultures    = cultures.ToArray();
     this.displayText = null;
 }
 /// <summary>Constructor for multi-language spelling suggestions smart tag actions.</summary>
 /// <param name="trackingSpan">The tracking span.</param>
 /// <param name="replaceWith">The suggestion to replace misspelled word with</param>
 /// <param name="cultures">The cultures from which the suggested word was chosen.</param>
 /// <param name="dictionary">The dictionary used to perform the Replace All action</param>
 public MultiLanguageSpellSmartTagAction(ITrackingSpan trackingSpan,
                                         SpellingSuggestion replaceWith,
                                         IEnumerable<CultureInfo> cultures,
                                         SpellingDictionary dictionary)
     : base(trackingSpan, replaceWith, dictionary)
 {
     this.cultures = cultures.ToArray();
     this.displayText = null;
 }
Beispiel #5
0
        /// <summary>
        /// This is called to clear the textbox cache
        /// </summary>
        /// <remarks>This is done whenever a change in solution is detected.  Dictionaries are cleared when that
        /// occurs so this will allow the text boxes to be reconnected with a valid dictionary afterwards.  The
        /// text box instances tend to accumulate so it's also a good time to clear them all out.</remarks>
        public static void ClearCache()
        {
            lock (syncRoot)
            {
                configuration = null;
                dictionary    = null;
            }

            foreach (var wsc in wpfSpellCheckers.Values.ToArray())
            {
                wsc.Disconnect();
            }
        }
Beispiel #6
0
        //=====================================================================

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="dictionary">The spelling dictionary</param>
        /// <param name="misspellingAggregator">The misspelling aggregator</param>
        public SpellSuggestedActionSource(SpellingDictionary dictionary,
                                          ITagAggregator <MisspellingTag> misspellingAggregator)
        {
            this.dictionary            = dictionary;
            this.misspellingAggregator = misspellingAggregator;

            this.misspellingAggregator.TagsChanged += (sender, args) =>
            {
                if (!this.disposed)
                {
                    this.SuggestedActionsChanged?.Invoke(this, EventArgs.Empty);
                }
            };
        }
        //=====================================================================

        /// <summary>
        /// Constructor for SpellDictionarySmartTagAction.
        /// </summary>
        /// <param name="word">The word to add or ignore.</param>
        /// <param name="dictionary">The dictionary used to ignore the word or add the word.</param>
        /// <param name="displayText">Text to show in the context menu for this action.</param>
        /// <param name="action">The action to take.</param>
        /// <param name="culture">The culture of the dictionary on which to perform the action or null if it
        /// does not apply.</param>
        public SpellDictionarySmartTagAction(ITrackingSpan span, SpellingDictionary dictionary,
          string displayText, DictionaryAction action, CultureInfo culture)
        {
            this.span = span;
            this.dictionary = dictionary;
            this.action = action;
            this.culture = culture;

            if(culture == null)
                this.DisplayText = displayText;
            else
                this.DisplayText = String.Format(CultureInfo.InvariantCulture, "{0} - {1} ({2})", displayText,
                    culture.EnglishName, culture.Name);
        }
Beispiel #8
0
        //=====================================================================

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="buffer">The text buffer</param>
        /// <param name="dictionary">The spelling dictionary</param>
        /// <param name="misspellingAggregator">The misspelling aggregator</param>
        public SpellSmartTagger(ITextBuffer buffer, SpellingDictionary dictionary,
                                ITagAggregator <MisspellingTag> misspellingAggregator)
        {
            _buffer                = buffer;
            _dictionary            = dictionary;
            _misspellingAggregator = misspellingAggregator;

            _misspellingAggregator.TagsChanged += (sender, args) =>
            {
                foreach (var span in args.Span.GetSpans(_buffer))
                {
                    RaiseTagsChangedEvent(span);
                }
            };
        }
Beispiel #9
0
        //=====================================================================

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="span">The word span to replace</param>
        /// <param name="replaceWith">The suggestion to replace the misspelled word</param>
        /// <param name="dictionary">The dictionary used to perform the Replace All action</param>
        public SpellSuggestedAction(ITrackingSpan span, ISpellingSuggestion replaceWith,
                                    SpellingDictionary dictionary) : base(replaceWith.Suggestion.Replace("_", "__"), span)
        {
            this.replaceWith = replaceWith;
            this.dictionary  = dictionary;

            // The preview is used to remind users that they can hold Ctrl when selecting this suggestion to
            // replace all instances of the word.
            this.Preview = new TextBlock(new Run {
                Text = "Hold Ctrl to replace all"
            })
            {
                Padding = new Thickness(5)
            };
        }
        //=====================================================================

        /// <summary>
        /// Constructor for SpellDictionarySmartTagAction.
        /// </summary>
        /// <param name="word">The word to add or ignore.</param>
        /// <param name="dictionary">The dictionary used to ignore the word or add the word.</param>
        /// <param name="displayText">Text to show in the context menu for this action.</param>
        /// <param name="action">The action to take.</param>
        /// <param name="culture">The culture of the dictionary on which to perform the action or null if it
        /// does not apply.</param>
        public SpellDictionarySmartTagAction(ITrackingSpan span, SpellingDictionary dictionary,
                                             string displayText, DictionaryAction action, CultureInfo culture)
        {
            this.span       = span;
            this.dictionary = dictionary;
            this.action     = action;
            this.culture    = culture;

            if (culture == null)
            {
                this.DisplayText = displayText;
            }
            else
            {
                this.DisplayText = String.Format(CultureInfo.InvariantCulture, "{0} - {1} ({2})", displayText,
                                                 culture.EnglishName, culture.Name);
            }
        }
        //=====================================================================

        /// <summary>
        /// Constructor for SpellDictionarySmartTagAction.
        /// </summary>
        /// <param name="span">The span containing the word to add or ignore.</param>
        /// <param name="dictionary">The dictionary used to ignore the word or add the word.</param>
        /// <param name="displayText">The display text for the suggested action.</param>
        /// <param name="action">The dictionary action to take.</param>
        /// <param name="culture">The culture of the dictionary on which to perform the action or null if it
        /// does not apply.</param>
        public SpellDictionarySuggestedAction(ITrackingSpan span, SpellingDictionary dictionary,
                                              string displayText, DictionaryAction action, CultureInfo culture) : base(displayText, span)
        {
            this.dictionary = dictionary;
            this.action     = action;
            this.culture    = culture;

            if (culture != null)
            {
                this.DisplayTextSuffix = $"{culture.EnglishName} ({culture.Name})";
            }

            if (String.IsNullOrEmpty(this.DisplayText))
            {
                this.DisplayText = !String.IsNullOrWhiteSpace(this.DisplayTextSuffix) ? this.DisplayTextSuffix :
                                   "Add to Dictionary";
                this.DisplayTextSuffix = null;
            }
        }
Beispiel #12
0
        //=====================================================================

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="span">The span containing the word to ignore.</param>
        /// <param name="dictionary">The dictionary used to ignore the word or add the word.</param>
        /// <param name="configurationType">The configuration file type associated with the ignored words file.</param>
        /// <param name="ignoredWordsFile">The ignored words file to which the word is added</param>
        /// <param name="displayText">The display text for the suggested action.</param>
        public IgnoredWordsSuggestedAction(ITrackingSpan span, SpellingDictionary dictionary,
                                           ConfigurationType configurationType, string ignoredWordsFile, string displayText) : base(displayText, span)
        {
            this.dictionary       = dictionary;
            this.ignoredWordsFile = ignoredWordsFile;

            if (configurationType == ConfigurationType.Global)
            {
                this.DisplayTextSuffix = "Global";
            }
            else
            {
                this.DisplayTextSuffix = $"{configurationType} ({Path.GetFileName(ignoredWordsFile)})";
            }

            if (String.IsNullOrWhiteSpace(this.DisplayText))
            {
                this.DisplayText       = this.DisplayTextSuffix;
                this.DisplayTextSuffix = null;
            }
        }
        //=====================================================================

        /// <summary>
        /// Constructor for spelling suggestions smart tag actions
        /// </summary>
        /// <param name="span">The word span to replace</param>
        /// <param name="replaceWith">The suggestion to replace misspelled word with</param>
        /// <param name="dictionary">The dictionary used to perform the Replace All action</param>
        public SpellSmartTagAction(ITrackingSpan span, ISpellingSuggestion replaceWith, SpellingDictionary dictionary)
        {
            this.span = span;
            this.replaceWith = replaceWith;
            this.dictionary = dictionary;
        }
        //=====================================================================

        /// <summary>
        /// Constructor for spelling suggestions smart tag actions
        /// </summary>
        /// <param name="span">The word span to replace</param>
        /// <param name="replaceWith">The suggestion to replace misspelled word with</param>
        /// <param name="dictionary">The dictionary used to perform the Replace All action</param>
        public SpellSmartTagAction(ITrackingSpan span, ISpellingSuggestion replaceWith, SpellingDictionary dictionary)
        {
            this.span        = span;
            this.replaceWith = replaceWith;
            this.dictionary  = dictionary;
        }
Beispiel #15
0
        //=====================================================================

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="trackingSpan">The tracking span</param>
        /// <param name="replaceWith">The suggestion to replace the misspelled word</param>
        /// <param name="escapeApostrophes">True to escape apostrophes in the suggestion, false if not</param>
        /// <param name="cultures">The cultures from which the suggested word was chosen</param>
        /// <param name="dictionary">The dictionary used to perform the Replace All action</param>
        public MultiLanguageSpellSuggestedAction(ITrackingSpan trackingSpan, ISpellingSuggestion replaceWith,
                                                 bool escapeApostrophes, IEnumerable <CultureInfo> cultures, SpellingDictionary dictionary) :
            base(trackingSpan, replaceWith, escapeApostrophes, dictionary)
        {
            this.DisplayTextSuffix = String.Join(" | ", cultures.Select(c => c.Name));
        }
        /// <summary>
        /// Get misspelled words in the given set of spans
        /// </summary>
        /// <param name="dictionary">The dictionary to use for checking words</param>
        /// <param name="spans">An enumerable list of spans to check</param>
        /// <returns>An enumerable list of misspelling issues</returns>
        private IEnumerable<FileMisspelling> GetMisspellingsInSpans(SpellingDictionary dictionary,
          IEnumerable<SpellCheckSpan> spans)
        {
            List<Match> rangeExclusions = null;
            IList<string> spellingAlternates;
            Span errorSpan, deleteWordSpan, lastWord;
            string textToSplit, textToCheck, preferredTerm;

            // **************************************************************************************************
            // NOTE: If anything changes here, update the related tagger spell checking code in
            // SpellingTagger.cs\GetMisspellingsInSpans().
            // **************************************************************************************************
            foreach(var span in spans)
            {
                cancellationTokenSource.Token.ThrowIfCancellationRequested();

                textToSplit = span.Text;

                // Always ignore URLs
                rangeExclusions = WordSplitter.Url.Matches(textToSplit).OfType<Match>().ToList();

                // Note the location of all XML elements if needed
                if(wordSplitter.Configuration.IgnoreXmlElementsInText)
                    rangeExclusions.AddRange(WordSplitter.XmlElement.Matches(textToSplit).OfType<Match>());

                // Add exclusions from the configuration if any
                foreach(var exclude in wordSplitter.Configuration.ExclusionExpressions)
                    try
                    {
                        rangeExclusions.AddRange(exclude.Matches(textToSplit).OfType<Match>());
                    }
                    catch(RegexMatchTimeoutException ex)
                    {
                        // Ignore expression timeouts
                        System.Diagnostics.Debug.WriteLine(ex);
                    }

                lastWord = new Span();

                wordSplitter.Classification = span.Classification;

                foreach(var word in wordSplitter.GetWordsInText(textToSplit))
                {
                    textToCheck = textToSplit.Substring(word.Start, word.Length);

                    // Spell check the word if it looks like one and is not ignored
                    if(wordSplitter.IsProbablyARealWord(textToCheck) && (rangeExclusions.Count == 0 ||
                      !rangeExclusions.Any(match => word.Start >= match.Index &&
                      word.Start <= match.Index + match.Length - 1)))
                    {
                        errorSpan = new Span(span.Span.Start + word.Start, word.Length);

                        // Check for a doubled word
                        if(wordSplitter.Configuration.DetectDoubledWords && lastWord.Length != 0 &&
                          textToSplit.Substring(lastWord.Start, lastWord.Length).Equals(textToCheck,
                          StringComparison.OrdinalIgnoreCase) && String.IsNullOrWhiteSpace(textToSplit.Substring(
                          lastWord.Start + lastWord.Length, word.Start - lastWord.Start - lastWord.Length)))
                        {
                            // Delete the whitespace ahead of it too
                            deleteWordSpan = new Span(span.Span.Start + lastWord.Start + lastWord.Length,
                                word.Length + word.Start - lastWord.Start - lastWord.Length);

                            yield return new FileMisspelling(errorSpan, deleteWordSpan, textToCheck);

                            lastWord = word;
                            continue;
                        }

                        lastWord = word;

                        // If the word is not being ignored, perform the other checks
                        if(!dictionary.ShouldIgnoreWord(textToCheck))
                        {
                            // Handle code analysis dictionary checks first as they may be not be recognized as
                            // correctly spelled words but have alternate handling.
                            if(wordSplitter.Configuration.CadOptions.TreatDeprecatedTermsAsMisspelled &&
                              wordSplitter.Configuration.DeprecatedTerms.TryGetValue(textToCheck, out preferredTerm))
                            {
                                yield return new FileMisspelling(MisspellingType.DeprecatedTerm, errorSpan,
                                    textToCheck, new[] { new SpellingSuggestion(null, preferredTerm) });
                                continue;
                            }

                            if(wordSplitter.Configuration.CadOptions.TreatCompoundTermsAsMisspelled &&
                              wordSplitter.Configuration.CompoundTerms.TryGetValue(textToCheck, out preferredTerm))
                            {
                                yield return new FileMisspelling(MisspellingType.CompoundTerm, errorSpan,
                                    textToCheck, new[] { new SpellingSuggestion(null, preferredTerm) });
                                continue;
                            }

                            if(wordSplitter.Configuration.CadOptions.TreatUnrecognizedWordsAsMisspelled &&
                              wordSplitter.Configuration.UnrecognizedWords.TryGetValue(textToCheck, out spellingAlternates))
                            {
                                yield return new FileMisspelling(MisspellingType.UnrecognizedWord, errorSpan,
                                    textToCheck, spellingAlternates.Select(a => new SpellingSuggestion(null, a)));
                                continue;
                            }

                            if(!dictionary.IsSpelledCorrectly(textToCheck))
                            {
                                // Sometimes it flags a word as misspelled if it ends with "'s".  Try checking the
                                // word without the "'s".  If ignored or correct without it, don't flag it.  This
                                // appears to be caused by the definitions in the dictionary rather than Hunspell.
                                if(textToCheck.EndsWith("'s", StringComparison.OrdinalIgnoreCase))
                                {
                                    textToCheck = textToCheck.Substring(0, textToCheck.Length - 2);

                                    if(dictionary.ShouldIgnoreWord(textToCheck) ||
                                      dictionary.IsSpelledCorrectly(textToCheck))
                                        continue;

                                    textToCheck += "'s";
                                }

                                // Some dictionaries include a trailing period on certain words such as "etc." which
                                // we don't include.  If the word is followed by a period, try it with the period to
                                // see if we get a match.  If so, consider it valid.
                                if(word.Start + word.Length < textToSplit.Length && textToSplit[word.Start + word.Length] == '.')
                                {
                                    if(dictionary.ShouldIgnoreWord(textToCheck + ".") ||
                                      dictionary.IsSpelledCorrectly(textToCheck + "."))
                                        continue;
                                }

                                yield return new FileMisspelling(errorSpan, textToCheck);
                            }
                        }
                    }
                }
            }
        }
        //=====================================================================

        /// <summary>
        /// This is used to handle spell checking the given set of files in the background
        /// </summary>
        /// <param name="maxIssues">The maximum number of issues to report.</param>
        /// <param name="spellCheckFiles">The files to spell check.</param>
        /// <param name="codeAnalysisFiles">The code analysis dictionaries from each project that may be used in
        /// the configurations used for spell checking.</param>
        /// <param name="openDocuments">A list of documents open in the IDE.  For these files, we'll get the
        /// content from the editor if possible rather than the file on disk.</param>
        private IEnumerable<FileMisspelling> SpellCheckFiles(int maxIssues,
          IEnumerable<SpellCheckFileInfo> spellCheckFiles, Dictionary<string, List<string>> codeAnalysisFiles,
          HashSet<string> openDocuments)
        {
            BindingList<FileMisspelling> issues = new BindingList<FileMisspelling>();
            TextClassifier classifier;
            List<string> cadFiles;
            string documentText;

            try
            {
                if(wordSplitter == null)
                    wordSplitter = new WordSplitter();

                foreach(var file in spellCheckFiles)
                {
                    cancellationTokenSource.Token.ThrowIfCancellationRequested();

                    // Get the code analysis files for the related project.  These may be used to generate the
                    // spell checking configuration.
                    if(!codeAnalysisFiles.TryGetValue(file.ProjectFile, out cadFiles))
                        cadFiles = null;

                    wordSplitter.Configuration = file.GenerateConfiguration(cadFiles);

                    if(wordSplitter.Configuration != 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 = wordSplitter.Configuration.DictionaryLanguages.Select(l =>
                            GlobalDictionary.CreateGlobalDictionary(l, null,
                            wordSplitter.Configuration.AdditionalDictionaryFolders,
                            wordSplitter.Configuration.RecognizedWords)).Where(d => d != null).Distinct().ToList();

                        if(globalDictionaries.Any())
                        {
                            var dictionary = new SpellingDictionary(globalDictionaries,
                                wordSplitter.Configuration.IgnoredWords);

                            classifier = ClassifierFactory.GetClassifier(file.CanonicalName, wordSplitter.Configuration);

                            // If null, the file type is ignored
                            if(classifier != null)
                            {
                                // If open in an editor, use the current text from it if possible
                                if(openDocuments.Contains(file.CanonicalName))
                                {
                                    documentText = GetDocumentText(file.CanonicalName);

                                    if(documentText != null)
                                        classifier.SetText(documentText);
                                }

                                // Switch to the UI thread to update the progress and then switch back to this one
                                ThreadHelper.Generic.Invoke(() =>
                                {
                                    lblProgress.Text = "Spell checking " + file.Description;
                                });

                                foreach(var issue in this.GetMisspellingsInSpans(dictionary, classifier.Parse()))
                                {
                                    issue.Dictionary = dictionary;
                                    issue.ProjectName = Path.GetFileName(file.ProjectFile);
                                    issue.Filename = file.Filename;
                                    issue.CanonicalName = file.CanonicalName;
                                    issue.LineNumber = classifier.GetLineNumber(issue.Span.Start);
                                    issue.LineText = classifier[issue.LineNumber].Trim();

                                    issues.Add(issue);

                                    if(issues.Count >= maxIssues)
                                        break;
                                }
                            }
                        }
                    }

                    if(issues.Count >= maxIssues)
                        break;
                }
            }
            catch(OperationCanceledException)
            {
                System.Diagnostics.Debug.WriteLine("Spell checking process canceled");
            }
            finally
            {
                wordSplitter.Configuration = null;
            }

            return issues;
        }