Example #1
0
        private ScanStatus ScanRange(ITextPointer start, ITextPointer end, long timeLimit) 
        {
            ITextPointer contextStart;
            ITextPointer contextEnd;
            ITextPointer contentStart; 
            ITextPointer contentEnd;
            TextMap textMap; 
            ScanStatus status; 

            // 
            // IMPORTANT: the scan logic here (word break expansion, TextMap creation, etc.)
            // must match GetSuggestionForError exactly.  Keep the methods in [....]!
            //
 
            //
            // Expand the content to include whole words. 
            // Also get pointers to sufficient surrounding text to analyze 
            // multi-word errors correctly.
            // 

            status = new ScanStatus(timeLimit);

            XmlLanguage language; 
            CultureInfo culture = GetCurrentCultureAndLanguage(start, out language);
 
            if (culture == null) 
            {
                // Someone set a bogus language on the run -- ignore it. 
                _statusTable.MarkCleanRange(start, end);
            }
            else
            { 
                SetCulture(culture);
 
                ExpandToWordBreakAndContext(start, LogicalDirection.Backward, language, out contentStart, out contextStart); 
                ExpandToWordBreakAndContext(end, LogicalDirection.Forward, language, out contentEnd, out contextEnd);
 
                Invariant.Assert(contentStart.CompareTo(contentEnd) < 0);
                Invariant.Assert(contextStart.CompareTo(contextEnd) < 0);
                Invariant.Assert(contentStart.CompareTo(contextStart) >= 0);
                Invariant.Assert(contentEnd.CompareTo(contextEnd) <= 0); 

                // 
                // Mark the range clean, before we scan for errors. 
                //
                _statusTable.MarkCleanRange(contentStart, contentEnd); 

                //
                // Read the text.
                // 

                // Check for a compatible language. 
                if (CanSpellCheck(culture)) 
                {
                    _spellerInterop.SetContextOption("IsSpellChecking", true); 
                    _spellerInterop.SetContextOption("IsSpellVerifyOnly", true);

                    textMap = new TextMap(contextStart, contextEnd, contentStart, contentEnd);
 
                    //
                    // Iterate over sentences and segments. 
                    // 

                    _spellerInterop.EnumTextSegments(textMap.Text, textMap.TextLength, new SpellerInterop.EnumSentencesCallback(ScanRangeCheckTimeLimitCallback), 
                        new SpellerInterop.EnumTextSegmentsCallback(ScanTextSegment), new TextMapCallbackData(textMap, status));

                    if (status.TimeoutPosition != null)
                    { 
                        if (status.TimeoutPosition.CompareTo(end) < 0)
                        { 
                            // We ran out of time before analyzing the whole block. 
                            // Reset the dirty status of the remainder.
                            _statusTable.MarkDirtyRange(status.TimeoutPosition, end); 
                            // We should always make some forward progress, even just one word,
                            // otherwise we'll never finish checking the document.
                            if (status.TimeoutPosition.CompareTo(start) <= 0)
                            { 
                                // Diagnostic info for bug 1577085.
                                string debugMessage = "Speller is not advancing! \n" + 
                                                      "Culture = " + culture + "\n" + 
                                                      "Start offset = " + start.Offset + " parent = " + start.ParentType.Name + "\n" +
                                                      "ContextStart offset = " + contextStart.Offset + " parent = " + contextStart.ParentType.Name + "\n" + 
                                                      "ContentStart offset = " + contentStart.Offset + " parent = " + contentStart.ParentType.Name + "\n" +
                                                      "ContentEnd offset = " + contentEnd.Offset + " parent = " + contentEnd.ParentType.Name + "\n" +
                                                      "ContextEnd offset = " + contextEnd.Offset + " parent = " + contextEnd.ParentType.Name + "\n" +
                                                      "Timeout offset = " + status.TimeoutPosition.Offset + " parent = " + status.TimeoutPosition.ParentType.Name + "\n" + 
                                                      "textMap TextLength = " + textMap.TextLength + " text = " + new string(textMap.Text) + "\n" +
                                                      "Document = " + start.TextContainer.Parent.GetType().Name + "\n"; 
 
                                if (start is TextPointer)
                                { 
                                    debugMessage += "Xml = " + new TextRange((TextPointer)start.TextContainer.Start, (TextPointer)start.TextContainer.End).Xml;
                                }

                                Invariant.Assert(false, debugMessage); 
                            }
                        } 
                        else 
                        {
                            // We ran of time but finished the whole block. 
                            // TimeoutPosition should never be past contentEnd.
                            // It might be less than contentEnd if the dirty run ends
                            // with an element edge, in which case TimeoutPosition
                            // will preceed the final element edge(s). 
                            Invariant.Assert(status.TimeoutPosition.CompareTo(contentEnd) <= 0);
                        } 
                    } 
                }
            } 

            return status;
        }
