public void reformat() {
    if (myCurrentGroup != myRootGroup) {
      throw new IllegalStateException("Cannot reformat while in a group");
    }
    if (myHasReformatted) {
      throw new IllegalStateException("Cannot reformat twice");
    }
    myHasReformatted = true;

    //myRootGroup.dump("");

    myRootGroup.myLength = myStringBuilder.length();

    List<Integer> offsetMapping = new ArrayList<>(myStringBuilder.length());
    offsetMapping.add(0);
    reformatGroup(myRootGroup, offsetMapping, 0);

    myBytecodeOffsetMapping.replaceAll((key, value) -> value + offsetMapping.get(value));
  }
Recursive reformatting of groups.
If the group's content would take you over the specified preferred line length, then the group is applied.

- This algorithm usually ensures that if a group is applied, then all the group's parents in the tree are also
  applied. This tends to produce a nice structured output with well-placed potential newlines.
- The formatter keeps track of the offset as a result of the reformat at each character in the text buffer, and
  applies them to the bytecode offsets afterwards, so that the line number mappings don't get messed up.
src/org/jetbrains/java/decompiler/util/TextBuffer.java