コード例 #1
0
        /// <summary>
        /// All items that can incrementally parse their children should run their
        /// full parse in the same way. This function does that work.
        /// </summary>
        public static bool FullParseIncrementalItem(
            IIncrementalParseItem item,
            ItemFactory itemFactory,
            ITextProvider text,
            TokenStream tokens)
        {
            ComplexItem complexItem = (ComplexItem)item;
            ParseItem   prevChild   = null;

            while (true)
            {
                ParseItem newChild = item.CreateNextChild(prevChild, itemFactory, text, tokens);

                if (newChild != null)
                {
                    complexItem.Children.Add(newChild);
                    prevChild = newChild;
                }
                else
                {
                    break;
                }
            }

            item.UpdateCachedChildren();
            item.UpdateParseErrors();

            return(complexItem.Children.Count > 0);
        }
コード例 #2
0
        public void OnTextChange(ITextProvider fullNewText, int changeStart, int deletedLength, int insertedLength)
        {
            Debug.Assert(IsOwnerThread, "CssTree.OnTextChange must be called on the main thread");

            if (StyleSheet == null || !IsOwnerThread)
            {
                return;
            }
#if DEBUG_INCREMENTAL_PARSE
            DateTime startTime = DateTime.UtcNow;
#endif
            // Figure out which tokens changed
            ICssParser parser = _parserFactory.CreateParser();
            IncrementalTokenizer.Result tokenResult = IncrementalTokenizer.TokenizeChange(
                parser.TokenizerFactory, Tokens, TextProvider, fullNewText, changeStart, deletedLength, insertedLength);

            // Adjust the input to match what was actually tokenized
            changeStart    = tokenResult.TokenizationStart;
            deletedLength  = tokenResult.TextDeletedLength;
            insertedLength = tokenResult.TextInsertedLength;

            // Figure out where to start incrementally parsing
            IIncrementalParseItem parentItem        = GetIncrementalParseParent(changeStart, deletedLength);
            ComplexItem           parentComplexItem = (ComplexItem)parentItem;

            int firstReparseChild          = FindFirstChildToReparse(parentComplexItem, tokenResult.OldTokenStart);
            int firstCleanChild            = FindFirstCleanChild(parentComplexItem, tokenResult.OldTokenStart + tokenResult.OldTokenCount);
            int firstCleanTokenAfterParent = FindFirstCleanTokenAfterParent(parentComplexItem);

            using (CreateWriteLock())
            {
                // Update the tokens and text
                IncrementalTokenizer.ApplyResult(tokenResult);
                firstCleanTokenAfterParent += tokenResult.NewTokens.Count - tokenResult.OldTokenCount;

                // Init the token stream for parsing
                TokenStream tokenStream         = new TokenStream(Tokens);
                int         streamPositionStart = FindTokenToStartParsing(parentComplexItem, firstReparseChild);
                tokenStream.Position = streamPositionStart;
                Debug.Assert(tokenStream.Position <= tokenResult.OldTokenStart);

                // Init parsing
                ItemFactory itemFactory = new ItemFactory(parser.ExternalItemFactory, fullNewText, tokenStream);
                tokenStream.SkipComments = true; // must be set after extracting comments

                // Init the old and new child lists
                ParseItemList oldChildren        = parentComplexItem.Children;
                ParseItemList newChildren        = new ParseItemList();
                ParseItemList deletedChildren    = new ParseItemList();
                ParseItemList errorsChangedItems = new ParseItemList();
                int           deleteChildCount   = oldChildren.Count - firstReparseChild;

                // CreateNextChild needs to know the previous child for context
                ParseItem prevChild = (firstReparseChild > 0) ? oldChildren[firstReparseChild - 1] : null;

                while (true)
                {
                    ParseItem newChild = parentItem.CreateNextChild(prevChild, itemFactory, fullNewText, tokenStream);

                    if (newChild != null)
                    {
                        // Are we done parsing yet?
                        if (newChild.Start >= changeStart + insertedLength)
                        {
                            // See if this new child exactly matches an old child
                            int       oldChildIndex = oldChildren.FindInsertIndex(newChild.Start, beforeExisting: true);
                            ParseItem oldChild      = (oldChildIndex < oldChildren.Count) ? oldChildren[oldChildIndex] : null;

                            if (oldChild != null &&
                                oldChildIndex >= firstCleanChild &&
                                oldChild.Start == newChild.Start &&
                                oldChild.Length == newChild.Length &&
                                oldChild.GetType() == newChild.GetType())
                            {
                                // Found a match, stop parsing
                                deleteChildCount = oldChildIndex - firstReparseChild;
                                break;
                            }
                        }

                        newChildren.Add(newChild);
                        prevChild = newChild;
                    }
                    else if (tokenStream.Position != firstCleanTokenAfterParent)
                    {
                        // When the parse doesn't stop exactly on the first clean token after the parent,
                        // then the tree structure changed too much. Just fall back to a full parse:
                        ParseNewStyleSheet(fullNewText, Tokens);
                        //Debug.WriteLine("CSS: Full parse:{0}ms", (DateTime.UtcNow - startTime).TotalMilliseconds);

                        return;
                    }
                    else
                    {
                        break;
                    }
                }

                // Replace items in the parent (saving the deleted items for later)
                oldChildren.RemoveRange(firstReparseChild, deleteChildCount, deletedChildren);
                oldChildren.AddRange(newChildren);

                if (oldChildren.Count == 0)
                {
                    // The parent was deleted, currently can't deal with that as an incremental change
                    ParseNewStyleSheet(fullNewText, Tokens);

                    return;
                }

                // Collect comments over the parsed region
                tokenStream.SkipComments = false;
                int tokenCount = tokenStream.Position - streamPositionStart;
                tokenStream.Position = streamPositionStart;
                IList <Comment> comments = parser.ExtractComments(fullNewText, Tokens, tokenStream.Position, tokenCount);

                // All done parsing and updating the tree, so now update caches and fire the "on changed" event

                StyleSheet.TextProvider = fullNewText;

                parentItem.UpdateCachedChildren();

                if (parentItem.UpdateParseErrors())
                {
                    errorsChangedItems.Add(parentComplexItem);
                }

                InsertComments(comments, newChildren);

#if DEBUG_INCREMENTAL_PARSE
                Debug.WriteLine("CSS: Inc parse:{0}ms. Deleted:{1}, Inserted:{2}", (DateTime.UtcNow - startTime).TotalMilliseconds, deletedChildren.Count, newChildren.Count);
                VerifyTokensAfterIncrementalChange(parser.TokenizerFactory, fullNewText, Tokens);
                VerifyTreeAfterIncrementalParse(fullNewText, Tokens, StyleSheet);
#endif
                FireOnItemsChanged(deletedChildren, newChildren, errorsChangedItems);

                // Clean up the deleted items (must be after the event is fired)
                foreach (ParseItem deletedItem in deletedChildren)
                {
                    deletedItem.Parent = null;
                }
            }
        }