private static int @NotNull [] getTargetOffsets(Editor @NotNull [] editors, int[] startLines, int[] endLines, int preferredTopShift) {
    int count = editors.length;
    assert startLines.length == count;
    assert endLines.length == count;

    int[] topOffsets = new int[count];
    int[] bottomOffsets = new int[count];
    int[] rangeHeights = new int[count];
    int[] gapLines = new int[count];
    int[] editorHeights = new int[count];
    int[] maximumOffsets = new int[count];
    int[] topShifts = new int[count];

    for (int i = 0; i < count; i++) {
      topOffsets[i] = editors[i].logicalPositionToXY(new LogicalPosition(startLines[i], 0)).y;
      bottomOffsets[i] = editors[i].logicalPositionToXY(new LogicalPosition(endLines[i] + 1, 0)).y;
      rangeHeights[i] = bottomOffsets[i] - topOffsets[i];

      gapLines[i] = 2 * editors[i].getLineHeight();
      editorHeights[i] = editors[i].getScrollingModel().getVisibleArea().height;

      maximumOffsets[i] = ((EditorEx)editors[i]).getScrollPane().getVerticalScrollBar().getMaximum() - editorHeights[i];

      // 'shift' here - distance between editor's top and first line of range

      // make whole range visible. If possible, locate it at 'center' (1/3 of height) (or at 'preferredTopShift' if it was specified)
      // If can't show whole range - show as much as we can
      boolean canShow = 2 * gapLines[i] + rangeHeights[i] <= editorHeights[i];

      int shift = preferredTopShift != -1 ? preferredTopShift : editorHeights[i] / 3;
      topShifts[i] = canShow ? Math.min(editorHeights[i] - gapLines[i] - rangeHeights[i], shift) : gapLines[i];
    }

    int topShift = ArrayUtil.min(topShifts);

    // check if we're at the top of file
    topShift = Math.min(topShift, ArrayUtil.min(topOffsets));

    int[] offsets = new int[count];
    boolean haveEnoughSpace = true;
    for (int i = 0; i < count; i++) {
      offsets[i] = topOffsets[i] - topShift;
      haveEnoughSpace &= maximumOffsets[i] > offsets[i];
    }

    if (haveEnoughSpace) return offsets;

    // One of the ranges is at end of file - we can't scroll where we want to.
    topShift = 0;
    for (int i = 0; i < count; i++) {
      topShift = Math.max(topOffsets[i] - maximumOffsets[i], topShift);
    }

    for (int i = 0; i < count; i++) {
      // Try to show as much of range as we can (even if it breaks alignment)
      offsets[i] = topOffsets[i] - topShift + Math.max(topShift + rangeHeights[i] + gapLines[i] - editorHeights[i], 0);

      // always show top of the range
      offsets[i] = Math.min(offsets[i], topOffsets[i] - gapLines[i]);
    }

    return offsets;
  }
private static int @NotNull [] getTargetOffsets(Editor @NotNull [] editors, int[] startLines, int[] endLines, int preferredTopShift)