int (@NotNull TextChange change) { int newChangeStart = change.getStart(); int newChangeEnd = change.getEnd(); int insertionIndex = getChangeIndex(change.getStart()); int clientShift = 0; // 'Client text' shift before the given change to store. I.e. this value can be subtracted from the // given change's start/end offsets in order to get original document range affected by the given change. int changeDiff = change.getText().length() - (change.getEnd() - change.getStart()); if (insertionIndex < 0) { insertionIndex = -insertionIndex - 1; if (insertionIndex >= myChanges.size()) { if (!myChanges.isEmpty()) { ChangeEntry changeEntry = myChanges.get(myChanges.size() - 1); clientShift = changeEntry.clientStartOffset - changeEntry.change.getStart() + changeEntry.change.getDiff(); } myChanges.add(new ChangeEntry( new TextChangeImpl(change.getText(), change.getStart() - clientShift, change.getEnd() - clientShift), change.getStart() )); return insertionIndex; } else if (insertionIndex > 0) { ChangeEntry changeEntry = myChanges.get(insertionIndex - 1); clientShift = changeEntry.clientStartOffset - changeEntry.change.getStart() + changeEntry.change.getDiff(); } } else { ChangeEntry changeEntry = myChanges.get(insertionIndex); clientShift = changeEntry.clientStartOffset - changeEntry.change.getStart(); } boolean updateClientOffsetOnly = false; for (int i = insertionIndex; i < myChanges.size(); i++) { ChangeEntry changeEntry = myChanges.get(i); int storedClientStart = changeEntry.change.getStart() + clientShift; CharSequence storedText = changeEntry.change.getText(); int storedClientEnd = storedClientStart + storedText.length(); // Stored change lays after the new one. if (!updateClientOffsetOnly && storedClientStart > newChangeEnd) { if (changeDiff != 0) { updateClientOffsetOnly = true; } else { break; } } if (updateClientOffsetOnly) { changeEntry.clientStartOffset += changeDiff; continue; } // Stored change lays before the new one. if (storedClientEnd <= newChangeStart) { clientShift += changeEntry.change.getDiff(); insertionIndex = i + 1; continue; } // Check if given change targets sub-range of the stored one. if (storedClientStart <= newChangeStart && storedClientEnd >= newChangeEnd) { StringBuilder adjustedText = new StringBuilder(); if (storedClientStart < newChangeStart) { adjustedText.append(storedText.subSequence(0, newChangeStart - storedClientStart)); } adjustedText.append(change.getText()); if (storedClientEnd > newChangeEnd) { adjustedText.append(storedText.subSequence(newChangeEnd - storedClientStart, storedText.length())); } if (adjustedText.length() == 0 && changeEntry.change.getStart() == changeEntry.change.getEnd()) { myChanges.remove(i); insertionIndex = -1; updateClientOffsetOnly = true; i--; // Assuming that 'i' is incremented at the 'for' loop. continue; } changeEntry.change = new TextChangeImpl(adjustedText, changeEntry.change.getStart(), changeEntry.change.getEnd()); insertionIndex = -1; updateClientOffsetOnly = true; continue; } // Check if given change completely contains stored change range. if (newChangeStart <= storedClientStart && newChangeEnd >= storedClientEnd) { myChanges.remove(i); insertionIndex = i; newChangeEnd -= changeEntry.change.getDiff(); i--; // Assuming that 'i' is incremented at the 'for' loop. continue; } // Check if given change intersects stored change range from the left. if (newChangeStart <= storedClientStart && newChangeEnd > storedClientStart) { int numberOfStoredChangeSymbolsToRemove = newChangeEnd - storedClientStart; CharSequence adjustedText = storedText.subSequence(numberOfStoredChangeSymbolsToRemove, storedText.length()); changeEntry.change = new TextChangeImpl(adjustedText, changeEntry.change.getStart(), changeEntry.change.getEnd()); changeEntry.clientStartOffset += changeDiff + numberOfStoredChangeSymbolsToRemove; newChangeEnd -= numberOfStoredChangeSymbolsToRemove; insertionIndex = i; continue; } // Check if given change intersects stored change range from the right. if (newChangeEnd >= storedClientEnd) { CharSequence adjustedText = storedText.subSequence(0, newChangeStart - storedClientStart); TextChangeImpl adjusted = new TextChangeImpl(adjustedText, changeEntry.change.getStart(), changeEntry.change.getEnd()); changeEntry.change = adjusted; clientShift += adjusted.getDiff(); newChangeEnd -= storedClientEnd - newChangeStart; insertionIndex = i + 1; continue; } // Check if given change is left-adjacent to the stored change. changeEntry.clientStartOffset += changeDiff; } if (insertionIndex >= 0) { myChanges.add(insertionIndex, new ChangeEntry( new TextChangeImpl(change.getText(), newChangeStart - clientShift, newChangeEnd - clientShift), change.getStart() )); } return insertionIndex; }