1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1996-2019. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20-module(io_lib_pretty). 21 22%%% Pretty printing Erlang terms 23%%% 24%%% In this module "print" means the formatted printing while "write" 25%%% means just writing out onto one line. 26 27-export([print/1,print/2,print/3,print/4,print/5,print/6]). 28 29%% To be used by io_lib only. 30-export([intermediate/6, write/1]). 31 32%%% 33%%% Exported functions 34%%% 35 36%% print(Term) -> [Chars] 37%% print(Term, Column, LineLength, Depth) -> [Chars] 38%% Depth = -1 gives unlimited print depth. Use io_lib:write for atomic terms. 39 40-spec print(term()) -> io_lib:chars(). 41 42print(Term) -> 43 print(Term, 1, 80, -1). 44 45%% print(Term, RecDefFun) -> [Chars] 46%% print(Term, Depth, RecDefFun) -> [Chars] 47%% RecDefFun = fun(Tag, NoFields) -> [FieldTag] | no 48%% Used by the shell for printing records and for Unicode. 49 50-type rec_print_fun() :: fun((Tag :: atom(), NFields :: non_neg_integer()) -> 51 'no' | [FieldName :: atom()]). 52-type column() :: integer(). 53-type encoding() :: epp:source_encoding() | 'unicode'. 54-type line_length() :: pos_integer(). 55-type depth() :: integer(). 56-type line_max_chars() :: integer(). 57-type chars_limit() :: integer(). 58 59-type chars() :: io_lib:chars(). 60-type option() :: {'chars_limit', chars_limit()} 61 | {'column', column()} 62 | {'depth', depth()} 63 | {'encoding', encoding()} 64 | {'line_length', line_length()} 65 | {'line_max_chars', line_max_chars()} 66 | {'record_print_fun', rec_print_fun()} 67 | {'strings', boolean()}. 68-type options() :: [option()]. 69 70-spec print(term(), rec_print_fun()) -> chars(); 71 (term(), options()) -> chars(). 72 73print(Term, Options) when is_list(Options) -> 74 Col = get_option(column, Options, 1), 75 Ll = get_option(line_length, Options, 80), 76 D = get_option(depth, Options, -1), 77 M = get_option(line_max_chars, Options, -1), 78 T = get_option(chars_limit, Options, -1), 79 RecDefFun = get_option(record_print_fun, Options, no_fun), 80 Encoding = get_option(encoding, Options, epp:default_encoding()), 81 Strings = get_option(strings, Options, true), 82 print(Term, Col, Ll, D, M, T, RecDefFun, Encoding, Strings); 83print(Term, RecDefFun) -> 84 print(Term, -1, RecDefFun). 85 86-spec print(term(), depth(), rec_print_fun()) -> chars(). 87 88print(Term, Depth, RecDefFun) -> 89 print(Term, 1, 80, Depth, RecDefFun). 90 91-spec print(term(), column(), line_length(), depth()) -> chars(). 92 93print(Term, Col, Ll, D) -> 94 print(Term, Col, Ll, D, _M=-1, _T=-1, no_fun, latin1, true). 95 96-spec print(term(), column(), line_length(), depth(), rec_print_fun()) -> 97 chars(). 98print(Term, Col, Ll, D, RecDefFun) -> 99 print(Term, Col, Ll, D, _M=-1, RecDefFun). 100 101-spec print(term(), column(), line_length(), depth(), line_max_chars(), 102 rec_print_fun()) -> chars(). 103 104print(Term, Col, Ll, D, M, RecDefFun) -> 105 print(Term, Col, Ll, D, M, _T=-1, RecDefFun, latin1, true). 106 107%% D = Depth, default -1 (infinite), or LINEMAX=30 when printing from shell 108%% T = chars_limit, that is, maximal number of characters, default -1 109%% Used together with D to limit the output. It is possible that 110%% more than T characters are returned. 111%% Col = current column, default 1 112%% Ll = line length/~p field width, default 80 113%% M = CHAR_MAX (-1 if no max, 60 when printing from shell) 114print(_, _, _, 0, _M, _T, _RF, _Enc, _Str) -> "..."; 115print(_, _, _, _D, _M, 0, _RF, _Enc, _Str) -> "..."; 116print(Term, Col, Ll, D, M, T, RecDefFun, Enc, Str) when Col =< 0 -> 117 %% ensure Col is at least 1 118 print(Term, 1, Ll, D, M, T, RecDefFun, Enc, Str); 119print(Atom, _Col, _Ll, _D, _M, _T, _RF, Enc, _Str) when is_atom(Atom) -> 120 write_atom(Atom, Enc); 121print(Term, Col, Ll, D, M0, T, RecDefFun, Enc, Str) when is_tuple(Term); 122 is_list(Term); 123 is_map(Term); 124 is_bitstring(Term) -> 125 %% preprocess and compute total number of chars 126 {_, Len, _Dots, _} = If = 127 case T < 0 of 128 true -> print_length(Term, D, T, RecDefFun, Enc, Str); 129 false -> intermediate(Term, D, T, RecDefFun, Enc, Str) 130 end, 131 %% use Len as CHAR_MAX if M0 = -1 132 M = max_cs(M0, Len), 133 if 134 Ll =:= 0 -> 135 write(If); 136 Len < Ll - Col, Len =< M -> 137 %% write the whole thing on a single line when there is room 138 write(If); 139 true -> 140 %% compute the indentation TInd for tagged tuples and records 141 TInd = while_fail([-1, 4], 142 fun(I) -> cind(If, Col, Ll, M, I, 0, 0) end, 143 1), 144 pp(If, Col, Ll, M, TInd, indent(Col), 0, 0) 145 end; 146print(Term, _Col, _Ll, _D, _M, _T, _RF, _Enc, _Str) -> 147 %% atomic data types (bignums, atoms, ...) are never truncated 148 io_lib:write(Term). 149 150%%% 151%%% Local functions 152%%% 153 154%% use M only if nonnegative, otherwise use Len as default value 155max_cs(M, Len) when M < 0 -> 156 Len; 157max_cs(M, _Len) -> 158 M. 159 160-define(ATM(T), is_list(element(1, T))). 161-define(ATM_PAIR(Pair), 162 ?ATM(element(2, element(1, Pair))) % Key 163 andalso 164 ?ATM(element(3, element(1, Pair)))). % Value 165-define(ATM_FLD(Field), ?ATM(element(4, element(1, Field)))). 166 167pp({_S,Len,_,_} = If, Col, Ll, M, _TInd, _Ind, LD, W) 168 when Len < Ll - Col - LD, Len + W + LD =< M -> 169 write(If); 170pp({{list,L}, _Len, _, _}, Col, Ll, M, TInd, Ind, LD, W) -> 171 [$[, pp_list(L, Col + 1, Ll, M, TInd, indent(1, Ind), LD, $|, W + 1), $]]; 172pp({{tuple,true,L}, _Len, _, _}, Col, Ll, M, TInd, Ind, LD, W) -> 173 [${, pp_tag_tuple(L, Col, Ll, M, TInd, Ind, LD, W + 1), $}]; 174pp({{tuple,false,L}, _Len, _, _}, Col, Ll, M, TInd, Ind, LD, W) -> 175 [${, pp_list(L, Col + 1, Ll, M, TInd, indent(1, Ind), LD, $,, W + 1), $}]; 176pp({{map,Pairs}, _Len, _, _}, Col, Ll, M, TInd, Ind, LD, W) -> 177 [$#, ${, pp_map(Pairs, Col + 2, Ll, M, TInd, indent(2, Ind), LD, W + 1), 178 $}]; 179pp({{record,[{Name,NLen} | L]}, _Len, _, _}, Col, Ll, M, TInd, Ind, LD, W) -> 180 [Name, ${, pp_record(L, NLen, Col, Ll, M, TInd, Ind, LD, W + NLen+1), $}]; 181pp({{bin,S}, _Len, _, _}, Col, Ll, M, _TInd, Ind, LD, W) -> 182 pp_binary(S, Col + 2, Ll, M, indent(2, Ind), LD, W); 183pp({S,_Len,_,_}, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) -> 184 S. 185 186%% Print a tagged tuple by indenting the rest of the elements 187%% differently to the tag. Tuple has size >= 2. 188pp_tag_tuple({dots, _, _, _}, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) -> 189 "..."; 190pp_tag_tuple([{Tag,Tlen,_,_} | L], Col, Ll, M, TInd, Ind, LD, W) -> 191 %% this uses TInd 192 TagInd = Tlen + 2, 193 Tcol = Col + TagInd, 194 S = $,, 195 if 196 TInd > 0, TagInd > TInd -> 197 Col1 = Col + TInd, 198 Indent = indent(TInd, Ind), 199 [Tag|pp_tail(L, Col1, Tcol, Ll, M, TInd, Indent, LD, S, W+Tlen)]; 200 true -> 201 Indent = indent(TagInd, Ind), 202 [Tag, S | pp_list(L, Tcol, Ll, M, TInd, Indent, LD, S, W+Tlen+1)] 203 end. 204 205pp_map([], _Col, _Ll, _M, _TInd, _Ind, _LD, _W) -> 206 ""; % cannot happen 207pp_map({dots, _, _, _}, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) -> 208 "..."; % cannot happen 209pp_map([P | Ps], Col, Ll, M, TInd, Ind, LD, W) -> 210 {PS, PW} = pp_pair(P, Col, Ll, M, TInd, Ind, last_depth(Ps, LD), W), 211 [PS | pp_pairs_tail(Ps, Col, Col + PW, Ll, M, TInd, Ind, LD, PW)]. 212 213pp_pairs_tail([], _Col0, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) -> 214 ""; 215pp_pairs_tail({dots, _, _, _}, _Col0, _Col, _M, _Ll, _TInd, _Ind, _LD, _W) -> 216 ",..."; 217pp_pairs_tail([{_, Len, _, _}=P | Ps], Col0, Col, Ll, M, TInd, Ind, LD, W) -> 218 LD1 = last_depth(Ps, LD), 219 ELen = 1 + Len, 220 if 221 LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM_PAIR(P); 222 LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM_PAIR(P) -> 223 [$,, write_pair(P) | 224 pp_pairs_tail(Ps, Col0, Col+ELen, Ll, M, TInd, Ind, LD, W+ELen)]; 225 true -> 226 {PS, PW} = pp_pair(P, Col0, Ll, M, TInd, Ind, LD1, 0), 227 [$,, $\n, Ind, PS | 228 pp_pairs_tail(Ps, Col0, Col0 + PW, Ll, M, TInd, Ind, LD, PW)] 229 end. 230 231pp_pair({_, Len, _, _}=Pair, Col, Ll, M, _TInd, _Ind, LD, W) 232 when Len < Ll - Col - LD, Len + W + LD =< M -> 233 {write_pair(Pair), if 234 ?ATM_PAIR(Pair) -> 235 Len; 236 true -> 237 Ll % force nl 238 end}; 239pp_pair({{map_pair, K, V}, _Len, _, _}, Col0, Ll, M, TInd, Ind0, LD, W) -> 240 I = map_value_indent(TInd), 241 Ind = indent(I, Ind0), 242 {[pp(K, Col0, Ll, M, TInd, Ind0, LD, W), " =>\n", 243 Ind | pp(V, Col0 + I, Ll, M, TInd, Ind, LD, 0)], Ll}. % force nl 244 245pp_record([], _Nlen, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) -> 246 ""; 247pp_record({dots, _, _, _}, _Nlen, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) -> 248 "..."; 249pp_record([F | Fs], Nlen, Col0, Ll, M, TInd, Ind0, LD, W0) -> 250 Nind = Nlen + 1, 251 {Col, Ind, S, W} = rec_indent(Nind, TInd, Col0, Ind0, W0), 252 {FS, FW} = pp_field(F, Col, Ll, M, TInd, Ind, last_depth(Fs, LD), W), 253 [S, FS | pp_fields_tail(Fs, Col, Col + FW, Ll, M, TInd, Ind, LD, W + FW)]. 254 255pp_fields_tail([], _Col0, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) -> 256 ""; 257pp_fields_tail({dots, _, _ ,_}, _Col0, _Col, _M, _Ll, _TInd, _Ind, _LD, _W) -> 258 ",..."; 259pp_fields_tail([{_, Len, _, _}=F | Fs], Col0, Col, Ll, M, TInd, Ind, LD, W) -> 260 LD1 = last_depth(Fs, LD), 261 ELen = 1 + Len, 262 if 263 LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM_FLD(F); 264 LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM_FLD(F) -> 265 [$,, write_field(F) | 266 pp_fields_tail(Fs, Col0, Col+ELen, Ll, M, TInd, Ind, LD, W+ELen)]; 267 true -> 268 {FS, FW} = pp_field(F, Col0, Ll, M, TInd, Ind, LD1, 0), 269 [$,, $\n, Ind, FS | 270 pp_fields_tail(Fs, Col0, Col0 + FW, Ll, M, TInd, Ind, LD, FW)] 271 end. 272 273pp_field({_, Len, _, _}=Fl, Col, Ll, M, _TInd, _Ind, LD, W) 274 when Len < Ll - Col - LD, Len + W + LD =< M -> 275 {write_field(Fl), if 276 ?ATM_FLD(Fl) -> 277 Len; 278 true -> 279 Ll % force nl 280 end}; 281pp_field({{field, Name, NameL, F},_,_, _}, Col0, Ll, M, TInd, Ind0, LD, W0) -> 282 {Col, Ind, S, W} = rec_indent(NameL, TInd, Col0, Ind0, W0 + NameL), 283 Sep = case S of 284 [$\n | _] -> " ="; 285 _ -> " = " 286 end, 287 {[Name, Sep, S | pp(F, Col, Ll, M, TInd, Ind, LD, W)], Ll}. % force nl 288 289rec_indent(RInd, TInd, Col0, Ind0, W0) -> 290 %% this uses TInd 291 Nl = (TInd > 0) and (RInd > TInd), 292 DCol = case Nl of 293 true -> TInd; 294 false -> RInd 295 end, 296 Col = Col0 + DCol, 297 Ind = indent(DCol, Ind0), 298 S = case Nl of 299 true -> [$\n | Ind]; 300 false -> "" 301 end, 302 W = case Nl of 303 true -> 0; 304 false -> W0 305 end, 306 {Col, Ind, S, W}. 307 308pp_list({dots, _, _, _}, _Col0, _Ll, _M, _TInd, _Ind, _LD, _S, _W) -> 309 "..."; 310pp_list([E | Es], Col0, Ll, M, TInd, Ind, LD, S, W) -> 311 {ES, WE} = pp_element(E, Col0, Ll, M, TInd, Ind, last_depth(Es, LD), W), 312 [ES | pp_tail(Es, Col0, Col0 + WE, Ll, M, TInd, Ind, LD, S, W + WE)]. 313 314pp_tail([], _Col0, _Col, _Ll, _M, _TInd, _Ind, _LD, _S, _W) -> 315 []; 316pp_tail([{_, Len, _, _}=E | Es], Col0, Col, Ll, M, TInd, Ind, LD, S, W) -> 317 LD1 = last_depth(Es, LD), 318 ELen = 1 + Len, 319 if 320 LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM(E); 321 LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM(E) -> 322 [$,, write(E) | 323 pp_tail(Es, Col0, Col + ELen, Ll, M, TInd, Ind, LD, S, W+ELen)]; 324 true -> 325 {ES, WE} = pp_element(E, Col0, Ll, M, TInd, Ind, LD1, 0), 326 [$,, $\n, Ind, ES | 327 pp_tail(Es, Col0, Col0 + WE, Ll, M, TInd, Ind, LD, S, WE)] 328 end; 329pp_tail({dots, _, _, _}, _Col0, _Col, _Ll, _M, _TInd, _Ind, _LD, S, _W) -> 330 [S | "..."]; 331pp_tail({_, Len, _, _}=E, _Col0, Col, Ll, M, _TInd, _Ind, LD, S, W) 332 when Len + 1 < Ll - Col - (LD + 1), 333 Len + 1 + W + (LD + 1) =< M, 334 ?ATM(E) -> 335 [S | write(E)]; 336pp_tail(E, Col0, _Col, Ll, M, TInd, Ind, LD, S, _W) -> 337 [S, $\n, Ind | pp(E, Col0, Ll, M, TInd, Ind, LD + 1, 0)]. 338 339pp_element({_, Len, _, _}=E, Col, Ll, M, _TInd, _Ind, LD, W) 340 when Len < Ll - Col - LD, Len + W + LD =< M, ?ATM(E) -> 341 {write(E), Len}; 342pp_element(E, Col, Ll, M, TInd, Ind, LD, W) -> 343 {pp(E, Col, Ll, M, TInd, Ind, LD, W), Ll}. % force nl 344 345%% Reuse the list created by io_lib:write_binary()... 346pp_binary([LT,LT,S,GT,GT], Col, Ll, M, Ind, LD, W) -> 347 N = erlang:max(8, erlang:min(Ll - Col, M - 4 - W) - LD), 348 [LT,LT,pp_binary(S, N, N, Ind),GT,GT]. 349 350pp_binary([BS, $, | S], N, N0, Ind) -> 351 Len = length(BS) + 1, 352 case N - Len of 353 N1 when N1 < 0 -> 354 [$\n, Ind, BS, $, | pp_binary(S, N0 - Len, N0, Ind)]; 355 N1 -> 356 [BS, $, | pp_binary(S, N1, N0, Ind)] 357 end; 358pp_binary([BS1, $:, BS2]=S, N, _N0, Ind) 359 when length(BS1) + length(BS2) + 1 > N -> 360 [$\n, Ind, S]; 361pp_binary(S, N, _N0, Ind) -> 362 case iolist_size(S) > N of 363 true -> 364 [$\n, Ind, S]; 365 false -> 366 S 367 end. 368 369%% write the whole thing on a single line 370write({{tuple, _IsTagged, L}, _, _, _}) -> 371 [${, write_list(L, $,), $}]; 372write({{list, L}, _, _, _}) -> 373 [$[, write_list(L, $|), $]]; 374write({{map, Pairs}, _, _, _}) -> 375 [$#,${, write_list(Pairs, $,), $}]; 376write({{map_pair, _K, _V}, _, _, _}=Pair) -> 377 write_pair(Pair); 378write({{record, [{Name,_} | L]}, _, _, _}) -> 379 [Name, ${, write_fields(L), $}]; 380write({{bin, S}, _, _, _}) -> 381 S; 382write({S, _, _, _}) -> 383 S. 384 385write_pair({{map_pair, K, V}, _, _, _}) -> 386 [write(K), " => ", write(V)]. 387 388write_fields([]) -> 389 ""; 390write_fields({dots, _, _, _}) -> 391 "..."; 392write_fields([F | Fs]) -> 393 [write_field(F) | write_fields_tail(Fs)]. 394 395write_fields_tail([]) -> 396 ""; 397write_fields_tail({dots, _, _, _}) -> 398 ",..."; 399write_fields_tail([F | Fs]) -> 400 [$,, write_field(F) | write_fields_tail(Fs)]. 401 402write_field({{field, Name, _NameL, F}, _, _, _}) -> 403 [Name, " = " | write(F)]. 404 405write_list({dots, _, _, _}, _S) -> 406 "..."; 407write_list([E | Es], S) -> 408 [write(E) | write_tail(Es, S)]. 409 410write_tail([], _S) -> 411 []; 412write_tail([E | Es], S) -> 413 [$,, write(E) | write_tail(Es, S)]; 414write_tail({dots, _, _, _}, S) -> 415 [S | "..."]; 416write_tail(E, S) -> 417 [S | write(E)]. 418 419-type more() :: fun((chars_limit(), DeltaDepth :: non_neg_integer()) -> 420 intermediate_format()). 421 422-type if_list() :: maybe_improper_list(intermediate_format(), 423 {'dots', non_neg_integer(), 424 non_neg_integer(), more()}). 425 426-type intermediate_format() :: 427 {chars() 428 | {'bin', chars()} 429 | 'dots' 430 | {'field', Name :: chars(), NameLen :: non_neg_integer(), 431 intermediate_format()} 432 | {'list', if_list()} 433 | {'map', if_list()} 434 | {'map_pair', K :: intermediate_format(), 435 V :: intermediate_format()} 436 | {'record', [{Name :: chars(), NameLen :: non_neg_integer()} 437 | if_list()]} 438 | {'tuple', IsTagged :: boolean(), if_list()}, 439 Len :: non_neg_integer(), 440 NumOfDots :: non_neg_integer(), 441 More :: more() | 'no_more' 442 }. 443 444-spec intermediate(term(), depth(), pos_integer(), rec_print_fun(), 445 encoding(), boolean()) -> intermediate_format(). 446 447intermediate(Term, D, T, RF, Enc, Str) when T > 0 -> 448 D0 = 1, 449 If = print_length(Term, D0, T, RF, Enc, Str), 450 case If of 451 {_, Len, Dots, _} when Dots =:= 0; Len > T; D =:= 1 -> 452 If; 453 {_, Len, _, _} -> 454 find_upper(If, Term, T, D0, 2, D, RF, Enc, Str, Len) 455 end. 456 457find_upper(Lower, Term, T, Dl, Dd, D, RF, Enc, Str, LastLen) -> 458 Dd2 = Dd * 2, 459 D1 = case D < 0 of 460 true -> Dl + Dd2; 461 false -> min(Dl + Dd2, D) 462 end, 463 If = expand(Lower, T, D1 - Dl), 464 case If of 465 {_, _, _Dots=0, _} -> % even if Len > T 466 If; 467 {_, LastLen, _, _} -> 468 %% Cannot happen if print_length() is free of bugs. 469 If; 470 {_, Len, _, _} when Len =< T, D1 < D orelse D < 0 -> 471 find_upper(If, Term, T, D1, Dd2, D, RF, Enc, Str, Len); 472 _ -> 473 search_depth(Lower, If, Term, T, Dl, D1, RF, Enc, Str) 474 end. 475 476%% Lower has NumOfDots > 0 and Len =< T. 477%% Upper has NumOfDots > 0 and Len > T. 478search_depth(Lower, Upper, _Term, T, Dl, Du, _RF, _Enc, _Str) 479 when Du - Dl =:= 1 -> 480 %% The returned intermediate format has Len >= T. 481 case Lower of 482 {_, T, _, _} -> 483 Lower; 484 _ -> 485 Upper 486 end; 487search_depth(Lower, Upper, Term, T, Dl, Du, RF, Enc, Str) -> 488 D1 = (Dl + Du) div 2, 489 If = expand(Lower, T, D1 - Dl), 490 case If of 491 {_, Len, _, _} when Len > T -> 492 %% Len can be greater than Upper's length. 493 %% This is a bit expensive since the work to 494 %% crate Upper is wasted. It is the price 495 %% to pay to get a more balanced output. 496 search_depth(Lower, If, Term, T, Dl, D1, RF, Enc, Str); 497 _ -> 498 search_depth(If, Upper, Term, T, D1, Du, RF, Enc, Str) 499 end. 500 501%% The depth (D) is used for extracting and counting the characters to 502%% print. The structure is kept so that the returned intermediate 503%% format can be formatted. The separators (list, tuple, record, map) are 504%% counted but need to be added later. 505 506%% D =/= 0 507print_length([], _D, _T, _RF, _Enc, _Str) -> 508 {"[]", 2, 0, no_more}; 509print_length({}, _D, _T, _RF, _Enc, _Str) -> 510 {"{}", 2, 0, no_more}; 511print_length(#{}=M, _D, _T, _RF, _Enc, _Str) when map_size(M) =:= 0 -> 512 {"#{}", 3, 0, no_more}; 513print_length(Atom, _D, _T, _RF, Enc, _Str) when is_atom(Atom) -> 514 S = write_atom(Atom, Enc), 515 {S, io_lib:chars_length(S), 0, no_more}; 516print_length(List, D, T, RF, Enc, Str) when is_list(List) -> 517 %% only flat lists are "printable" 518 case Str andalso printable_list(List, D, T, Enc) of 519 true -> 520 %% print as string, escaping double-quotes in the list 521 S = write_string(List, Enc), 522 {S, io_lib:chars_length(S), 0, no_more}; 523 {true, Prefix} -> 524 %% Truncated lists when T < 0 could break some existing code. 525 S = write_string(Prefix, Enc), 526 %% NumOfDots = 0 to avoid looping--increasing the depth 527 %% does not make Prefix longer. 528 {[S | "..."], 3 + io_lib:chars_length(S), 0, no_more}; 529 false -> 530 case print_length_list(List, D, T, RF, Enc, Str) of 531 {What, Len, Dots, _More} when Dots > 0 -> 532 More = fun(T1, Dd) -> 533 ?FUNCTION_NAME(List, D+Dd, T1, RF, Enc, Str) 534 end, 535 {What, Len, Dots, More}; 536 If -> 537 If 538 end 539 end; 540print_length(Fun, _D, _T, _RF, _Enc, _Str) when is_function(Fun) -> 541 S = io_lib:write(Fun), 542 {S, iolist_size(S), 0, no_more}; 543print_length(R, D, T, RF, Enc, Str) when is_atom(element(1, R)), 544 is_function(RF) -> 545 case RF(element(1, R), tuple_size(R) - 1) of 546 no -> 547 print_length_tuple(R, D, T, RF, Enc, Str); 548 RDefs -> 549 print_length_record(R, D, T, RF, RDefs, Enc, Str) 550 end; 551print_length(Tuple, D, T, RF, Enc, Str) when is_tuple(Tuple) -> 552 print_length_tuple(Tuple, D, T, RF, Enc, Str); 553print_length(Map, D, T, RF, Enc, Str) when is_map(Map) -> 554 print_length_map(Map, D, T, RF, Enc, Str); 555print_length(<<>>, _D, _T, _RF, _Enc, _Str) -> 556 {"<<>>", 4, 0, no_more}; 557print_length(<<_/bitstring>> = Bin, 1, _T, RF, Enc, Str) -> 558 More = fun(T1, Dd) -> ?FUNCTION_NAME(Bin, 1+Dd, T1, RF, Enc, Str) end, 559 {"<<...>>", 7, 3, More}; 560print_length(<<_/bitstring>> = Bin, D, T, RF, Enc, Str) -> 561 D1 = D - 1, 562 case 563 Str andalso 564 (bit_size(Bin) rem 8) =:= 0 andalso 565 printable_bin0(Bin, D1, tsub(T, 6), Enc) 566 of 567 {true, List} when is_list(List) -> 568 S = io_lib:write_string(List, $"), %" 569 {[$<,$<,S,$>,$>], 4 + length(S), 0, no_more}; 570 {false, List} when is_list(List) -> 571 S = io_lib:write_string(List, $"), %" 572 {[$<,$<,S,"/utf8>>"], 9 + io_lib:chars_length(S), 0, no_more}; 573 {true, true, Prefix} -> 574 S = io_lib:write_string(Prefix, $"), %" 575 More = fun(T1, Dd) -> 576 ?FUNCTION_NAME(Bin, D+Dd, T1, RF, Enc, Str) 577 end, 578 {[$<,$<,S|"...>>"], 7 + length(S), 3, More}; 579 {false, true, Prefix} -> 580 S = io_lib:write_string(Prefix, $"), %" 581 More = fun(T1, Dd) -> 582 ?FUNCTION_NAME(Bin, D+Dd, T1, RF, Enc, Str) 583 end, 584 {[$<,$<,S|"/utf8...>>"], 12 + io_lib:chars_length(S), 3, More}; 585 false -> 586 case io_lib:write_binary(Bin, D, T) of 587 {S, <<>>} -> 588 {{bin, S}, iolist_size(S), 0, no_more}; 589 {S, _Rest} -> 590 More = fun(T1, Dd) -> 591 ?FUNCTION_NAME(Bin, D+Dd, T1, RF, Enc, Str) 592 end, 593 {{bin, S}, iolist_size(S), 3, More} 594 end 595 end; 596print_length(Term, _D, _T, _RF, _Enc, _Str) -> 597 S = io_lib:write(Term), 598 %% S can contain unicode, so iolist_size(S) cannot be used here 599 {S, io_lib:chars_length(S), 0, no_more}. 600 601print_length_map(Map, 1, _T, RF, Enc, Str) -> 602 More = fun(T1, Dd) -> ?FUNCTION_NAME(Map, 1+Dd, T1, RF, Enc, Str) end, 603 {"#{...}", 6, 3, More}; 604print_length_map(Map, D, T, RF, Enc, Str) when is_map(Map) -> 605 Next = maps:next(maps:iterator(Map)), 606 PairsS = print_length_map_pairs(Next, D, D - 1, tsub(T, 3), RF, Enc, Str), 607 {Len, Dots} = list_length(PairsS, 3, 0), 608 {{map, PairsS}, Len, Dots, no_more}. 609 610print_length_map_pairs(none, _D, _D0, _T, _RF, _Enc, _Str) -> 611 []; 612print_length_map_pairs(Term, D, D0, T, RF, Enc, Str) when D =:= 1; T =:= 0-> 613 More = fun(T1, Dd) -> 614 ?FUNCTION_NAME(Term, D+Dd, D0, T1, RF, Enc, Str) 615 end, 616 {dots, 3, 3, More}; 617print_length_map_pairs({K, V, Iter}, D, D0, T, RF, Enc, Str) -> 618 Next = maps:next(Iter), 619 T1 = case Next =:= none of 620 false -> tsub(T, 1); 621 true -> T 622 end, 623 Pair1 = print_length_map_pair(K, V, D0, T1, RF, Enc, Str), 624 {_, Len1, _, _} = Pair1, 625 [Pair1 | 626 print_length_map_pairs(Next, D - 1, D0, tsub(T1, Len1), RF, Enc, Str)]. 627 628print_length_map_pair(K, V, D, T, RF, Enc, Str) -> 629 {_, KL, KD, _} = P1 = print_length(K, D, T, RF, Enc, Str), 630 KL1 = KL + 4, 631 {_, VL, VD, _} = P2 = print_length(V, D, tsub(T, KL1), RF, Enc, Str), 632 {{map_pair, P1, P2}, KL1 + VL, KD + VD, no_more}. 633 634print_length_tuple(Tuple, 1, _T, RF, Enc, Str) -> 635 More = fun(T1, Dd) -> ?FUNCTION_NAME(Tuple, 1+Dd, T1, RF, Enc, Str) end, 636 {"{...}", 5, 3, More}; 637print_length_tuple(Tuple, D, T, RF, Enc, Str) -> 638 L = print_length_tuple1(Tuple, 1, D, tsub(T, 2), RF, Enc, Str), 639 IsTagged = is_atom(element(1, Tuple)) and (tuple_size(Tuple) > 1), 640 {Len, Dots} = list_length(L, 2, 0), 641 {{tuple,IsTagged,L}, Len, Dots, no_more}. 642 643print_length_tuple1(Tuple, I, _D, _T, _RF, _Enc, _Str) 644 when I > tuple_size(Tuple) -> 645 []; 646print_length_tuple1(Tuple, I, D, T, RF, Enc, Str) when D =:= 1; T =:= 0-> 647 More = fun(T1, Dd) -> ?FUNCTION_NAME(Tuple, I, D+Dd, T1, RF, Enc, Str) end, 648 {dots, 3, 3, More}; 649print_length_tuple1(Tuple, I, D, T, RF, Enc, Str) -> 650 E = element(I, Tuple), 651 T1 = case I =:= tuple_size(Tuple) of 652 false -> tsub(T, 1); 653 true -> T 654 end, 655 {_, Len1, _, _} = Elem1 = print_length(E, D - 1, T1, RF, Enc, Str), 656 T2 = tsub(T1, Len1), 657 [Elem1 | print_length_tuple1(Tuple, I + 1, D - 1, T2, RF, Enc, Str)]. 658 659print_length_record(Tuple, 1, _T, RF, RDefs, Enc, Str) -> 660 More = fun(T1, Dd) -> 661 ?FUNCTION_NAME(Tuple, 1+Dd, T1, RF, RDefs, Enc, Str) 662 end, 663 {"{...}", 5, 3, More}; 664print_length_record(Tuple, D, T, RF, RDefs, Enc, Str) -> 665 Name = [$# | write_atom(element(1, Tuple), Enc)], 666 NameL = io_lib:chars_length(Name), 667 T1 = tsub(T, NameL+2), 668 L = print_length_fields(RDefs, D - 1, T1, Tuple, 2, RF, Enc, Str), 669 {Len, Dots} = list_length(L, NameL + 2, 0), 670 {{record, [{Name,NameL} | L]}, Len, Dots, no_more}. 671 672print_length_fields([], _D, _T, Tuple, I, _RF, _Enc, _Str) 673 when I > tuple_size(Tuple) -> 674 []; 675print_length_fields(Term, D, T, Tuple, I, RF, Enc, Str) 676 when D =:= 1; T =:= 0 -> 677 More = fun(T1, Dd) -> 678 ?FUNCTION_NAME(Term, D+Dd, T1, Tuple, I, RF, Enc, Str) 679 end, 680 {dots, 3, 3, More}; 681print_length_fields([Def | Defs], D, T, Tuple, I, RF, Enc, Str) -> 682 E = element(I, Tuple), 683 T1 = case I =:= tuple_size(Tuple) of 684 false -> tsub(T, 1); 685 true -> T 686 end, 687 Field1 = print_length_field(Def, D - 1, T1, E, RF, Enc, Str), 688 {_, Len1, _, _} = Field1, 689 T2 = tsub(T1, Len1), 690 [Field1 | 691 print_length_fields(Defs, D - 1, T2, Tuple, I + 1, RF, Enc, Str)]. 692 693print_length_field(Def, D, T, E, RF, Enc, Str) -> 694 Name = write_atom(Def, Enc), 695 NameL = io_lib:chars_length(Name) + 3, 696 {_, Len, Dots, _} = 697 Field = print_length(E, D, tsub(T, NameL), RF, Enc, Str), 698 {{field, Name, NameL, Field}, NameL + Len, Dots, no_more}. 699 700print_length_list(List, D, T, RF, Enc, Str) -> 701 L = print_length_list1(List, D, tsub(T, 2), RF, Enc, Str), 702 {Len, Dots} = list_length(L, 2, 0), 703 {{list, L}, Len, Dots, no_more}. 704 705print_length_list1([], _D, _T, _RF, _Enc, _Str) -> 706 []; 707print_length_list1(Term, D, T, RF, Enc, Str) when D =:= 1; T =:= 0-> 708 More = fun(T1, Dd) -> ?FUNCTION_NAME(Term, D+Dd, T1, RF, Enc, Str) end, 709 {dots, 3, 3, More}; 710print_length_list1([E | Es], D, T, RF, Enc, Str) -> 711 %% If E is the last element in list, don't account length for a comma. 712 T1 = case Es =:= [] of 713 false -> tsub(T, 1); 714 true -> T 715 end, 716 {_, Len1, _, _} = Elem1 = print_length(E, D - 1, T1, RF, Enc, Str), 717 [Elem1 | print_length_list1(Es, D - 1, tsub(T1, Len1), RF, Enc, Str)]; 718print_length_list1(E, D, T, RF, Enc, Str) -> 719 print_length(E, D - 1, T, RF, Enc, Str). 720 721list_length([], Acc, DotsAcc) -> 722 {Acc, DotsAcc}; 723list_length([{_, Len, Dots, _} | Es], Acc, DotsAcc) -> 724 list_length_tail(Es, Acc + Len, DotsAcc + Dots); 725list_length({_, Len, Dots, _}, Acc, DotsAcc) -> 726 {Acc + Len, DotsAcc + Dots}. 727 728list_length_tail([], Acc, DotsAcc) -> 729 {Acc, DotsAcc}; 730list_length_tail([{_, Len, Dots, _} | Es], Acc, DotsAcc) -> 731 list_length_tail(Es, Acc + 1 + Len, DotsAcc + Dots); 732list_length_tail({_, Len, Dots, _}, Acc, DotsAcc) -> 733 {Acc + 1 + Len, DotsAcc + Dots}. 734 735%% ?CHARS printable characters has depth 1. 736-define(CHARS, 4). 737 738%% only flat lists are "printable" 739printable_list(_L, 1, _T, _Enc) -> 740 false; 741printable_list(L, _D, T, latin1) when T < 0 -> 742 io_lib:printable_latin1_list(L); 743printable_list(L, _D, T, latin1) when T >= 0 -> 744 N = tsub(T, 2), 745 case printable_latin1_list(L, N) of 746 all -> 747 true; 748 0 -> 749 {L1, _} = lists:split(N, L), 750 {true, L1}; 751 _NC -> 752 false 753 end; 754printable_list(L, _D, T, _Unicode) when T >= 0 -> 755 N = tsub(T, 2), 756 %% Be careful not to traverse more of L than necessary. 757 try string:slice(L, 0, N) of 758 "" -> 759 false; 760 Prefix -> 761 case is_flat(L, lists:flatlength(Prefix)) of 762 true -> 763 case string:equal(Prefix, L) of 764 true -> 765 io_lib:printable_list(L); 766 false -> 767 io_lib:printable_list(Prefix) 768 andalso {true, Prefix} 769 end; 770 false -> 771 false 772 end 773 catch _:_ -> false 774 end; 775printable_list(L, _D, T, _Uni) when T < 0-> 776 io_lib:printable_list(L). 777 778is_flat(_L, 0) -> 779 true; 780is_flat([C|Cs], N) when is_integer(C) -> 781 is_flat(Cs, N - 1); 782is_flat(_, _N) -> 783 false. 784 785printable_bin0(Bin, D, T, Enc) -> 786 Len = case D >= 0 of 787 true -> 788 %% Use byte_size() also if Enc =/= latin1. 789 DChars = erlang:min(?CHARS * D, byte_size(Bin)), 790 case T >= 0 of 791 true -> 792 erlang:min(T, DChars); 793 false -> 794 DChars 795 end; 796 false when T < 0 -> 797 byte_size(Bin); 798 false when T >= 0 -> % cannot happen 799 T 800 end, 801 printable_bin(Bin, Len, D, Enc). 802 803printable_bin(_Bin, 0, _D, _Enc) -> 804 false; 805printable_bin(Bin, Len, D, latin1) -> 806 N = erlang:min(20, Len), 807 L = binary_to_list(Bin, 1, N), 808 case printable_latin1_list(L, N) of 809 all when N =:= byte_size(Bin) -> 810 {true, L}; 811 all when N =:= Len -> % N < byte_size(Bin) 812 {true, true, L}; 813 all -> 814 case printable_bin1(Bin, 1 + N, Len - N) of 815 0 when byte_size(Bin) =:= Len -> 816 {true, binary_to_list(Bin)}; 817 NC when D > 0, Len - NC >= D -> 818 {true, true, binary_to_list(Bin, 1, Len - NC)}; 819 NC when is_integer(NC) -> 820 false 821 end; 822 NC when is_integer(NC), D > 0, N - NC >= D -> 823 {true, true, binary_to_list(Bin, 1, N - NC)}; 824 NC when is_integer(NC) -> 825 false 826 end; 827printable_bin(Bin, Len, D, _Uni) -> 828 case valid_utf8(Bin,Len) of 829 true -> 830 case printable_unicode(Bin, Len, [], io:printable_range()) of 831 {_, <<>>, L} -> 832 {byte_size(Bin) =:= length(L), L}; 833 {NC, Bin1, L} when D > 0, Len - NC >= D -> 834 {byte_size(Bin)-byte_size(Bin1) =:= length(L), true, L}; 835 {_NC, _Bin, _L} -> 836 false 837 end; 838 false -> 839 printable_bin(Bin, Len, D, latin1) 840 end. 841 842printable_bin1(_Bin, _Start, 0) -> 843 0; 844printable_bin1(Bin, Start, Len) -> 845 N = erlang:min(10000, Len), 846 L = binary_to_list(Bin, Start, Start + N - 1), 847 case printable_latin1_list(L, N) of 848 all -> 849 printable_bin1(Bin, Start + N, Len - N); 850 NC when is_integer(NC) -> 851 Len - (N - NC) 852 end. 853 854%% -> all | integer() >=0. Adopted from io_lib.erl. 855printable_latin1_list([_ | _], 0) -> 0; 856printable_latin1_list([C | Cs], N) when is_integer(C), C >= $\s, C =< $~ -> 857 printable_latin1_list(Cs, N - 1); 858printable_latin1_list([C | Cs], N) when is_integer(C), C >= $\240, C =< $\377 -> 859 printable_latin1_list(Cs, N - 1); 860printable_latin1_list([$\n | Cs], N) -> printable_latin1_list(Cs, N - 1); 861printable_latin1_list([$\r | Cs], N) -> printable_latin1_list(Cs, N - 1); 862printable_latin1_list([$\t | Cs], N) -> printable_latin1_list(Cs, N - 1); 863printable_latin1_list([$\v | Cs], N) -> printable_latin1_list(Cs, N - 1); 864printable_latin1_list([$\b | Cs], N) -> printable_latin1_list(Cs, N - 1); 865printable_latin1_list([$\f | Cs], N) -> printable_latin1_list(Cs, N - 1); 866printable_latin1_list([$\e | Cs], N) -> printable_latin1_list(Cs, N - 1); 867printable_latin1_list([], _) -> all; 868printable_latin1_list(_, N) -> N. 869 870valid_utf8(<<>>,_) -> 871 true; 872valid_utf8(_,0) -> 873 true; 874valid_utf8(<<_/utf8, R/binary>>,N) -> 875 valid_utf8(R,N-1); 876valid_utf8(_,_) -> 877 false. 878 879printable_unicode(<<C/utf8, R/binary>>=Bin, I, L, Range) when I > 0 -> 880 case printable_char(C,Range) of 881 true -> 882 printable_unicode(R, I - 1, [C | L],Range); 883 false -> 884 {I, Bin, lists:reverse(L)} 885 end; 886printable_unicode(Bin, I, L,_) -> 887 {I, Bin, lists:reverse(L)}. 888 889printable_char($\n,_) -> true; 890printable_char($\r,_) -> true; 891printable_char($\t,_) -> true; 892printable_char($\v,_) -> true; 893printable_char($\b,_) -> true; 894printable_char($\f,_) -> true; 895printable_char($\e,_) -> true; 896printable_char(C,latin1) -> 897 C >= $\s andalso C =< $~ orelse 898 C >= 16#A0 andalso C =< 16#FF; 899printable_char(C,unicode) -> 900 C >= $\s andalso C =< $~ orelse 901 C >= 16#A0 andalso C < 16#D800 orelse 902 C > 16#DFFF andalso C < 16#FFFE orelse 903 C > 16#FFFF andalso C =< 16#10FFFF. 904 905write_atom(A, latin1) -> 906 io_lib:write_atom_as_latin1(A); 907write_atom(A, _Uni) -> 908 io_lib:write_atom(A). 909 910write_string(S, latin1) -> 911 io_lib:write_latin1_string(S, $"); %" 912write_string(S, _Uni) -> 913 io_lib:write_string(S, $"). %" 914 915expand({_, _, _Dots=0, no_more} = If, _T, _Dd) -> If; 916expand({{tuple,IsTagged,L}, _Len, _, no_more}, T, Dd) -> 917 {NL, NLen, NDots} = expand_list(L, T, Dd, 2), 918 {{tuple,IsTagged,NL}, NLen, NDots, no_more}; 919expand({{map, Pairs}, _Len, _, no_more}, T, Dd) -> 920 {NPairs, NLen, NDots} = expand_list(Pairs, T, Dd, 3), 921 {{map, NPairs}, NLen, NDots, no_more}; 922expand({{map_pair, K, V}, _Len, _, no_more}, T, Dd) -> 923 {_, KL, KD, _} = P1 = expand(K, tsub(T, 1), Dd), 924 KL1 = KL + 4, 925 {_, VL, VD, _} = P2 = expand(V, tsub(T, KL1), Dd), 926 {{map_pair, P1, P2}, KL1 + VL, KD + VD, no_more}; 927expand({{record, [{Name,NameL} | L]}, _Len, _, no_more}, T, Dd) -> 928 {NL, NLen, NDots} = expand_list(L, T, Dd, NameL + 2), 929 {{record, [{Name,NameL} | NL]}, NLen, NDots, no_more}; 930expand({{field, Name, NameL, Field}, _Len, _, no_more}, T, Dd) -> 931 F = {_S, L, Dots, _} = expand(Field, tsub(T, NameL), Dd), 932 {{field, Name, NameL, F}, NameL + L, Dots, no_more}; 933expand({_, _, _, More}, T, Dd) -> 934 More(T, Dd). 935 936expand_list(Ifs, T, Dd, L0) -> 937 L = expand_list(Ifs, tsub(T, L0), Dd), 938 {Len, Dots} = list_length(L, L0, 0), 939 {L, Len, Dots}. 940 941expand_list([], _T, _Dd) -> 942 []; 943expand_list([If | Ifs], T, Dd) -> 944 T1 = case Ifs =:= [] of 945 false -> tsub(T, 1); 946 true -> T 947 end, 948 {_, Len1, _, _} = Elem1 = expand(If, T1, Dd), 949 [Elem1 | expand_list(Ifs, tsub(T1, Len1), Dd)]; 950expand_list({_, _, _, More}, T, Dd) -> 951 More(T, Dd). 952 953%% Make sure T does not change sign. 954tsub(T, _) when T < 0 -> T; 955tsub(T, E) when T >= E -> T - E; 956tsub(_, _) -> 0. 957 958%% Throw 'no_good' if the indentation exceeds half the line length 959%% unless there is room for M characters on the line. 960 961cind({_S, Len, _, _}, Col, Ll, M, Ind, LD, W) when Len < Ll - Col - LD, 962 Len + W + LD =< M -> 963 Ind; 964cind({{list,L}, _Len, _, _}, Col, Ll, M, Ind, LD, W) -> 965 cind_list(L, Col + 1, Ll, M, Ind, LD, W + 1); 966cind({{tuple,true,L}, _Len, _ ,_}, Col, Ll, M, Ind, LD, W) -> 967 cind_tag_tuple(L, Col, Ll, M, Ind, LD, W + 1); 968cind({{tuple,false,L}, _Len, _, _}, Col, Ll, M, Ind, LD, W) -> 969 cind_list(L, Col + 1, Ll, M, Ind, LD, W + 1); 970cind({{map,Pairs}, _Len, _, _}, Col, Ll, M, Ind, LD, W) -> 971 cind_map(Pairs, Col + 2, Ll, M, Ind, LD, W + 2); 972cind({{record,[{_Name,NLen} | L]}, _Len, _, _}, Col, Ll, M, Ind, LD, W) -> 973 cind_record(L, NLen, Col, Ll, M, Ind, LD, W + NLen + 1); 974cind({{bin,_S}, _Len, _, _}, _Col, _Ll, _M, Ind, _LD, _W) -> 975 Ind; 976cind({_S,_Len,_,_}, _Col, _Ll, _M, Ind, _LD, _W) -> 977 Ind. 978 979cind_tag_tuple([{_Tag,Tlen,_,_} | L], Col, Ll, M, Ind, LD, W) -> 980 TagInd = Tlen + 2, 981 Tcol = Col + TagInd, 982 if 983 Ind > 0, TagInd > Ind -> 984 Col1 = Col + Ind, 985 if 986 M + Col1 =< Ll; Col1 =< Ll div 2 -> 987 cind_tail(L, Col1, Tcol, Ll, M, Ind, LD, W + Tlen); 988 true -> 989 throw(no_good) 990 end; 991 M + Tcol < Ll; Tcol < Ll div 2 -> 992 cind_list(L, Tcol, Ll, M, Ind, LD, W + Tlen + 1); 993 true -> 994 throw(no_good) 995 end; 996cind_tag_tuple(_, _Col, _Ll, _M, Ind, _LD, _W) -> 997 Ind. 998 999cind_map([P | Ps], Col, Ll, M, Ind, LD, W) -> 1000 PW = cind_pair(P, Col, Ll, M, Ind, last_depth(Ps, LD), W), 1001 cind_pairs_tail(Ps, Col, Col + PW, Ll, M, Ind, LD, W + PW); 1002cind_map(_, _Col, _Ll, _M, Ind, _LD, _W) -> 1003 Ind. % cannot happen 1004 1005cind_pairs_tail([{_, Len, _, _} = P | Ps], Col0, Col, Ll, M, Ind, LD, W) -> 1006 LD1 = last_depth(Ps, LD), 1007 ELen = 1 + Len, 1008 if 1009 LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM_PAIR(P); 1010 LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM_PAIR(P) -> 1011 cind_pairs_tail(Ps, Col0, Col + ELen, Ll, M, Ind, LD, W + ELen); 1012 true -> 1013 PW = cind_pair(P, Col0, Ll, M, Ind, LD1, 0), 1014 cind_pairs_tail(Ps, Col0, Col0 + PW, Ll, M, Ind, LD, PW) 1015 end; 1016cind_pairs_tail(_, _Col0, _Col, _Ll, _M, Ind, _LD, _W) -> 1017 Ind. 1018 1019cind_pair({{map_pair, _Key, _Value}, Len, _, _}=Pair, Col, Ll, M, _Ind, LD, W) 1020 when Len < Ll - Col - LD, Len + W + LD =< M -> 1021 if 1022 ?ATM_PAIR(Pair) -> 1023 Len; 1024 true -> 1025 Ll 1026 end; 1027cind_pair({{map_pair, K, V}, _Len, _, _}, Col0, Ll, M, Ind, LD, W0) -> 1028 cind(K, Col0, Ll, M, Ind, LD, W0), 1029 I = map_value_indent(Ind), 1030 cind(V, Col0 + I, Ll, M, Ind, LD, 0), 1031 Ll. 1032 1033map_value_indent(TInd) -> 1034 case TInd > 0 of 1035 true -> 1036 TInd; 1037 false -> 1038 4 1039 end. 1040 1041cind_record([F | Fs], Nlen, Col0, Ll, M, Ind, LD, W0) -> 1042 Nind = Nlen + 1, 1043 {Col, W} = cind_rec(Nind, Col0, Ll, M, Ind, W0), 1044 FW = cind_field(F, Col, Ll, M, Ind, last_depth(Fs, LD), W), 1045 cind_fields_tail(Fs, Col, Col + FW, Ll, M, Ind, LD, W + FW); 1046cind_record(_, _Nlen, _Col, _Ll, _M, Ind, _LD, _W) -> 1047 Ind. 1048 1049cind_fields_tail([{_, Len, _, _} = F | Fs], Col0, Col, Ll, M, Ind, LD, W) -> 1050 LD1 = last_depth(Fs, LD), 1051 ELen = 1 + Len, 1052 if 1053 LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM_FLD(F); 1054 LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM_FLD(F) -> 1055 cind_fields_tail(Fs, Col0, Col + ELen, Ll, M, Ind, LD, W + ELen); 1056 true -> 1057 FW = cind_field(F, Col0, Ll, M, Ind, LD1, 0), 1058 cind_fields_tail(Fs, Col0, Col + FW, Ll, M, Ind, LD, FW) 1059 end; 1060cind_fields_tail(_, _Col0, _Col, _Ll, _M, Ind, _LD, _W) -> 1061 Ind. 1062 1063cind_field({{field, _N, _NL, _F}, Len, _, _}=Fl, Col, Ll, M, _Ind, LD, W) 1064 when Len < Ll - Col - LD, Len + W + LD =< M -> 1065 if 1066 ?ATM_FLD(Fl) -> 1067 Len; 1068 true -> 1069 Ll 1070 end; 1071cind_field({{field, _Name, NameL, F},_Len,_,_}, Col0, Ll, M, Ind, LD, W0) -> 1072 {Col, W} = cind_rec(NameL, Col0, Ll, M, Ind, W0 + NameL), 1073 cind(F, Col, Ll, M, Ind, LD, W), 1074 Ll. 1075 1076cind_rec(RInd, Col0, Ll, M, Ind, W0) -> 1077 Nl = (Ind > 0) and (RInd > Ind), 1078 DCol = case Nl of 1079 true -> Ind; 1080 false -> RInd 1081 end, 1082 Col = Col0 + DCol, 1083 if 1084 M + Col =< Ll; Col =< Ll div 2 -> 1085 W = case Nl of 1086 true -> 0; 1087 false -> W0 1088 end, 1089 {Col, W}; 1090 true -> 1091 throw(no_good) 1092 end. 1093 1094cind_list({dots, _, _, _}, _Col0, _Ll, _M, Ind, _LD, _W) -> 1095 Ind; 1096cind_list([E | Es], Col0, Ll, M, Ind, LD, W) -> 1097 WE = cind_element(E, Col0, Ll, M, Ind, last_depth(Es, LD), W), 1098 cind_tail(Es, Col0, Col0 + WE, Ll, M, Ind, LD, W + WE). 1099 1100cind_tail([], _Col0, _Col, _Ll, _M, Ind, _LD, _W) -> 1101 Ind; 1102cind_tail([{_, Len, _, _} = E | Es], Col0, Col, Ll, M, Ind, LD, W) -> 1103 LD1 = last_depth(Es, LD), 1104 ELen = 1 + Len, 1105 if 1106 LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM(E); 1107 LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM(E) -> 1108 cind_tail(Es, Col0, Col + ELen, Ll, M, Ind, LD, W + ELen); 1109 true -> 1110 WE = cind_element(E, Col0, Ll, M, Ind, LD1, 0), 1111 cind_tail(Es, Col0, Col0 + WE, Ll, M, Ind, LD, WE) 1112 end; 1113cind_tail({dots, _, _, _}, _Col0, _Col, _Ll, _M, Ind, _LD, _W) -> 1114 Ind; 1115cind_tail({_, Len, _, _}=E, _Col0, Col, Ll, M, Ind, LD, W) 1116 when Len + 1 < Ll - Col - (LD + 1), 1117 Len + 1 + W + (LD + 1) =< M, 1118 ?ATM(E) -> 1119 Ind; 1120cind_tail(E, _Col0, Col, Ll, M, Ind, LD, _W) -> 1121 cind(E, Col, Ll, M, Ind, LD + 1, 0). 1122 1123cind_element({_, Len, _, _}=E, Col, Ll, M, _Ind, LD, W) 1124 when Len < Ll - Col - LD, Len + W + LD =< M, ?ATM(E) -> 1125 Len; 1126cind_element(E, Col, Ll, M, Ind, LD, W) -> 1127 cind(E, Col, Ll, M, Ind, LD, W), 1128 Ll. 1129 1130last_depth([_ | _], _LD) -> 1131 0; 1132last_depth(_, LD) -> 1133 LD + 1. 1134 1135while_fail([], _F, V) -> 1136 V; 1137while_fail([A | As], F, V) -> 1138 try F(A) catch _ -> while_fail(As, F, V) end. 1139 1140%% make a string of N spaces 1141indent(N) when is_integer(N), N > 0 -> 1142 chars($\s, N-1). 1143 1144%% prepend N spaces onto Ind 1145indent(1, Ind) -> % Optimization of common case 1146 [$\s | Ind]; 1147indent(4, Ind) -> % Optimization of common case 1148 S2 = [$\s, $\s], 1149 [S2, S2 | Ind]; 1150indent(N, Ind) when is_integer(N), N > 0 -> 1151 [chars($\s, N) | Ind]. 1152 1153%% A deep version of string:chars/2 1154chars(_C, 0) -> 1155 []; 1156chars(C, 2) -> 1157 [C, C]; 1158chars(C, 3) -> 1159 [C, C, C]; 1160chars(C, N) when (N band 1) =:= 0 -> 1161 S = chars(C, N bsr 1), 1162 [S | S]; 1163chars(C, N) -> 1164 S = chars(C, N bsr 1), 1165 [C, S | S]. 1166 1167get_option(Key, TupleList, Default) -> 1168 case lists:keyfind(Key, 1, TupleList) of 1169 false -> Default; 1170 {Key, Value} -> Value; 1171 _ -> Default 1172 end. 1173