Exemple #1
0
        /// <summary>
        /// Check for misspellings in the given set of dirty spans
        /// </summary>
        /// <param name="dirtySpans">The enumerable list of dirty spans to check for misspellings</param>
        private void CheckSpellings(IEnumerable <SnapshotSpan> dirtySpans)
        {
            ITextSnapshot snapshot = _buffer.CurrentSnapshot;

            foreach (var dirtySpan in dirtySpans)
            {
                if (_isClosed)
                {
                    return;
                }

                var dirty = dirtySpan.TranslateTo(snapshot, SpanTrackingMode.EdgeInclusive);

                // We have to go back to the UI thread to get natural text spans
                List <SnapshotSpan> naturalTextSpans = new List <SnapshotSpan>();
                OnForegroundThread(() => naturalTextSpans = GetNaturalLanguageSpansForDirtySpan(dirty).ToList());

                var naturalText = new NormalizedSnapshotSpanCollection(
                    naturalTextSpans.Select(span => span.TranslateTo(snapshot, SpanTrackingMode.EdgeInclusive)));

                List <MisspellingTag> currentMisspellings = new List <MisspellingTag>(_misspellings);
                List <MisspellingTag> newMisspellings     = new List <MisspellingTag>();

                int removed = currentMisspellings.RemoveAll(tag => tag.ToTagSpan(snapshot).Span.OverlapsWith(dirty));

                newMisspellings.AddRange(GetMisspellingsInSpans(naturalText));

                // Also remove empties
                removed += currentMisspellings.RemoveAll(tag => tag.ToTagSpan(snapshot).Span.IsEmpty);

                // If anything has been updated, we need to send out a change event
                if (newMisspellings.Count != 0 || removed != 0)
                {
                    foreach (var g in newMisspellings.Where(
                                 w => w.MisspellingType == MisspellingType.MisspelledWord).GroupBy(w => w.Word))
                    {
                        var suggestions = _dictionary.SuggestCorrections(g.Key);

                        foreach (var m in g)
                        {
                            m.Suggestions = suggestions;
                        }
                    }

                    currentMisspellings.AddRange(newMisspellings);

                    _dispatcher.Invoke(new Action(() =>
                    {
                        if (_isClosed)
                        {
                            return;
                        }

                        _misspellings = currentMisspellings;

                        var temp = TagsChanged;

                        if (temp != null)
                        {
                            temp(this, new SnapshotSpanEventArgs(dirty));
                        }
                    }));
                }
            }

            lock (_dirtySpanLock)
            {
                if (!_isClosed)
                {
                    // Clear out any inline ignored word spans that don't exist anymore (i.e. the containing line
                    // was deleted).
                    int removed = inlineIgnoredWords.RemoveAll(s => s.Span.GetSpan(snapshot).IsEmpty);

                    // If any new inline ignored words were seen or removed, rescan the whole file
                    var newInlineIgnored = inlineIgnoredWords.Where(i => i.IsNew);

                    if (newInlineIgnored.Any() || removed != 0)
                    {
                        foreach (var i in newInlineIgnored)
                        {
                            i.IsNew = false;
                        }

                        _dirtySpans.Clear();

                        foreach (var line in snapshot.Lines)
                        {
                            if (!line.Extent.IsEmpty)
                            {
                                _dirtySpans.Add(line.Extent);
                            }
                        }
                    }

                    if (_dirtySpans.Count != 0)
                    {
                        _dispatcher.BeginInvoke(new Action(() => ScheduleUpdate()));
                    }
                }
            }
        }
Exemple #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)));
                            }
                        }
                    }
                }
            }
        }