public TextBuffer append(TextBuffer buffer, String className, String methodKey) {
    if (buffer.myCurrentGroup != buffer.myRootGroup) {
      throw new IllegalArgumentException("Can't append buffer with non-root group");
    }
    if (buffer.myDebugTrace != null) {
      buffer.myDebugTrace.myPreventDeletion = false;
    }
    if (buffer.myLineToOffsetMapping != null && !buffer.myLineToOffsetMapping.isEmpty()) {
      checkMapCreated();
      for (Map.Entry<Integer, Integer> entry : buffer.myLineToOffsetMapping.entrySet()) {
        myLineToOffsetMapping.put(entry.getKey(), entry.getValue() + myStringBuilder.length());
      }
    }
    buffer.myBytecodeOffsetMapping.forEach((key, value) -> {
      if (key.myClass == null) {
        key = new BytecodeMappingKey(key.myBytecodeOffset, className, methodKey);
      }
      myBytecodeOffsetMapping.putIfAbsent(key, value + myStringBuilder.length());
    });
    NewlineGroup otherRoot = buffer.myRootGroup.copy();
    otherRoot.shift(myStringBuilder.length());
    myCurrentGroup.myReplacements.addAll(otherRoot.myReplacements);
    myCurrentGroup.myTokens.addAll(otherRoot.myTokens);
    myCurrentGroup.myChildren.addAll(otherRoot.myChildren);
    myStringBuilder.append(buffer.myStringBuilder);
    return this;
  }
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