void (@NotNull ArrangementEntryWrapper<E> newWrapper, @NotNull ArrangementEntryWrapper<E> oldWrapper, @Nullable ArrangementEntryWrapper<E> previous, @Nullable ArrangementEntryWrapper<E> next, @NotNull Context<E> context) { // Calculate blank lines before the arrangement. int blankLinesBefore = 0; IntList lineFeedOffsets = new IntArrayList(); int oldStartLine = context.document.getLineNumber(oldWrapper.getStartOffset()); if (oldStartLine > 0) { int lastLineFeed = context.document.getLineStartOffset(oldStartLine) - 1; lineFeedOffsets.add(lastLineFeed); for (int i = lastLineFeed - 1 - myParentShift; i >= 0; i--) { i = CharArrayUtil.shiftBackward(myParentText, i, " \t"); if (myParentText.charAt(i) == '\n') { blankLinesBefore++; lineFeedOffsets.add(i + myParentShift); } else { break; } } } ArrangementEntryWrapper<E> parentWrapper = oldWrapper.getParent(); int desiredBlankLinesNumber = getBlankLines(context, parentWrapper, newWrapper, previous, next); if (desiredBlankLinesNumber == blankLinesBefore && newWrapper.equals(oldWrapper)) { return; } String newEntryText = myParentText.substring(newWrapper.getStartOffset() - myParentShift, newWrapper.getEndOffset() - myParentShift); int lineFeedsDiff = desiredBlankLinesNumber - blankLinesBefore; if (lineFeedsDiff == 0 || desiredBlankLinesNumber < 0) { context.addMoveInfo(newWrapper.getStartOffset() - myParentShift, newWrapper.getEndOffset() - myParentShift, oldWrapper.getStartOffset()); context.document.replaceString(oldWrapper.getStartOffset(), oldWrapper.getEndOffset(), newEntryText); return; } if (lineFeedsDiff > 0) { // Insert necessary number of blank lines. StringBuilder buffer = new StringBuilder(StringUtil.repeat("\n", lineFeedsDiff)); buffer.append(newEntryText); context.document.replaceString(oldWrapper.getStartOffset(), oldWrapper.getEndOffset(), buffer); } else { // Cut exceeding blank lines. int replacementStartOffset = lineFeedOffsets.getInt(-lineFeedsDiff) + 1; context.document.replaceString(replacementStartOffset, oldWrapper.getEndOffset(), newEntryText); } // Update wrapper ranges. ArrangementEntryWrapper<E> parent = oldWrapper.getParent(); if (parent == null) { return; } Deque<ArrangementEntryWrapper<E>> parents = new ArrayDeque<>(); do { parents.add(parent); parent.setEndOffset(parent.getEndOffset() + lineFeedsDiff); parent = parent.getParent(); } while (parent != null); while (!parents.isEmpty()) { for (ArrangementEntryWrapper<E> wrapper = parents.removeLast().getNext(); wrapper != null; wrapper = wrapper.getNext()) { wrapper.applyShift(lineFeedsDiff); } } }