Example #2
0
 internal TextMapCallbackData(TextMap textmap, object data)
 { 
     _textmap = textmap;
     _data = data; 
 } 
Example #3
0
        private void AdjustScanRangeAroundComposition(ITextPointer rawStart, ITextPointer rawEnd,
            out ITextPointer start, out ITextPointer end) 
        {
            start = rawStart;
            end = rawEnd;
 
            if (!_textEditor.Selection.IsEmpty)
            { 
                // No caret to adjust around. 
                return;
            } 

            if (!_textEditor.UiScope.IsKeyboardFocused)
            {
                // Document isn't focused, no caret rendered. 
                return;
            } 
 
            // Get the word surrounding the caret.
 
            ITextPointer wordBreakLeft;
            ITextPointer wordBreakRight;
            ITextPointer caretPosition;
            TextMap textMap; 
            ArrayList segments;
 
            caretPosition = _textEditor.Selection.Start; 

            // Disable spell checking functionality since we're only 
            // interested in word breaks here.  This greatly cuts down
            // the engine's workload.
            _spellerInterop.SetContextOption("IsSpellChecking", false);
 
            XmlLanguage language = GetCurrentLanguage(caretPosition);
            wordBreakLeft = SearchForWordBreaks(caretPosition, LogicalDirection.Backward, language, 1, false /* stopOnError */); 
            wordBreakRight = SearchForWordBreaks(caretPosition, LogicalDirection.Forward, language, 1, false /* stopOnError */); 

            textMap = new TextMap(wordBreakLeft, wordBreakRight, caretPosition, caretPosition); 
            segments = new ArrayList(2);
            _spellerInterop.EnumTextSegments(textMap.Text, textMap.TextLength, null,
                new SpellerInterop.EnumTextSegmentsCallback(ExpandToWordBreakCallback), segments);
 
            // We will have no segments when position is surrounded by
            // nothing but white space. 
            if (segments.Count != 0) 
            {
                int leftBreakOffset; 
                int rightBreakOffset;

                // Figure out where caretPosition lives in the segment list.
                FindPositionInSegmentList(textMap, LogicalDirection.Backward, segments, out leftBreakOffset, out rightBreakOffset); 

                wordBreakLeft = textMap.MapOffsetToPosition(leftBreakOffset); 
                wordBreakRight = textMap.MapOffsetToPosition(rightBreakOffset); 
            }
 
            // Overlap?
            if (wordBreakLeft.CompareTo(rawEnd) < 0 &&
                wordBreakRight.CompareTo(rawStart) > 0)
            { 
                if (wordBreakLeft.CompareTo(rawStart) > 0)
                { 
                    // Truncate the right half of the input range. 
                    end = wordBreakLeft;
                } 
                else if (wordBreakRight.CompareTo(rawEnd) < 0)
                {
                    // Truncate the left half of the input range.
                    start = wordBreakRight; 
                }
                else 
                { 
                    // The entire dirty range is covered by the caret word.
                    // Try to find a following dirty range. 
                    GetNextScanRangeRaw(wordBreakRight, out start, out end);
                }

                // Schedule a future callback to deal with the skipped 
                // overlapping section.
                ScheduleCaretMovedCallback(); 
            } 
        }
