docs: static String makeMessage(ImmutableList<String> messages, ImmutableList<Fact> facts) {
      int longestKeyLength = 0;
      boolean seenNewlineInValue = false;
      for (Fact fact : facts) {
        if (fact.value != null) {
          longestKeyLength = max(longestKeyLength, fact.key.length());
          // TODO(cpovirk): Look for other kinds of newlines.
          seenNewlineInValue |= fact.value.contains("\n");
        }
      }
  
      StringBuilder builder = new StringBuilder();
      for (String message : messages) {
        builder.append(message);
        builder.append('\n');
      }
  
      /*
       * *Usually* the first fact is printed at the beginning of a new line. However, when this
       * exception is the cause of another exception, that exception will print it starting after
       * "Caused by: " on the same line. The other exception sometimes also reuses this message as its
       * own message. In both of those scenarios, the first line doesn't start at column 0, so the
       * horizontal alignment is thrown off.
       *
       * There's not much we can do about this, short of always starting with a newline (which would
       * leave a blank line at the beginning of the message in the normal case).
       */
      for (Fact fact : facts) {
        if (fact.value == null) {
          builder.append(fact.key);
        } else if (seenNewlineInValue) {
          builder.append(fact.key);
          builder.append(":\n");
          builder.append(indent(fact.value));
        } else {
          builder.append(padEnd(fact.key, longestKeyLength, ' '));
          builder.append(": ");
          builder.append(fact.value);
        }
        builder.append('\n');
      }
      builder.setLength(builder.length() - 1); // remove trailing \n
      return builder.toString();
    }