sclass StructureStringIndenter {
  // how many levels to indent, max
  settable int levels = 100;
  
  // inline lowest levels of char count is not higher than this
  settable int inlineChars = 40;
  
  settable bool verbose;
  
  // internal
  LS tok;
  Map<Int> bracketMap;
  
  S get(S s) {
    if (s == null) null;
    tok = javaTokForStructure(s);
    bracketMap = getBracketMapIncludingAngleBrackets(tok);
    
    // being careful because this.levels might be Int.MAX_VALUE
    int levels = clampToInt(this.levels*2L);
    
    new StringBuilder buf;
    int indent = 0;
    for i over tok: {
      S t = tok.get(i);
      if (isOpeningBracket(t)) {
        Int j = bracketMap.get(i);
        if (j != null && !tokenRangeLongerThanNChars(tok, i+1, j, inlineChars)) {
          buf.append(joinSubList(tok, i, j+1));
          i = j;
        } else {
          if (verbose)
            print("Bracket part longer than " + inlineChars + " chars: " + quote(shortenJoinSubList(inlineChars, tok, i)));
          indent += 2;
          buf.append(t);
          if (indent <= levels)
            buf.append("\n").append(spaces(indent));
        }
      } else if (isClosingBracket(t)) {
        indent -= 2;
        if (indent < levels)
          buf.append("\n").append(spaces(indent));
        buf.append(t);
      } else if (indent <= levels && eq(t, ",")) {
        buf.append(t).append("\n").append(spaces(indent));
        i++;
      } else
        buf.append(t);
    }
    
    ret str(buf);
  }
}