Example #4
0
        internal IList GetSuggestionsForError(SpellingError error) 
        { 
            ITextPointer contextStart;
            ITextPointer contextEnd; 
            ITextPointer contentStart;
            ITextPointer contentEnd;
            TextMap textMap;
            ArrayList suggestions; 

            suggestions = new ArrayList(1); 
 
            //
            // IMPORTANT!! 
            //
            // This logic here must match ScanRange, or else we might not
            // calculate the exact same error.  Keep the two methods in [....]!
            // 

            XmlLanguage language; 
            CultureInfo culture = GetCurrentCultureAndLanguage(error.Start, out language); 
            if (culture == null || !CanSpellCheck(culture))
            { 
                // Return an empty list.
            }
            else
            { 
                ExpandToWordBreakAndContext(error.Start, LogicalDirection.Backward, language, out contentStart, out contextStart);
                ExpandToWordBreakAndContext(error.End, LogicalDirection.Forward, language, out contentEnd, out contextEnd); 
 
                textMap = new TextMap(contextStart, contextEnd, contentStart, contentEnd);
 
                SetCulture(culture);

                _spellerInterop.SetContextOption("IsSpellChecking", true);
                _spellerInterop.SetContextOption("IsSpellVerifyOnly", false); 

                _spellerInterop.EnumTextSegments(textMap.Text, textMap.TextLength, null, 
                    new SpellerInterop.EnumTextSegmentsCallback(ScanErrorTextSegment), new TextMapCallbackData(textMap, suggestions)); 
            }
 
            return suggestions;
        }
Example #5
0
        private ITextPointer SearchForWordBreaks(ITextPointer position, LogicalDirection direction, XmlLanguage language, int minWordCount, bool stopOnError)
        { 
            ITextPointer closestErrorPosition;
            ITextPointer searchPosition; 
            ITextPointer start; 
            ITextPointer end;
            StaticTextPointer nextErrorTransition; 
            int segmentCount;
            TextMap textMap;

            searchPosition = position.CreatePointer(); 

            closestErrorPosition = null; 
            if (stopOnError) 
            {
                nextErrorTransition = _statusTable.GetNextErrorTransition(position.CreateStaticPointer(), direction); 
                if (!nextErrorTransition.IsNull)
                {
                    closestErrorPosition = nextErrorTransition.CreateDynamicTextPointer(LogicalDirection.Forward);
                } 
            }
 
            bool hitBreakPoint = false; 

            do 
            {
                searchPosition.MoveByOffset(direction == LogicalDirection.Backward ? -ContextBlockSize : +ContextBlockSize);

                // Don't go past closestErrorPosition. 
                if (closestErrorPosition != null)
                { 
                    if (direction == LogicalDirection.Backward && closestErrorPosition.CompareTo(searchPosition) > 0 || 
                        direction == LogicalDirection.Forward && closestErrorPosition.CompareTo(searchPosition) < 0)
                    { 
                        searchPosition.MoveToPosition(closestErrorPosition);
                        hitBreakPoint = true;
                    }
                } 

                // Don't venture into text in another language. 
                ITextPointer closestLanguageTransition = GetNextLanguageTransition(position, direction, language, searchPosition); 

                if (direction == LogicalDirection.Backward && closestLanguageTransition.CompareTo(searchPosition) > 0 || 
                    direction == LogicalDirection.Forward && closestLanguageTransition.CompareTo(searchPosition) < 0)
                {
                    searchPosition.MoveToPosition(closestLanguageTransition);
                    hitBreakPoint = true; 
                }
 
                if (direction == LogicalDirection.Backward) 
                {
                    start = searchPosition; 
                    end = position;
                }
                else
                { 
                    start = position;
                    end = searchPosition; 
                } 

                textMap = new TextMap(start, end, start, end); 
                segmentCount = _spellerInterop.EnumTextSegments(textMap.Text, textMap.TextLength, null, null, null);
            }
            while (!hitBreakPoint &&
                   segmentCount < minWordCount + 1 && 
                   searchPosition.GetPointerContext(direction) != TextPointerContext.None);
 
            return searchPosition; 
        }
