private void processChunk(@Nullable Runnable onAfterDone) {
    long deadline = System.currentTimeMillis() + MAX_UPDATE_DURATION_MS;
    Map<Editor, EditorScrollingPositionKeeper> keepers = new HashMap<>();
    // This is a heuristic to lessen visual 'jumping' on editor opening. We'd like regions visible at target opening location to be updated
    // first, and all the rest - later. We're not specifically optimizing for the case when multiple editors are opened simultaneously now,
    // opening several editors in succession should work fine with this logic though (by the time a new editor is opened, 'high-priority'
    // regions from the previous editor are likely to have been processed already).
    List<CustomFoldRegion> toProcess = new ArrayList<>(myQueue.keySet());
    Object2IntMap<Editor> memoMap = new Object2IntOpenHashMap<>();
    toProcess.sort(Comparator.comparingInt(i -> -Math.abs(i.getStartOffset() - getVisibleOffset(i.getEditor(), memoMap))));
    Map<Editor, List<Runnable>> editorTasks = new HashMap<>();
    do {
      CustomFoldRegion region = toProcess.remove(toProcess.size() - 1);
      boolean updateContent = myQueue.remove(region);
      if (region.isValid()) {
        Editor editor = region.getEditor();
        keepers.computeIfAbsent(editor, e -> {
          EditorScrollingPositionKeeper keeper = new EditorScrollingPositionKeeper(editor);
          keeper.savePosition();
          return keeper;
        });
        var tasks = editorTasks.computeIfAbsent(editor, e -> new ArrayList<>());
        ((DocRenderer)region.getRenderer()).update(true, updateContent, tasks);
        if (tasks.size() > 20) {
          runFoldingTasks(editor, tasks);
        }
      }
    }
    while (!toProcess.isEmpty() && System.currentTimeMillis() < deadline);
    editorTasks.entrySet().forEach((entry -> runFoldingTasks(entry.getKey(), entry.getValue())));
    keepers.values().forEach(k -> k.restorePosition(false));
    if (!myQueue.isEmpty()) SwingUtilities.invokeLater(ThreadContext.captureThreadContext(() -> processChunk(onAfterDone)));
    if (myQueue.isEmpty() && onAfterDone != null) onAfterDone.run();
  }
private void processChunk(@Nullable Runnable onAfterDone)