private void doStoreChange(@NotNull TextChangeImpl change) { TextChangesStorage storage = myDeferredChangesStorage.get(); if (storage.size() >= MAX_DEFERRED_CHANGES_NUMBER) { flushDeferredChanged(storage); } storage.store(change); myDeferredShift += change.getDiff(); if (myDebugDeferredProcessing) { myDebugDeferredChanges.add(change); } }
/** * Stores given change at the current storage and returns its index at {@link #myChanges changes * collection} (if any). * * @param change change to store * @return non-negative value that indicates index under which given change is stored at the * {@link #myChanges changes collection}; negative value if given change only modifies * sub-range of already registered range */ @SuppressWarnings({"AssignmentToForLoopParameter"}) private int doStore(@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()); boolean updateClientOffsetOnly = false; 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 && !myChanges.isEmpty()) { 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(); } 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 (newChangeStart < storedClientEnd && 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. if (newChangeEnd == storedClientStart) { changeEntry.clientStartOffset += changeDiff; } } if (insertionIndex >= 0) { myChanges.add( insertionIndex, new ChangeEntry( new TextChangeImpl( change.getText(), newChangeStart - clientShift, newChangeEnd - clientShift), change.getStart())); } return insertionIndex; }