Example #6
0
        // Helper for ExpandToWordBreakAndContext -- returns the index of a segment
        // containing or bordering a specified position (textMap.ContentStartOffset). 
        // Also returns the offset, within the TextMap, of the two word breaks surrounding
        // TextMap.ContentStartOffset.  The word breaks may be segment edges, or 
        // the extent of a run of whitespace between two segments. 
        private int FindPositionInSegmentList(TextMap textMap, LogicalDirection direction, ArrayList segments,
            out int leftWordBreak, out int rightWordBreak) 
        {
            SpellerInterop.STextRange sTextRange;
            int index;
 
            // Make the compiler happy by initializing the out's to bogus values.
            leftWordBreak = Int32.MaxValue; 
            rightWordBreak = -1; 

            // Check before the first segment, which start at the first 
            // non-whitespace char.
            sTextRange = (SpellerInterop.STextRange)segments[0];
            if (textMap.ContentStartOffset < sTextRange.Start)
            { 
                leftWordBreak = 0;
                rightWordBreak = sTextRange.Start; 
                index = -1; 
            }
            else 
            {
                // Check after the last segment, which does not include final whitespace.
                sTextRange = (SpellerInterop.STextRange)segments[segments.Count-1];
                if (textMap.ContentStartOffset > sTextRange.Start + sTextRange.Length) 
                {
                    leftWordBreak = sTextRange.Start + sTextRange.Length; 
                    rightWordBreak = textMap.TextLength; 
                    index = segments.Count;
                } 
                else
                {
                    // Walk the segment list, checking each segment and space in between.
                    for (index = 0; index < segments.Count; index++) 
                    {
                        sTextRange = (SpellerInterop.STextRange)segments[index]; 
 
                        leftWordBreak = sTextRange.Start;
                        rightWordBreak = sTextRange.Start + sTextRange.Length; 

                        // Check if we're inside this segment.
                        if (leftWordBreak <= textMap.ContentStartOffset &&
                            rightWordBreak >= textMap.ContentStartOffset) 
                        {
                            break; 
                        } 
                        // Or if we're between this segment and the next one --
                        // segments do not include white space. 
                        if (index < segments.Count - 1 &&
                            rightWordBreak < textMap.ContentStartOffset)
                        {
                            sTextRange = (SpellerInterop.STextRange)segments[index + 1]; 
                            leftWordBreak = rightWordBreak;
                            rightWordBreak = sTextRange.Start; 
 
                            if (rightWordBreak > textMap.ContentStartOffset)
                            { 
                                // position is between segments[i] and segments[i+1].
                                // Adjust i so that adding MinWordBreaksForContext below
                                // doesn't include an extra word.
                                if (direction == LogicalDirection.Backward) 
                                {
                                    index++; 
                                } 
                                break;
                            } 
                        }
                    }
                }
            } 

            Invariant.Assert(leftWordBreak <= textMap.ContentStartOffset && textMap.ContentStartOffset <= rightWordBreak); 
 
            return index;
        } 
