Example #1
0
 public DocumentChange(
     IDocumentSnapshot documentSnapshot,
     DocumentChangeType changeType,
     int newIndex,
     int oldIndex)
 {
     DocumentSnapshot = documentSnapshot;
     ChangeType       = changeType;
     NewIndex         = newIndex;
     OldIndex         = oldIndex;
 }
Example #2
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);
        }
Example #3
0
 /// <summary>
 /// Constructor
 /// </summary>
 /// <param name="type">Type of change applied to the line</param>
 /// <param name="lineIndex">Index of the line which was changed</param>
 /// <param name="newLine">New line content after the update (null in case of a LineRemoved event)</param>
 public DocumentChange(DocumentChangeType type, int lineIndex, T newLine)
 {
     Type      = type;
     LineIndex = lineIndex;
     NewLine   = newLine;
 }
Example #4
0
        /// <summary>
        /// Illustration : lines with directives continued from previous line or with a continuation on next line (before update) are marked with a cross
        /// [     ]
        /// [    x]
        /// [x   x]
        /// [x    ]
        /// [     ]
        /// A DocumentChange intersects with a previously parsed multiline compiler directive if :
        /// * LineInserted :
        ///   - the next line was a continuation
        /// * LineUpdated / LineRemoved :
        ///   - the previous line was continued
        ///   - the next line was a continuation
        /// When navigating to previous or next line searching for a continuation, we must ignore all fresh insertions / updates.
        /// We must then reset all processed tokens lines involved in a multiline compiler directive.
        /// </summary>
        private static int CheckIfAdjacentLinesNeedRefresh(DocumentChangeType changeType, int lineIndex, ISearchableReadOnlyList <ProcessedTokensLine> documentLines, PrepareDocumentLineForUpdate prepareDocumentLineForUpdate, IList <DocumentChange <IProcessedTokensLine> > processedTokensLinesChanges, int lastLineIndexReset)
        {
            // Navigate backwards to the start of the multiline compiler directive
            if (lineIndex > 0)
            {
                int previousLineIndex = lineIndex;
                IEnumerator <ProcessedTokensLine> reversedEnumerator = documentLines.GetEnumerator(previousLineIndex - 1, -1, true);
                while (reversedEnumerator.MoveNext() && (--previousLineIndex > lastLineIndexReset))
                {
                    // Get the previous line until a non continued line is encountered
                    ProcessedTokensLine previousLine = reversedEnumerator.Current;

                    // A reset line was already treated by the previous call to CheckIfAdjacentLinesNeedRefresh : stop searching
                    if (previousLine.PreprocessingState == ProcessedTokensLine.PreprocessorState.NeedsCompilerDirectiveParsing)
                    {
                        break;
                    }
                    // Previous line is a continuation : reset this line and continue navigating backwards
                    // Previous line is not a continuation but is continued : reset this line and stop navigating backwards
                    else if (previousLine.HasDirectiveTokenContinuationFromPreviousLine || previousLine.HasDirectiveTokenContinuedOnNextLine)
                    {
                        lineIndex    = previousLineIndex;
                        previousLine = (ProcessedTokensLine)prepareDocumentLineForUpdate(previousLineIndex, previousLine, CompilationStep.Preprocessor);
                        processedTokensLinesChanges.Add(new DocumentChange <IProcessedTokensLine>(DocumentChangeType.LineUpdated, previousLineIndex, previousLine));
                        if (!previousLine.HasDirectiveTokenContinuationFromPreviousLine)
                        {
                            break;
                        }
                    }
                    // Previous line not involved in a multiline compiler directive : stop searching
                    else
                    {
                        break;
                    }
                }
            }

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

                    // A reset line will be treated by the next call to CheckIfAdjacentLinesNeedRefresh : stop searching
                    if (nextLine.PreprocessingState == ProcessedTokensLine.PreprocessorState.NeedsCompilerDirectiveParsing)
                    {
                        break;
                    }
                    // Next line is a continuation and is continued: reset this line and continue navigating forwards
                    // Next line is a continuation but is not continued : reset this line and stop navigating forwards
                    else if (nextLine.HasDirectiveTokenContinuationFromPreviousLine)
                    {
                        nextLine = (ProcessedTokensLine)prepareDocumentLineForUpdate(nextLineIndex, nextLine, CompilationStep.Preprocessor);
                        processedTokensLinesChanges.Add(new DocumentChange <IProcessedTokensLine>(DocumentChangeType.LineUpdated, nextLineIndex, nextLine));
                        lastLineIndexReset = nextLineIndex;
                        if (!nextLine.HasDirectiveTokenContinuedOnNextLine)
                        {
                            break;
                        }
                    }
                    // Previous line not involved in a multiline compiler directive : stop searching
                    else
                    {
                        break;
                    }
                }
            }

            return(lastLineIndexReset > lineIndex ? lastLineIndexReset : lineIndex);
        }
        /// <summary>
        /// Illustration : lines with directives continued from previous line or with a continuation on next line (before update) are marked with a cross
        /// [     ]
        /// [    x]
        /// [x   x]
        /// [x    ]
        /// [     ]
        /// A DocumentChange intersects with a previously parsed multiline compiler directive if : 
        /// * LineInserted :
        ///   - the next line was a continuation
        /// * LineUpdated / LineRemoved :
        ///   - the previous line was continued
        ///   - the next line was a continuation
        /// When navigating to previous or next line searching for a continuation, we must ignore all fresh insertions / updates.
        /// We must then reset all processed tokens lines involved in a multiline compiler directive.
        /// </summary>
        private static int CheckIfAdjacentLinesNeedRefresh(DocumentChangeType changeType, int lineIndex, ISearchableReadOnlyList<ProcessedTokensLine> documentLines, PrepareDocumentLineForUpdate prepareDocumentLineForUpdate, IList<DocumentChange<IProcessedTokensLine>> processedTokensLinesChanges, int lastLineIndexReset)
        {
            // Navigate backwards to the start of the multiline compiler directive
            if (lineIndex > 0)
            {
                int previousLineIndex = lineIndex;
                IEnumerator<ProcessedTokensLine> reversedEnumerator = documentLines.GetEnumerator(previousLineIndex - 1, -1, true);
                while (reversedEnumerator.MoveNext() && (--previousLineIndex > lastLineIndexReset))
                {
                    // Get the previous line until a non continued line is encountered
                    ProcessedTokensLine previousLine = reversedEnumerator.Current;

                    // A reset line was already treated by the previous call to CheckIfAdjacentLinesNeedRefresh : stop searching
                    if (previousLine.PreprocessingState == ProcessedTokensLine.PreprocessorState.NeedsCompilerDirectiveParsing)
                    {
                        break;
                    }
                    // Previous line is a continuation : reset this line and continue navigating backwards
                    // Previous line is not a continuation but is continued : reset this line and stop navigating backwards
                    else if (previousLine.HasDirectiveTokenContinuationFromPreviousLine || previousLine.HasDirectiveTokenContinuedOnNextLine)
                    {
                        lineIndex = previousLineIndex;
                        previousLine = (ProcessedTokensLine)prepareDocumentLineForUpdate(previousLineIndex, previousLine, CompilationStep.Preprocessor);
                        processedTokensLinesChanges.Add(new DocumentChange<IProcessedTokensLine>(DocumentChangeType.LineUpdated, previousLineIndex, previousLine));
                        if(!previousLine.HasDirectiveTokenContinuationFromPreviousLine)
                        {
                            break;
                        }
                    }
                    // Previous line not involved in a multiline compiler directive : stop searching
                    else
                    {
                        break;
                    }
                }
            }

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

                    // A reset line will be treated by the next call to CheckIfAdjacentLinesNeedRefresh : stop searching
                    if (nextLine.PreprocessingState == ProcessedTokensLine.PreprocessorState.NeedsCompilerDirectiveParsing)
                    {
                       break;
                    }
                    // Next line is a continuation and is continued: reset this line and continue navigating forwards
                    // Next line is a continuation but is not continued : reset this line and stop navigating forwards
                    else if (nextLine.HasDirectiveTokenContinuationFromPreviousLine)
                    {
                        nextLine = (ProcessedTokensLine)prepareDocumentLineForUpdate(nextLineIndex, nextLine, CompilationStep.Preprocessor);
                        processedTokensLinesChanges.Add(new DocumentChange<IProcessedTokensLine>(DocumentChangeType.LineUpdated, nextLineIndex, nextLine));
                        lastLineIndexReset = nextLineIndex;
                        if (!nextLine.HasDirectiveTokenContinuedOnNextLine)
                        {
                            break;
                        }
                    }
                    // Previous line not involved in a multiline compiler directive : stop searching
                    else
                    {
                        break;
                    }
                }
            }

            return lastLineIndexReset > lineIndex ? lastLineIndexReset : lineIndex;
        }
        /// <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);
                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
                    bool previousLineHasCodeElements = previousLine.HasCodeElements;
                    if (previousLineHasCodeElements)
                    {
                        currentParseSection.StartLineIndex = previousLineIndex;
                        currentParseSection.StartToken = previousLine.CodeElements[0].ConsumedTokens[0];
                    }

                    // 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;
                    }
                }
            }

            // 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, true);
                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
                    bool nextCodeElementStartsAtTheBeginningOfTheLine = false;
                    if (nextLine.HasCodeElements)
                    {
                        Token startTokenForNextParseSection = nextLine.CodeElements[0].ConsumedTokens[0];
                        Token firstSourceTokenOfThisLine = nextLine.TokensWithCompilerDirectives.First(token => token.Channel == Token.CHANNEL_SourceTokens);
                        nextCodeElementStartsAtTheBeginningOfTheLine = startTokenForNextParseSection == firstSourceTokenOfThisLine;
                    }

                    // All lines contained in the parse section could be modified
                    if (!nextCodeElementStartsAtTheBeginningOfTheLine)
                    {
                        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 (nextLine.HasCodeElements)
                    {
                        if (!nextCodeElementStartsAtTheBeginningOfTheLine)
                        {
                            currentParseSection.StopLineIndex = nextLineIndex;
                            currentParseSection.StopToken = nextLine.CodeElements[0].ConsumedTokens[0];
                        }
                        else
                        {
                            currentParseSection.StopLineIndex = nextLineIndex;
                            while (currentParseSection.StopToken == null && currentParseSection.StopLineIndex >= 1)
                            {
                                currentParseSection.StopLineIndex = currentParseSection.StopLineIndex - 1;
                                currentParseSection.StopToken = documentLines[currentParseSection.StopLineIndex].TokensWithCompilerDirectives.Last(token => token.Channel == Token.CHANNEL_SourceTokens);
                            }
                        }
                        currentParseSection.StopTokenIsLastTokenOfTheLine = nextCodeElementStartsAtTheBeginningOfTheLine;
                        break;
                    }
                }
            }
            // Current parse section ends with the updated line
            else
            {
                currentParseSection.StopLineIndex = lineIndex + 1;
                while (currentParseSection.StopToken == null && currentParseSection.StopLineIndex >= 1)
                {
                    currentParseSection.StopLineIndex = currentParseSection.StopLineIndex - 1;
                    currentParseSection.StopToken = documentLines[currentParseSection.StopLineIndex].TokensWithCompilerDirectives.Last(token => token.Channel == Token.CHANNEL_SourceTokens);
                }
                currentParseSection.StopTokenIsLastTokenOfTheLine = true;
            }

            return currentParseSection;
        }
Example #7
0
 /// <summary>
 /// Initializes a new instance of the <see cref="DocumentChangedEventArgs"/> class.
 /// </summary>
 /// <param name="change">
 /// The change.
 /// </param>
 public DocumentChangedEventArgs(DocumentChangeType change)
 {
     this.Change = change;
 }