Example #1
0
        /// <summary>
        /// Illustration : lines with code elements continued from previous line or with a continuation on next line (before update)
        /// SW represents a code element starting word
        /// [  SW    x]
        /// [        x]
        /// [SW      x]
        /// [   SW    ]
        /// [   x   SW]
        /// A DocumentChange intersects with a previously parsed multiline code element if :
        /// * LineInserted :
        ///   - all previous lines until the last starting word
        ///     (get previous lines until the previous code element is found)
        ///   - all the next lines until the next starting word
        ///     (get next lines until the next code element is found,
        ///      do not reset the last line if the next code element starts at the beginning of the line)
        /// * LineUpdated / LineRemoved :
        ///   - all previous lines until the last starting word
        ///     (get previous lines until the previous code element is found)
        ///   - all the next lines until the next starting word
        ///     (get next lines until the next code element is found,
        ///      do not reset the last line if the next code element starts at the beginning of the line)
        /// When navigating to previous or next line searching for a code element, we can stop when a fresh insertion / update is encountered.
        /// When we reset a line which was not directly updated, and where the code element started in the middle of the line,
        /// we must "remember" at which token we must start parsing.
        /// In conclusion, the incremental parsing step for code elements is divided in two steps :
        /// 1. List all the starting and stop tokens of sections to parse with the rules above
        /// 2. Parse code elements beginning with starting token and until we reach stop token
        /// </summary>
        private static ParseSection CheckIfAdjacentLinesNeedRefresh(DocumentChangeType changeType, int lineIndex, ISearchableReadOnlyList <CodeElementsLine> documentLines, PrepareDocumentLineForUpdate prepareDocumentLineForUpdate, IList <DocumentChange <ICodeElementsLine> > codeElementsLinesChanges, ParseSection lastParseSection)
        {
            ParseSection currentParseSection = new ParseSection();

            // Navigate backwards to the start of the multiline code element
            if (lineIndex > 0)
            {
                int previousLineIndex  = lineIndex;
                int lastLineIndexReset = lastParseSection != null ? lastParseSection.StopLineIndex : -1;
                IEnumerator <CodeElementsLine> reversedEnumerator = documentLines.GetEnumerator(previousLineIndex - 1, -1, true);
                bool previousLineHasCodeElements = false;
                while (reversedEnumerator.MoveNext() && (--previousLineIndex > lastLineIndexReset))
                {
                    // Get the previous line until the first code element is encountered
                    CodeElementsLine previousLine = reversedEnumerator.Current;

                    // The start of the parse section is delimited by the previous CodeElement
                    if (previousLine != null)
                    {
                        previousLineHasCodeElements = previousLine.HasCodeElements;
                        if (previousLineHasCodeElements)
                        {
                            currentParseSection.StartLineIndex = previousLineIndex;
                            currentParseSection.StartToken     = previousLine.CodeElements.First().ConsumedTokens.FirstOrDefault();
                        }

                        // All lines contained in the parse section could be modified, and should be reset
                        previousLine = (CodeElementsLine)prepareDocumentLineForUpdate(previousLineIndex, previousLine, CompilationStep.CodeElementsParser);
                        previousLine.ResetCodeElements();
                        codeElementsLinesChanges.Add(new DocumentChange <ICodeElementsLine>(DocumentChangeType.LineUpdated, previousLineIndex, previousLine));
                    }

                    // Stop iterating backwards as soon as the start of an old CodeElement is found
                    if (previousLineHasCodeElements)
                    {
                        break;
                    }
                }
                // If no CodeElement was found on the previous lines, current parse section starts at the beggining of the file
                // (because last parseSection could only stop at a CodeElement or at the end of the file)
                if (!previousLineHasCodeElements)
                {
                    currentParseSection.StartLineIndex = 0;
                    currentParseSection.StartToken     = null;
                }
            }
            // If line 0 was updated, current parse section starts at the beggining of the file
            else
            {
                currentParseSection.StartLineIndex = 0;
                currentParseSection.StartToken     = null;
            }

            // Navigate forwards to the end of the multiline code element
            if (lineIndex < (documentLines.Count - 1))
            {
                int nextLineIndex = lineIndex;
                IEnumerator <CodeElementsLine> enumerator = documentLines.GetEnumerator(nextLineIndex + 1, -1, false);
                bool nextLineHasCodeElements = false;
                while (enumerator.MoveNext())
                {
                    // Get the next line until non continuation line is encountered
                    nextLineIndex++;
                    CodeElementsLine nextLine = enumerator.Current;

                    // Check if the next CodeElement found starts at the beginning of the line
                    if (nextLine != null)
                    {
                        nextLineHasCodeElements = nextLine.HasCodeElements;
                        bool nextCodeElementStartsAtTheBeginningOfTheLine = false;
                        if (nextLineHasCodeElements)
                        {
                            try
                            {
                                Token startTokenForNextParseSection = nextLine.CodeElements.First().ConsumedTokens.FirstOrDefault();
                                Token firstSourceTokenOfThisLine    = nextLine.TokensWithCompilerDirectives.First(token => token.Channel == Token.CHANNEL_SourceTokens);
                                nextCodeElementStartsAtTheBeginningOfTheLine = startTokenForNextParseSection == firstSourceTokenOfThisLine;
                            }
                            catch (System.InvalidOperationException /*e*/)
                            {//JCM: 28/08/2017: I noticed that this Exception can occur if: it doesn't exists a token which verifies the predicate: token.Channel == Token.CHANNEL_SourceToken
                                nextCodeElementStartsAtTheBeginningOfTheLine = false;
                            }
                        }

                        // All lines contained in the parse section could be modified
                        if (!nextCodeElementStartsAtTheBeginningOfTheLine)
                        {
                            // TO DO : ERROR below, will not work if we have source tokens from previous CodeElement + one other CodeElement on the same line
                            // => the other CodeElement will be deleted by prepareDocumentLineForUpdate and not parsed again
                            nextLine = (CodeElementsLine)prepareDocumentLineForUpdate(nextLineIndex, nextLine, CompilationStep.CodeElementsParser);
                            codeElementsLinesChanges.Add(new DocumentChange <ICodeElementsLine>(DocumentChangeType.LineUpdated, nextLineIndex, nextLine));
                        }

                        // Stop iterating forwards as soon as the start of an old CodeElement is found
                        if (nextLineHasCodeElements)
                        {
                            currentParseSection.StopLineIndex = nextLineIndex;
                            currentParseSection.StopToken     = nextLine.CodeElements.First().ConsumedTokens.FirstOrDefault();
                            currentParseSection.StopTokenIsFirstTokenOfTheLine = nextCodeElementStartsAtTheBeginningOfTheLine;
                            break;
                        }
                    }
                }
                // If no CodeElement was found on the next lines, current parse section current parse section ends at the end of the file
                if (!nextLineHasCodeElements)
                {
                    currentParseSection.StopLineIndex = nextLineIndex;
                    currentParseSection.StopToken     = null;
                    currentParseSection.StopTokenIsFirstTokenOfTheLine = false;
                }
            }
            // If last line was updated, or if no CodeElement was found after the updated line, current parse section ends at the end of the file
            else
            {
                currentParseSection.StopLineIndex = documentLines.Count - 1;
                currentParseSection.StopToken     = null;
                currentParseSection.StopTokenIsFirstTokenOfTheLine = false;
            }

            return(currentParseSection);
        }