code: private Tuple3<ReadOnlyStyledDocument<PS, SEG, S>, RichTextChange<PS, SEG, S>, MaterializedListModification<Paragraph<PS, SEG, S>>> replace(
              BiIndex start, BiIndex end, UnaryOperator<ReadOnlyStyledDocument<PS, SEG, S>> f) {
          int pos = tree.getSummaryBetween(0, start.major).map(s -> s.length() + 1).orElse(0) + start.minor;
  
          List<Paragraph<PS, SEG, S>> removedPars =
                  getParagraphs().subList(start.major, end.major + 1);
  
          return end.map(this::split).map((l0, r) -> {
              return start.map(l0::split).map((l, removed) -> {
                  ReadOnlyStyledDocument<PS, SEG, S> replacement = f.apply(removed);
                  ReadOnlyStyledDocument<PS, SEG, S> doc = l.concatR(replacement).concat(r);
                  RichTextChange<PS, SEG, S> change = new RichTextChange<>(pos, removed, replacement);
                  List<Paragraph<PS, SEG, S>> addedPars = doc.getParagraphs().subList(start.major, start.major + replacement.getParagraphCount());
                  MaterializedListModification<Paragraph<PS, SEG, S>> parChange =
                          MaterializedListModification.create(start.major, removedPars, addedPars);
                  return t(doc, change, parChange);
              });
          });
      }
docs: Note: there must be a "ensureValid_()" call preceding the call of this method