Beispiel #1
0
        /// <summary>
        /// Get misspelled words in the given set of spans
        /// </summary>
        /// <param name="spans">The spans to check</param>
        /// <returns>An enumerable list of misspelling tags</returns>
        private IEnumerable <MisspellingTag> GetMisspellingsInSpans(NormalizedSnapshotSpanCollection spans)
        {
            List <Match>   rangeExclusions = new List <Match>();
            IList <string> spellingAlternates;
            SnapshotSpan   errorSpan, deleteWordSpan;

            Microsoft.VisualStudio.Text.Span lastWord;
            string textToSplit, actualWord, textToCheck, preferredTerm;
            int    mnemonicPos;
            var    ignoredWords = wordsIgnoredOnce;

            // **************************************************************************************************
            // NOTE: If anything changes here, update the related solution/project spell checking code in
            // ToolWindows\SolutionProjectSpellCheckControl.xaml.cs\GetMisspellingsInSpans().
            // **************************************************************************************************
            foreach (var span in spans)
            {
                textToSplit = span.GetText();

                rangeExclusions.Clear();

                // Note the location of all XML elements if needed
                if (configuration.IgnoreXmlElementsInText)
                {
                    rangeExclusions.AddRange(WordSplitter.XmlElement.Matches(textToSplit).Cast <Match>());
                }

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

                // Get any ignored words specified inline within the span
                foreach (Match m in reIgnoreSpelling.Matches(textToSplit))
                {
                    string ignored       = m.Groups["IgnoredWords"].Value;
                    bool   caseSensitive = !String.IsNullOrWhiteSpace(m.Groups["CaseSensitive"].Value);
                    int    start         = m.Groups["IgnoredWords"].Index;

                    foreach (var ignoreSpan in wordSplitter.GetWordsInText(ignored))
                    {
                        var ss = new SnapshotSpan(span.Snapshot, span.Start + start + ignoreSpan.Start,
                                                  ignoreSpan.Length);
                        var match = inlineIgnoredWords.FirstOrDefault(i => i.Span.GetSpan(span.Snapshot).IntersectsWith(ss));

                        if (match != null)
                        {
                            // If the span is already there, ignore it
                            if (match.Word == ss.GetText() && match.CaseSensitive == caseSensitive)
                            {
                                continue;
                            }

                            // If different, replace it
                            inlineIgnoredWords.Remove(match);
                        }

                        var ts = span.Snapshot.CreateTrackingSpan(ss, SpanTrackingMode.EdgeExclusive);

                        inlineIgnoredWords.Add(new InlineIgnoredWord
                        {
                            Word          = ignored.Substring(ignoreSpan.Start, ignoreSpan.Length),
                            CaseSensitive = caseSensitive,
                            Span          = ts,
                            IsNew         = true
                        });
                    }
                }

                lastWord = new Microsoft.VisualStudio.Text.Span();

                foreach (var word in wordSplitter.GetWordsInText(textToSplit))
                {
                    if (_isClosed)
                    {
                        yield break;
                    }

                    actualWord  = textToSplit.Substring(word.Start, word.Length);
                    mnemonicPos = actualWord.IndexOf(wordSplitter.Mnemonic);

                    if (mnemonicPos == -1)
                    {
                        textToCheck = actualWord;
                    }
                    else
                    {
                        textToCheck = actualWord.Substring(0, mnemonicPos) + actualWord.Substring(mnemonicPos + 1);
                    }

                    if (unescapeApostrophes && textToCheck.IndexOf("''", StringComparison.Ordinal) != -1)
                    {
                        textToCheck = textToCheck.Replace("''", "'");
                    }

                    if (inlineIgnoredWords.Any(w => w.IsMatch(textToCheck)))
                    {
                        continue;
                    }

                    // 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 SnapshotSpan(span.Start + word.Start, word.Length);

                        // Check for a doubled word.  This isn't perfect as it won't detected doubled words
                        // across a line break.
                        if (configuration.DetectDoubledWords && lastWord.Length != 0 &&
                            textToSplit.Substring(lastWord.Start, lastWord.Length).Equals(actualWord,
                                                                                          StringComparison.OrdinalIgnoreCase) && String.IsNullOrWhiteSpace(textToSplit.Substring(
                                                                                                                                                               lastWord.Start + lastWord.Length, word.Start - lastWord.Start - lastWord.Length)))
                        {
                            // If the doubled word is not being ignored at the current location, return it
                            if (!ignoredWords.Any(w => w.StartPoint == errorSpan.Start && w.Word.Equals(actualWord,
                                                                                                        StringComparison.OrdinalIgnoreCase)))
                            {
                                // Delete the whitespace ahead of it too
                                deleteWordSpan = new SnapshotSpan(span.Start + lastWord.Start + lastWord.Length,
                                                                  word.Length + word.Start - lastWord.Start - lastWord.Length);

                                yield return(new MisspellingTag(errorSpan, deleteWordSpan));

                                lastWord = word;
                                continue;
                            }
                        }

                        lastWord = word;

                        // If the word is not being ignored, perform the other checks
                        if (!_dictionary.ShouldIgnoreWord(textToCheck) && !ignoredWords.Any(
                                w => w.StartPoint == errorSpan.Start && w.Word.Equals(actualWord,
                                                                                      StringComparison.OrdinalIgnoreCase)))
                        {
                            // Handle code analysis dictionary checks first as they may be not be recognized as
                            // correctly spelled words but have alternate handling.
                            if (configuration.CadOptions.TreatDeprecatedTermsAsMisspelled &&
                                configuration.DeprecatedTerms.TryGetValue(textToCheck, out preferredTerm))
                            {
                                yield return(new MisspellingTag(MisspellingType.DeprecatedTerm, errorSpan,
                                                                new[] { new SpellingSuggestion(null, preferredTerm) }));

                                continue;
                            }

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

                                continue;
                            }

                            if (configuration.CadOptions.TreatUnrecognizedWordsAsMisspelled &&
                                configuration.UnrecognizedWords.TryGetValue(textToCheck, out spellingAlternates))
                            {
                                yield return(new MisspellingTag(MisspellingType.UnrecognizedWord, errorSpan,
                                                                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.EndsWith("\u2019s", StringComparison.OrdinalIgnoreCase))
                                {
                                    string aposEss = textToCheck.Substring(textToCheck.Length - 2);

                                    textToCheck = textToCheck.Substring(0, textToCheck.Length - 2);

                                    if (_dictionary.ShouldIgnoreWord(textToCheck) ||
                                        _dictionary.IsSpelledCorrectly(textToCheck))
                                    {
                                        continue;
                                    }

                                    textToCheck += aposEss;
                                }

                                // 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 MisspellingTag(errorSpan)
                                {
                                    EscapeApostrophes = unescapeApostrophes
                                });
                            }
                        }
                    }
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Get misspelled words in the given set of spans
        /// </summary>
        /// <param name="spans">The spans to check</param>
        /// <returns>An enumerable list of misspelling tags</returns>
        private IEnumerable <MisspellingTag> GetMisspellingsInSpans(NormalizedSnapshotSpanCollection spans)
        {
            List <Match> xmlTags = null;
            SnapshotSpan errorSpan, deleteWordSpan;

            Microsoft.VisualStudio.Text.Span lastWord;
            string text, textToParse;
            var    ignoredWords = wordsIgnoredOnce;

            foreach (var span in spans)
            {
                text = span.GetText();

                // Note the location of all XML elements if needed
                if (configuration.IgnoreXmlElementsInText)
                {
                    xmlTags = reXml.Matches(text).OfType <Match>().ToList();
                }

                lastWord = new Microsoft.VisualStudio.Text.Span();

                foreach (var word in GetWordsInText(text))
                {
                    textToParse = text.Substring(word.Start, word.Length);

                    // Spell check the word if it looks like one and is not ignored
                    if (IsProbablyARealWord(textToParse) && (xmlTags == null || xmlTags.Count == 0 ||
                                                             !xmlTags.Any(match => word.Start >= match.Index &&
                                                                          word.Start <= match.Index + match.Length - 1)))
                    {
                        // Check for a doubled word.  This isn't perfect as it won't detected doubled words
                        // across a line break.
                        if (lastWord.Length != 0 && text.Substring(lastWord.Start, lastWord.Length).Equals(
                                textToParse, StringComparison.OrdinalIgnoreCase) && String.IsNullOrWhiteSpace(
                                text.Substring(lastWord.Start + lastWord.Length, word.Start - lastWord.Start - lastWord.Length)))
                        {
                            errorSpan = new SnapshotSpan(span.Start + word.Start, word.Length);

                            // If the doubled word is not being ignored at the current location, return it
                            if (!ignoredWords.Any(w => w.StartPoint == errorSpan.Start && w.Word.Equals(textToParse,
                                                                                                        StringComparison.OrdinalIgnoreCase)))
                            {
                                // Delete the whitespace ahead of it too
                                deleteWordSpan = new SnapshotSpan(span.Start + lastWord.Start + lastWord.Length,
                                                                  word.Length + word.Start - lastWord.Start - lastWord.Length);

                                yield return(new MisspellingTag(errorSpan, deleteWordSpan));

                                lastWord = word;
                                continue;
                            }
                        }

                        lastWord = word;

                        if (!_dictionary.ShouldIgnoreWord(textToParse) && !_dictionary.IsSpelledCorrectly(textToParse))
                        {
                            // 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 (textToParse.EndsWith("'s", StringComparison.OrdinalIgnoreCase))
                            {
                                textToParse = textToParse.Substring(0, textToParse.Length - 2);

                                if (_dictionary.ShouldIgnoreWord(textToParse) ||
                                    _dictionary.IsSpelledCorrectly(textToParse))
                                {
                                    continue;
                                }

                                textToParse += "'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 < text.Length && text[word.Start + word.Length] == '.')
                            {
                                if (_dictionary.ShouldIgnoreWord(textToParse + ".") ||
                                    _dictionary.IsSpelledCorrectly(textToParse + "."))
                                {
                                    continue;
                                }
                            }

                            errorSpan = new SnapshotSpan(span.Start + word.Start, word.Length);

                            // If the word is not being ignored at the current location, return it and its
                            // suggested corrections.
                            if (!ignoredWords.Any(w => w.StartPoint == errorSpan.Start && w.Word.Equals(textToParse,
                                                                                                        StringComparison.OrdinalIgnoreCase)))
                            {
                                yield return(new MisspellingTag(errorSpan, _dictionary.SuggestCorrections(textToParse)));
                            }
                        }
                    }
                }
            }
        }