int incrementalUpdate(int eventOffset, int eventOldLength, int eventNewLength, @NotNull Document document) {
    CharSequence text = document.getImmutableCharSequence();
    if (mySegments.getSegmentCount() == 0 || mySegments.getLastValidOffset() < eventOffset) {
      setText(text);
      return text.length();
    }
    myText = text;

    int segmentIndex = mySegments.findSegmentIndex(eventOffset) - 2;
    int oldStartIndex = Math.max(0, segmentIndex);
    int startIndex = oldStartIndex;

    int data;
    do {
      data = mySegments.getSegmentData(startIndex);
      if (isInitialState(data) || startIndex == 0) break;
      startIndex--;
    }
    while (true);

    int startOffset = mySegments.getSegmentStart(startIndex);

    int initialState;
    int textLength = text.length();
    if (startOffset == 0 && myLexer instanceof RestartableLexer) {
      initialState = ((RestartableLexer)myLexer).getStartState();
      myLexer.start(text, startOffset, text.length(), initialState);
    }
    else {
      if (myLexer instanceof RestartableLexer) {
        initialState = mySegments.unpackStateFromData(mySegments.getSegmentData(startIndex));
        ((RestartableLexer)myLexer).start(text, startOffset, text.length(), initialState, createTokenIterator(startIndex));
      }
      else {
        initialState = myInitialState;
        myLexer.start(text, startOffset, text.length(), initialState);
      }
    }

    Lexer lexerWrapper = new ValidatingLexerWrapper(myLexer);
    for (IElementType tokenType = lexerWrapper.getTokenType(); tokenType != null; tokenType = lexerWrapper.getTokenType()) {
      if (startIndex >= oldStartIndex) break;

      int lexerState = lexerWrapper.getState();
      int tokenStart = lexerWrapper.getTokenStart();
      int tokenEnd = lexerWrapper.getTokenEnd();

      data = mySegments.packData(tokenType, lexerState, canRestart(lexerState));
      if (mySegments.getSegmentStart(startIndex) != tokenStart ||
          mySegments.getSegmentEnd(startIndex) != tokenEnd ||
          mySegments.getSegmentData(startIndex) != data) {
        break;
      }
      startIndex++;
      lexerWrapper.advance();
    }

      /*
        Highlighting lexer is expected to periodically return to its "initial state" and
        so to denote valid starting points for incremental highlighting.

        If this requirement is unfulfiled, document has to be always re-analyzed from the beginning
        up to the point of modification,  which can hog CPU and make typing / editing very sluggish,
        especially at large offsets (with at least O(n) time complexity).

        As the faulty lexer implementations otherwise behave normally, it's often hard to spot the problem in the wild.
        Despite additng LexerTestCase.checkCorrectRestart and LexerTestCase.checkZeroState checks and fixing many lexers,
        it's still not so unusual to discover a further broken lexer through pure luck.

        The following runtime check reports cases when document has to be re-analyzed from 0 offset and
        the number of traversed tokens is greater than a predefined threshold.

        Because many highlighting lexers are implemented via the LayeredLexer which forces non-initial state
        (and thus suppresses incrementality) within layers, some false-positivess are probable.
        For example, it's possible to trigger the warning by creating a file with a really large comment
        right at the beginning, and then to modify text at the end of that comment.
        However, this seems to be a rather unusual use case, so that the gain from detecting faulty
        lexers (including third-party ones) justifies the check.

        In a sense, the warning is always righteous, as even with proper layered lexers there really is
        no incrementality within layers, which might lead to performance problem in corresponding cases.
       */
    if (ApplicationManager.getApplication().isInternal() &&
        startOffset == 0 && startIndex > LEXER_INCREMENTALITY_THRESHOLD) {

      Class lexerClass = myLexer.getClass();

      if (!ourNonIncrementalLexers.contains(lexerClass)) {
        LOG.warn(String.format("%s is probably not incremental: no initial state throughout %d tokens",
                               lexerClass.getName(), startIndex));

        ourNonIncrementalLexers.add(lexerClass);
      }
    }

    startOffset = mySegments.getSegmentStart(startIndex);
    SegmentArrayWithData insertSegments = new SegmentArrayWithData(mySegments.createStorage());

    int repaintEnd = -1;
    int insertSegmentCount = 0;
    int oldEndIndex = -1;
    int shift = eventNewLength - eventOldLength;
    int newEndOffset = eventOffset + eventNewLength;
    int lastSegmentOffset = mySegments.getLastValidOffset();
    for (IElementType tokenType = lexerWrapper.getTokenType(); tokenType != null; tokenType = lexerWrapper.getTokenType()) {
      int lexerState = lexerWrapper.getState();
      int tokenStart = lexerWrapper.getTokenStart();
      int tokenEnd = lexerWrapper.getTokenEnd();

      data = mySegments.packData(tokenType, lexerState, canRestart(lexerState));
      int shiftedTokenStart = tokenStart - shift;
      if (tokenStart >= newEndOffset && shiftedTokenStart < lastSegmentOffset && canRestart(lexerState)) {
        int index = mySegments.findSegmentIndex(shiftedTokenStart);
        if (mySegments.getSegmentStart(index) == shiftedTokenStart && mySegments.getSegmentData(index) == data) {
          repaintEnd = tokenStart;
          oldEndIndex = index;
          break;
        }
      }
      insertSegments.setElementAt(insertSegmentCount, tokenStart, tokenEnd, data);
      insertSegmentCount++;
      lexerWrapper.advance();
    }

    if (repaintEnd > 0) {
      while (insertSegmentCount > 0 && oldEndIndex > startIndex) {
        if (!segmentsEqual(mySegments, oldEndIndex - 1, insertSegments, insertSegmentCount - 1, shift) ||
            hasAdditionalData(oldEndIndex - 1)) {
          break;
        }
        insertSegmentCount--;
        oldEndIndex--;
        repaintEnd = insertSegments.getSegmentStart(insertSegmentCount);
        insertSegments.remove(insertSegmentCount, insertSegmentCount + 1);
      }
    }

    if (repaintEnd == -1) {
      repaintEnd = textLength;
    }

    if (oldEndIndex < 0) {
      oldEndIndex = mySegments.getSegmentCount();
    }
    mySegments.shiftSegments(oldEndIndex, shift);
    mySegments.replace(startIndex, oldEndIndex, insertSegments);

    if (insertSegmentCount != 0 &&
        (oldEndIndex != startIndex + 1 || insertSegmentCount != 1 || data != mySegments.getSegmentData(startIndex))) {
      myEditor.repaint(startOffset, repaintEnd);
    }
    return repaintEnd;
  }
int incrementalUpdate(int eventOffset, int eventOldLength, int eventNewLength, @NotNull Document document)