Example #7
0
        private void ExpandToWordBreakAndContext(ITextPointer position, LogicalDirection direction, XmlLanguage language,
            out ITextPointer contentPosition, out ITextPointer contextPosition) 
        {
            ITextPointer start; 
            ITextPointer end; 
            ITextPointer outwardPosition;
            ITextPointer inwardPosition; 
            TextMap textMap;
            ArrayList segments;
            SpellerInterop.STextRange sTextRange;
            LogicalDirection inwardDirection; 
            int i;
 
            contentPosition = position; 
            contextPosition = position;
 
            if (position.GetPointerContext(direction) == TextPointerContext.None)
            {
                // There is no following context, we're at document start/end.
                return; 
            }
 
            // Disable spell checking functionality since we're only 
            // interested in word breaks here.  This greatly cuts down
            // the engine's workload. 
            _spellerInterop.SetContextOption("IsSpellChecking", false);

            //
            // Build an array of wordbreak offsets surrounding the position. 
            //
 
            // 1. Search outward, into surrounding text.  We need MinWordBreaksForContext 
            // word breaks to handle multi-word errors.
            outwardPosition = SearchForWordBreaks(position, direction, language, MinWordBreaksForContext, true /* stopOnError */); 

            // 2. Search inward, towards content.  We just need one word break inward.
            inwardDirection = direction == LogicalDirection.Forward ? LogicalDirection.Backward : LogicalDirection.Forward;
            inwardPosition = SearchForWordBreaks(position, inwardDirection, language, 1, false /* stopOnError */); 

            // Get combined word breaks.  This may not be the same as we calculated 
            // in two parts above, since we don't know yet whether or not position is 
            // on a word break.
            if (direction == LogicalDirection.Backward) 
            {
                start = outwardPosition;
                end = inwardPosition;
            } 
            else
            { 
                start = inwardPosition; 
                end = outwardPosition;
            } 
            textMap = new TextMap(start, end, position, position);
            segments = new ArrayList(MinWordBreaksForContext + 1);
            _spellerInterop.EnumTextSegments(textMap.Text, textMap.TextLength, null,
                new SpellerInterop.EnumTextSegmentsCallback(ExpandToWordBreakCallback), segments); 

            // 
            // Use our table of word breaks to calculate context and content positions. 
            //
            if (segments.Count == 0) 
            {
                // No segments.  This can happen if position is surrounded by
                // nothing but white space.  We've already initialized contentPosition
                // and contextPosition so there's nothing to do. 
            }
            else 
            { 
                int leftWordBreak;
                int rightWordBreak; 
                int contentOffset;
                int contextOffset;

                // Figure out where position lives in the segment list. 
                i = FindPositionInSegmentList(textMap, direction, segments, out leftWordBreak, out rightWordBreak);
 
                // contentPosition should be an edge on the segment we found. 
                if (direction == LogicalDirection.Backward)
                { 
                    contentOffset = textMap.ContentStartOffset == rightWordBreak ? rightWordBreak : leftWordBreak;
                }
                else
                { 
                    contentOffset = textMap.ContentStartOffset == leftWordBreak ? leftWordBreak : rightWordBreak;
                } 
                contentPosition = textMap.MapOffsetToPosition(contentOffset); 

                // contextPosition should be MinWordBreaksForContext - 1 words away. 
                if (direction == LogicalDirection.Backward)
                {
                    i -= (MinWordBreaksForContext - 1);
                    sTextRange = (SpellerInterop.STextRange)segments[Math.Max(i, 0)]; 
                    // We might actually follow contentOffset if we're at the document edge.
                    // Don't let that happen. 
                    contextOffset = Math.Min(sTextRange.Start, contentOffset); 
                }
                else 
                {
                    i += MinWordBreaksForContext;
                    sTextRange = (SpellerInterop.STextRange)segments[Math.Min(i, segments.Count-1)];
                    // We might actually preceed contentOffset if we're at the document edge. 
                    // Don't let that happen.
                    contextOffset = Math.Max(sTextRange.Start + sTextRange.Length, contentOffset); 
                } 
                contextPosition = textMap.MapOffsetToPosition(contextOffset);
            } 

            // Final fixup: if the dirty range covers only formatting (which is not passed
            // to the speller engine) then we might actually "expand" in the wrong
            // direction, since the TextMap will jump over formatting. 
            // Backup if necessary.
            if (direction == LogicalDirection.Backward) 
            { 
                if (position.CompareTo(contentPosition) < 0)
                { 
                    contentPosition = position;
                }
                if (position.CompareTo(contextPosition) < 0)
                { 
                    contextPosition = position;
                } 
            } 
            else
            { 
                if (position.CompareTo(contentPosition) > 0)
                {
                    contentPosition = position;
                } 
                if (position.CompareTo(contextPosition) > 0)
                { 
                    contextPosition = position; 
                }
            } 
        }
Example #8
0
        // Flags a run of text with an error.
        // In two exceptional circumstances we schedule an idle-time callback
        // to re-analyze the run instead of marking it:
        // - when the caret is within the error text. 
        // - when an IME composition covers the text.
        private void MarkErrorRange(TextMap textMap, SpellerInterop.STextRange sTextRange) 
        { 
            ITextPointer errorStart;
            ITextPointer errorEnd; 

            if (sTextRange.Start + sTextRange.Length > textMap.ContentEndOffset)
            {
                // We found an error that starts in the content but extends into 
                // the context.  This must be a multi-word error.
                // For now, ignore it. 
                // 
                return;
            } 

            errorStart = textMap.MapOffsetToPosition(sTextRange.Start);
            errorEnd = textMap.MapOffsetToPosition(sTextRange.Start + sTextRange.Length);
 
            if (sTextRange.Start < textMap.ContentStartOffset)
            { 
                Invariant.Assert(sTextRange.Start + sTextRange.Length > textMap.ContentStartOffset); 

                // We've found an error that start in the context and extends into 
                // the content.  This can happen as more text is revealed to the
                // speller engine as the caret moves forward.
                // E.g., while scanning "avalon's" we flag an error over "avalon",
                // ignoring the "'s" because the caret is positioned within that segment. 
                // Then, the user hits space and now we analyze "'s" along with its
                // preceding context "avalon".  In this final scan, "avalon's" as a while 
                // is flagged as an error and we enter this if statement. 

                // We must mark the range clean before we can mark it dirty. 
                // _statusTable.MarkErrorRange can only handle clean runs.
                _statusTable.MarkCleanRange(errorStart, errorEnd);
            }
 
            _statusTable.MarkErrorRange(errorStart, errorEnd);
        }