void (@NotNull DocumentEvent event) { String docText = event.getDocument().getText(); if (myText.equals(docText)) return; if (event.isWholeTextReplaced()) { myRanges.clear(); myRanges.add(new RangeHighlightingData(0, docText.length(), false)); } else { int offset = event.getOffset(); if (event.getNewLength() >= event.getOldLength()) { if (myRanges.isEmpty()) { myRanges.add(new RangeHighlightingData(0, 0, false)); } int insertedLen = event.getNewLength() - event.getOldLength(); for (RangeHighlightingData data : myRanges) { if (data.textRange.contains(offset) || data == myRanges.get(myRanges.size() - 1) && data.textRange.getEndOffset() <= offset) { data.updateRange(data.textRange.grown(insertedLen)); } else if (data.textRange.getStartOffset() > offset) { data.updateRange(data.textRange.shiftRight(insertedLen)); } } } else { int deletedLen = event.getOldLength() - event.getNewLength(); TextRange deletedRange = new TextRange(offset, offset + deletedLen); int delta = 0; Iterator<RangeHighlightingData> rangeIterator = myRanges.iterator(); while(rangeIterator.hasNext()) { RangeHighlightingData data =rangeIterator.next(); int cutoutStart = Math.max(deletedRange.getStartOffset(), data.textRange.getStartOffset()); int cutoutEnd = Math.min(deletedRange.getEndOffset(), data.textRange.getEndOffset()); if (cutoutStart < cutoutEnd) { int shrinkSize = cutoutEnd - cutoutStart; if (shrinkSize == data.textRange.getLength()) { rangeIterator.remove(); } else { data.updateRange(data.textRange.grown(-shrinkSize)); } data.updateRange(data.textRange.shiftLeft(delta)); delta += shrinkSize; } else { data.updateRange(data.textRange.shiftLeft(delta)); } } } } myText = docText; }