/// <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 (SpellCheckerConfiguration.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))); } } } } } }