From 93eb41c97012d1af3a9675886d8824eda1618bb6 Mon Sep 17 00:00:00 2001 From: SisMaker <1713699517@qq.com> Date: Fri, 26 Feb 2021 13:32:34 +0800 Subject: [PATCH] =?UTF-8?q?ft:=20=E4=BF=AE=E6=94=B9=E4=B8=BA=E4=BA=8C?= =?UTF-8?q?=E8=BF=9B=E5=88=B6=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/eFmt.erl | 122 +++-- src/eFmtPretty.erl | 1134 -------------------------------------------- 2 files changed, 57 insertions(+), 1199 deletions(-) delete mode 100644 src/eFmtPretty.erl diff --git a/src/eFmt.erl b/src/eFmt.erl index b3dc872..90707ff 100644 --- a/src/eFmt.erl +++ b/src/eFmt.erl @@ -156,63 +156,13 @@ writeList([One | List], D, E, BinAcc) -> writeList(Other, D, E, BinAcc) -> <>. --spec printable_list(Term) -> boolean() when - Term :: term(). - -visableList(L, Encoding) -> - %% There will be more alternatives returns from io:printable range - %% in the future. To not have a catch-all clause is deliberate. - case Encoding of - latin1 -> - printable_latin1_list(L); - _ -> - printable_unicode_list(L) - end. - --spec printable_unicode_list(Term) -> boolean() when - Term :: term(). - --spec printable_latin1_list(Term) -> boolean() when - Term :: term(). - -printable_latin1_list([C|Cs]) when is_integer(C), C >= $\040, C =< $\176 -> - printable_latin1_list(Cs); -printable_latin1_list([C|Cs]) when is_integer(C), C >= $\240, C =< $\377 -> - printable_latin1_list(Cs); -printable_latin1_list([$\n|Cs]) -> printable_latin1_list(Cs); -printable_latin1_list([$\r|Cs]) -> printable_latin1_list(Cs); -printable_latin1_list([$\t|Cs]) -> printable_latin1_list(Cs); -printable_latin1_list([$\v|Cs]) -> printable_latin1_list(Cs); -printable_latin1_list([$\b|Cs]) -> printable_latin1_list(Cs); -printable_latin1_list([$\f|Cs]) -> printable_latin1_list(Cs); -printable_latin1_list([$\e|Cs]) -> printable_latin1_list(Cs); -printable_latin1_list([]) -> true; -printable_latin1_list(_) -> false. %Everything else is false - -printable_unicode_list([C|Cs]) when is_integer(C), C >= $\040, C =< $\176 -> - printable_unicode_list(Cs); -printable_unicode_list([C|Cs]) - when is_integer(C), C >= 16#A0, C < 16#D800; - is_integer(C), C > 16#DFFF, C < 16#FFFE; - is_integer(C), C > 16#FFFF, C =< 16#10FFFF -> - printable_unicode_list(Cs); -printable_unicode_list([$\n|Cs]) -> printable_unicode_list(Cs); -printable_unicode_list([$\r|Cs]) -> printable_unicode_list(Cs); -printable_unicode_list([$\t|Cs]) -> printable_unicode_list(Cs); -printable_unicode_list([$\v|Cs]) -> printable_unicode_list(Cs); -printable_unicode_list([$\b|Cs]) -> printable_unicode_list(Cs); -printable_unicode_list([$\f|Cs]) -> printable_unicode_list(Cs); -printable_unicode_list([$\e|Cs]) -> printable_unicode_list(Cs); -printable_unicode_list([]) -> true; -printable_unicode_list(_) -> false. %Everything else is false - writeList(List, Depth, Width, Encoding, Strings) -> - case Strings andalso visableList(List, Encoding) of + case Strings andalso visualList(List, Encoding) of true -> list_to_binary(List); _ -> - writeList([], Depth, Widt, Encoding, Strings, SumLC, <<"[">>) + writeList([], Depth, Width, Encoding, Strings, SumLC, <<"[">>) end. writeList([], Depth, Width, Encoding, Strings, SumLC, BinAcc) -> @@ -239,16 +189,7 @@ writeList(Term, Depth, Width, Encoding, Strings, <<"[">>) -> writeTuple(Tuple, Depth, Width, CharsLimit, Encoding, Strings, Index, TupleSize, BinAcc) -> if - Depth =:= 1 -> <>; - true -> - if - Index < TupleSize -> - writeTuple(Tuple, Depth, Width, CharsLimit, Encoding, Strings, Index + 1, TupleSize, <>); - Index == TupleSize -> - <>; - true -> - <> - end + end. writeMap(Map, D, E, BinAcc) -> @@ -318,9 +259,12 @@ writeTerm(_Term, Depth, _Width, _Encoding, _Strings) when Depth =< 0 -> <<"..."> writeTerm(Term, _Depth, _Width, _Encoding, _Strings) when is_integer(Term) -> ?writeInt(Term); writeTerm(Term, _Depth, _Width, Encoding, _Strings) when is_atom(Term) -> ?writeAtom(Term, Encoding); writeTerm(Term, Depth, Width, Encoding, Strings) when is_list(Term) -> writeList(Term, Depth, Width, Encoding, Strings); -writeTerm(Term, Depth, Width, Encoding, Strings) when is_map(Term) -> writeMap(Term, Depth, Width, Encoding, Strings, <<"#{">>); -writeTerm(Term, Depth, Width, Encoding, Strings) when is_tuple(Term) -> writeTuple(Term, Depth, Width, Encoding, Strings, 1, tuple_size(Term), <<"{">>); -writeTerm(Term, Depth, Width, Encoding, Strings) when is_bitstring(Term) -> writeBinary(Term, Depth, Width, Encoding, Strings, <<"<<">>); +writeTerm(Term, Depth, Width, Encoding, Strings) when is_map(Term) -> + writeMap(Term, Depth, Width, Encoding, Strings, <<"#{">>); +writeTerm(Term, Depth, Width, Encoding, Strings) when is_tuple(Term) -> + writeTuple(Term, Depth, Width, Encoding, Strings, 1, tuple_size(Term), <<"{">>); +writeTerm(Term, Depth, Width, Encoding, Strings) when is_bitstring(Term) -> + writeBinary(Term, Depth, Width, Encoding, Strings, <<"<<">>); writeTerm(Term, _Depth, _Width, _Encoding, _Strings) when is_pid(Term) -> ?writePid(Term); writeTerm(Term, _Depth, _Width, _Encoding, _Strings) when is_float(Term) -> ?writeFloat(Term); writeTerm(Term, _Depth, _Width, _Encoding, _Strings) when is_port(Term) -> ?writePort(Term); @@ -867,4 +811,52 @@ toBinary(Value) when is_float(Value) -> float_to_binary(Value, [{decimals, 6}, c toBinary(Value) when is_atom(Value) -> atom_to_binary(Value, utf8); toBinary(Value) when is_binary(Value) -> Value; toBinary(Value) -> term_to_binary(Value). + +visualList(L, Encoding) -> + ?IIF(Encoding == latin1, visualLatin1List(L), visualUnicodeList(L, Encoding)). + +visualBin(Bin, Encoding) -> + ?IIF(Encoding == latin1, visualLatin1Bin(Bin), visualUtf8Bin(Bin, io:printable_range())). + +visualLatin1List([]) -> true; +visualLatin1List([C | Cs]) -> ?IIF(visualLatin1Char(C), visualLatin1List(Cs), false); +visualLatin1List(_) -> false. + +visualUnicodeList([], _) -> true; +visualUnicodeList([C | Cs], Encoding) -> ?IIF(visualUtf8Char(C, Encoding), visualUnicodeList(Cs, Encoding), false); +visualUnicodeList(_, _) -> false. + +visualLatin1Bin(<<>>) -> true; +visualLatin1Bin(<>) -> ?IIF(visualLatin1Char(C), visualLatin1Bin(Left), false); +visualLatin1Bin(_) -> false. + +visualUtf8Bin(<<>>, _) -> true; +visualUtf8Bin(<>, Range) -> ?IIF(visualUtf8Char(C, Range), visualUtf8Bin(Left, Range), false); +visualUtf8Bin(_, _) -> false. + +visualLatin1Char($\n) -> true; +visualLatin1Char($\r) -> true; +visualLatin1Char($\t) -> true; +visualLatin1Char($\v) -> true; +visualLatin1Char($\b) -> true; +visualLatin1Char($\f) -> true; +visualLatin1Char($\e) -> true; +visualLatin1Char(C) -> + C >= $\040 andalso C =< $\176 orelse C >= $\240, C =< $\377. + +visualUtf8Char($\n, _) -> true; +visualUtf8Char($\r, _) -> true; +visualUtf8Char($\t, _) -> true; +visualUtf8Char($\v, _) -> true; +visualUtf8Char($\b, _) -> true; +visualUtf8Char($\f, _) -> true; +visualUtf8Char($\e, _) -> true; +visualUtf8Char(C, _Encoding) -> + C >= $\s andalso C =< $~ orelse C >= 16#A0 andalso C < 16#D800 orelse C > 16#DFFF andalso C < 16#FFFE orelse C > 16#FFFF andalso C =< 16#10FFFF. + %% case Encoding of + %% latin1 -> + %% C >= $\s andalso C =< $~ orelse C >= 16#A0 andalso C =< 16#FF; + %% _ -> + %% C >= $\s andalso C =< $~ orelse C >= 16#A0 andalso C < 16#D800 orelse C > 16#DFFF andalso C < 16#FFFE orelse C > 16#FFFF andalso C =< 16#10FFFF + %% end. %% ********************************************** utils end ********************************************************** \ No newline at end of file diff --git a/src/eFmtPretty.erl b/src/eFmtPretty.erl deleted file mode 100644 index 621b8fb..0000000 --- a/src/eFmtPretty.erl +++ /dev/null @@ -1,1134 +0,0 @@ --module(eFmtPretty). - --include("eFmt.hrl"). - --export([ - pPrint/1 - , pPrint/2 - , pPrint/3 - , pPrint/4 - , pPrint/5 - , pPrint/6 - - %% To be used by io_lib only. - , pIntermediate/6 - , pWrite/1 -]). - - -%%% -%%% Exported functions -%%% - -%% print(Term) -> [Chars] -%% print(Term, Column, LineLength, Depth) -> [Chars] -%% Depth = -1 gives unlimited print depth. Use io_lib:write for atomic terms. - --spec pPrint(term()) -> io_lib:chars(). -pPrint(Term) -> - print(Term, 1, ?LineCCnt, -1, -1, -1, no_fun, latin1, true). - -%% print(Term, RecDefFun) -> [Chars] -%% print(Term, Depth, RecDefFun) -> [Chars] -%% RecDefFun = fun(Tag, NoFields) -> [FieldTag] | no -%% Used by the shell for printing records and for Unicode. - --type rec_print_fun() :: fun((Tag :: atom(), NFields :: non_neg_integer()) -> 'no' | [FieldName :: atom()]). --type column() :: integer(). --type encoding() :: epp:source_encoding() | 'unicode'. --type line_length() :: pos_integer(). --type depth() :: integer(). --type line_max_chars() :: integer(). --type chars_limit() :: integer(). - --type chars() :: io_lib:chars(). --type option() :: {'chars_limit', chars_limit()} -| {'column', column()} -| {'depth', depth()} -| {'encoding', encoding()} -| {'line_length', line_length()} -| {'line_max_chars', line_max_chars()} -| {'record_print_fun', rec_print_fun()} -| {'strings', boolean()}. --type options() :: [option()]. - --spec pPrint(term(), rec_print_fun()) -> chars(); - (term(), options()) -> chars(). - -pPrint(Term, Options) when is_list(Options) -> - Col = get_option(column, Options, 1), - Ll = get_option(line_length, Options, ?LineCCnt), - D = get_option(depth, Options, -1), - M = get_option(line_max_chars, Options, -1), - T = get_option(chars_limit, Options, -1), - RecDefFun = get_option(record_print_fun, Options, no_fun), - Encoding = get_option(encoding, Options, epp:default_encoding()), - Strings = get_option(strings, Options, true), - print(Term, Col, Ll, D, M, T, RecDefFun, Encoding, Strings); -pPrint(Term, RecDefFun) -> - pPrint(Term, -1, RecDefFun). - --spec pPrint(term(), depth(), rec_print_fun()) -> chars(). - -pPrint(Term, Depth, RecDefFun) -> - pPrint(Term, 1, ?LineCCnt, Depth, RecDefFun). - --spec pPrint(term(), column(), line_length(), depth()) -> chars(). -pPrint(Term, Col, Ll, D) -> - print(Term, Col, Ll, D, _M = -1, _T = -1, no_fun, latin1, true). - --spec pPrint(term(), column(), line_length(), depth(), rec_print_fun()) -> - chars(). -pPrint(Term, Col, Ll, D, RecDefFun) -> - pPrint(Term, Col, Ll, D, _M = -1, RecDefFun). - --spec pPrint(term(), column(), line_length(), depth(), line_max_chars(), - rec_print_fun()) -> chars(). - -pPrint(Term, Col, Ll, D, M, RecDefFun) -> - print(Term, Col, Ll, D, M, _T = -1, RecDefFun, latin1, true). - -%% D = Depth, default -1 (infinite), or LINEMAX=30 when printing from shell -%% T = chars_limit, that is, maximal number of characters, default -1 -%% Used together with D to limit the output. It is possible that -%% more than T characters are returned. -%% Col = current column, default 1 -%% Ll = line length/~p field width, default 120 -%% M = CHAR_MAX (-1 if no max, 60 when printing from shell) -%%Term, Precision, Width, Depth, -1, CharsLimit, no_fun, Encoding, Strings - -print(_, _, _, 0, _M, _T, _RF, _Enc, _Str) -> "..."; -print(_, _, _, _D, _M, 0, _RF, _Enc, _Str) -> "..."; -print(Term, Col, Ll, D, M, T, RecDefFun, Enc, Str) when Col =< 0 -> - %% ensure Col is at least 1 - print(Term, 1, Ll, D, M, T, RecDefFun, Enc, Str); -print(Term, Col, Ll, D, M0, T, RecDefFun, Enc, Str) when is_tuple(Term); is_list(Term); is_map(Term); is_bitstring(Term) -> - %% preprocess and compute total number of chars - {_, Len, _Dots, _} = If = - case T < 0 of - true -> printTerm(Term, D, T, RecDefFun, Enc, Str); - false -> pIntermediate(Term, D, T, RecDefFun, Enc, Str) - end, - %% use Len as CHAR_MAX if M0 = -1 - M = max_cs(M0, Len), - if - Ll =:= 0 -> - pWrite(If); - Len < Ll - Col, Len =< M -> - %% write the whole thing on a single line when there is room - pWrite(If); - true -> - %% compute the indentation TInd for tagged tuples and records - TInd = while_fail([-1, 4], - fun(I) -> cind(If, Col, Ll, M, I, 0, 0) end, - 1), - pp(If, Col, Ll, M, TInd, indent(Col), 0, 0) - end; -print(Term, _Col, _Ll, _D, _M, _T, _RF, _Enc, _Str) -> - %% atomic data types (bignums, atoms, ...) are never truncated - eFmt:write(Term, _D, _Enc). - - -print(_, _, _, 0, _M, _T, _RF, _Enc, _Str) -> "..."; -print(_, _, _, _D, _M, 0, _RF, _Enc, _Str) -> "..."; -print(Term, Col, Ll, D, M, T, RecDefFun, Enc, Str) when Col =< 0 -> - %% ensure Col is at least 1 - print(Term, 1, Ll, D, M, T, RecDefFun, Enc, Str); -%% print(Term, Precision, Width, Depth, -1, CharsLimit, no_fun, Encoding, Strings) -print(Term, Precision, Width, Depth, M0_1, CharsLimit, RecDefFun, Encoding, Strings) when is_tuple(Term); is_list(Term); is_map(Term); is_bitstring(Term) -> - if - Depth == 0 orelse CharsLimit == 0 -> - <<"...">>; - true -> - NewPrecision == ?IIF(Precision =< 0, 1, Precision), - if - is_tuple(Term); is_list(Term); is_map(Term); is_bitstring(Term) -> - %% preprocess and compute total number of chars - {_, Len, _Dots, _} = If = - case CharsLimit < 0 of - true -> printTerm(Term, Depth, CharsLimit, RecDefFun, Encoding, Strings); - _ -> pIntermediate(Term, Depth, CharsLimit, RecDefFun, Encoding, Strings) - end, - %% use Len as CHAR_MAX if M0 = -1 - M = max_cs(M0_1, Len), - if - Width =:= 0 -> - pWrite(If); - Len < Width - NewPrecision, Len =< M -> - %% write the whole thing on a single line when there is room - pWrite(If); - true -> - %% compute the indentation TInd for tagged tuples and records - TInd = while_fail([-1, 4], - fun(I) -> cind(If, NewPrecision, Width, M, I, 0, 0) end, - 1), - pp(If, NewPrecision, Width, M, TInd, indent(NewPrecision), 0, 0) - end; - true -> - eFmt:write(Term, Depth, Encoding, CharsLimit) - end - end. - - -%%% -%%% Local functions -%%% - -%% use M only if nonnegative, otherwise use Len as default value -max_cs(M, Len) when M < 0 -> - Len; -max_cs(M, _Len) -> - M. - --define(ATM(T), is_list(element(1, T))). --define(ATM_PAIR(Pair), - ?ATM(element(2, element(1, Pair))) % Key - andalso - ?ATM(element(3, element(1, Pair)))). % Value --define(ATM_FLD(Field), ?ATM(element(4, element(1, Field)))). - -pp({_S, Len, _, _} = If, Col, Ll, M, _TInd, _Ind, LD, W) - when Len < Ll - Col - LD, Len + W + LD =< M -> - pWrite(If); -pp({{list, L}, _Len, _, _}, Col, Ll, M, TInd, Ind, LD, W) -> - [$[, pp_list(L, Col + 1, Ll, M, TInd, indent(1, Ind), LD, $|, W + 1), $]]; -pp({{tuple, true, L}, _Len, _, _}, Col, Ll, M, TInd, Ind, LD, W) -> - [${, pp_tag_tuple(L, Col, Ll, M, TInd, Ind, LD, W + 1), $}]; -pp({{tuple, false, L}, _Len, _, _}, Col, Ll, M, TInd, Ind, LD, W) -> - [${, pp_list(L, Col + 1, Ll, M, TInd, indent(1, Ind), LD, $,, W + 1), $}]; -pp({{map, Pairs}, _Len, _, _}, Col, Ll, M, TInd, Ind, LD, W) -> - [$#, ${, pp_map(Pairs, Col + 2, Ll, M, TInd, indent(2, Ind), LD, W + 1), - $}]; -pp({{record, [{Name, NLen} | L]}, _Len, _, _}, Col, Ll, M, TInd, Ind, LD, W) -> - [Name, ${, pp_record(L, NLen, Col, Ll, M, TInd, Ind, LD, W + NLen + 1), $}]; -pp({{bin, S}, _Len, _, _}, Col, Ll, M, _TInd, Ind, LD, W) -> - pp_binary(S, Col + 2, Ll, M, indent(2, Ind), LD, W); -pp({S, _Len, _, _}, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) -> - S. - -%% Print a tagged tuple by indenting the rest of the elements -%% differently to the tag. Tuple has size >= 2. -pp_tag_tuple([{Tag, Tlen, _, _} | L], Col, Ll, M, TInd, Ind, LD, W) -> - %% this uses TInd - TagInd = Tlen + 2, - Tcol = Col + TagInd, - S = $,, - if - TInd > 0, TagInd > TInd -> - Col1 = Col + TInd, - Indent = indent(TInd, Ind), - [Tag | pp_tail(L, Col1, Tcol, Ll, M, TInd, Indent, LD, S, W + Tlen)]; - true -> - Indent = indent(TagInd, Ind), - [Tag, S | pp_list(L, Tcol, Ll, M, TInd, Indent, LD, S, W + Tlen + 1)] - end. - -pp_map([], _Col, _Ll, _M, _TInd, _Ind, _LD, _W) -> - ""; % cannot happen -pp_map({dots, _, _, _}, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) -> - "..."; % cannot happen -pp_map([P | Ps], Col, Ll, M, TInd, Ind, LD, W) -> - {PS, PW} = pp_pair(P, Col, Ll, M, TInd, Ind, last_depth(Ps, LD), W), - [PS | pp_pairs_tail(Ps, Col, Col + PW, Ll, M, TInd, Ind, LD, PW)]. - -pp_pairs_tail([], _Col0, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) -> - ""; -pp_pairs_tail({dots, _, _, _}, _Col0, _Col, _M, _Ll, _TInd, _Ind, _LD, _W) -> - ",..."; -pp_pairs_tail([{_, Len, _, _} = P | Ps], Col0, Col, Ll, M, TInd, Ind, LD, W) -> - LD1 = last_depth(Ps, LD), - ELen = 1 + Len, - if - LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM_PAIR(P); - LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM_PAIR(P) -> - [$,, write_pair(P) | - pp_pairs_tail(Ps, Col0, Col + ELen, Ll, M, TInd, Ind, LD, W + ELen)]; - true -> - {PS, PW} = pp_pair(P, Col0, Ll, M, TInd, Ind, LD1, 0), - [$,, $\n, Ind, PS | - pp_pairs_tail(Ps, Col0, Col0 + PW, Ll, M, TInd, Ind, LD, PW)] - end. - -pp_pair({_, Len, _, _} = Pair, Col, Ll, M, _TInd, _Ind, LD, W) - when Len < Ll - Col - LD, Len + W + LD =< M -> - {write_pair(Pair), if - ?ATM_PAIR(Pair) -> - Len; - true -> - Ll % force nl - end}; -pp_pair({{map_pair, K, V}, _Len, _, _}, Col0, Ll, M, TInd, Ind0, LD, W) -> - I = map_value_indent(TInd), - Ind = indent(I, Ind0), - {[pp(K, Col0, Ll, M, TInd, Ind0, LD, W), " =>\n", - Ind | pp(V, Col0 + I, Ll, M, TInd, Ind, LD, 0)], Ll}. % force nl - -pp_record([], _Nlen, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) -> - ""; -pp_record({dots, _, _, _}, _Nlen, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) -> - "..."; -pp_record([F | Fs], Nlen, Col0, Ll, M, TInd, Ind0, LD, W0) -> - Nind = Nlen + 1, - {Col, Ind, S, W} = rec_indent(Nind, TInd, Col0, Ind0, W0), - {FS, FW} = pp_field(F, Col, Ll, M, TInd, Ind, last_depth(Fs, LD), W), - [S, FS | pp_fields_tail(Fs, Col, Col + FW, Ll, M, TInd, Ind, LD, W + FW)]. - -pp_fields_tail([], _Col0, _Col, _Ll, _M, _TInd, _Ind, _LD, _W) -> - ""; -pp_fields_tail({dots, _, _, _}, _Col0, _Col, _M, _Ll, _TInd, _Ind, _LD, _W) -> - ",..."; -pp_fields_tail([{_, Len, _, _} = F | Fs], Col0, Col, Ll, M, TInd, Ind, LD, W) -> - LD1 = last_depth(Fs, LD), - ELen = 1 + Len, - if - LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM_FLD(F); - LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM_FLD(F) -> - [$,, write_field(F) | - pp_fields_tail(Fs, Col0, Col + ELen, Ll, M, TInd, Ind, LD, W + ELen)]; - true -> - {FS, FW} = pp_field(F, Col0, Ll, M, TInd, Ind, LD1, 0), - [$,, $\n, Ind, FS | - pp_fields_tail(Fs, Col0, Col0 + FW, Ll, M, TInd, Ind, LD, FW)] - end. - -pp_field({_, Len, _, _} = Fl, Col, Ll, M, _TInd, _Ind, LD, W) - when Len < Ll - Col - LD, Len + W + LD =< M -> - {write_field(Fl), if - ?ATM_FLD(Fl) -> - Len; - true -> - Ll % force nl - end}; -pp_field({{field, Name, NameL, F}, _, _, _}, Col0, Ll, M, TInd, Ind0, LD, W0) -> - {Col, Ind, S, W} = rec_indent(NameL, TInd, Col0, Ind0, W0 + NameL), - Sep = case S of - [$\n | _] -> " ="; - _ -> " = " - end, - {[Name, Sep, S | pp(F, Col, Ll, M, TInd, Ind, LD, W)], Ll}. % force nl - -rec_indent(RInd, TInd, Col0, Ind0, W0) -> - %% this uses TInd - Nl = (TInd > 0) and (RInd > TInd), - DCol = case Nl of - true -> TInd; - false -> RInd - end, - Col = Col0 + DCol, - Ind = indent(DCol, Ind0), - S = case Nl of - true -> [$\n | Ind]; - false -> "" - end, - W = case Nl of - true -> 0; - false -> W0 - end, - {Col, Ind, S, W}. - -pp_list({dots, _, _, _}, _Col0, _Ll, _M, _TInd, _Ind, _LD, _S, _W) -> - "..."; -pp_list([E | Es], Col0, Ll, M, TInd, Ind, LD, S, W) -> - {ES, WE} = pp_element(E, Col0, Ll, M, TInd, Ind, last_depth(Es, LD), W), - [ES | pp_tail(Es, Col0, Col0 + WE, Ll, M, TInd, Ind, LD, S, W + WE)]. - -pp_tail([], _Col0, _Col, _Ll, _M, _TInd, _Ind, _LD, _S, _W) -> - []; -pp_tail([{_, Len, _, _} = E | Es], Col0, Col, Ll, M, TInd, Ind, LD, S, W) -> - LD1 = last_depth(Es, LD), - ELen = 1 + Len, - if - LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM(E); - LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM(E) -> - [$,, pWrite(E) | - pp_tail(Es, Col0, Col + ELen, Ll, M, TInd, Ind, LD, S, W + ELen)]; - true -> - {ES, WE} = pp_element(E, Col0, Ll, M, TInd, Ind, LD1, 0), - [$,, $\n, Ind, ES | - pp_tail(Es, Col0, Col0 + WE, Ll, M, TInd, Ind, LD, S, WE)] - end; -pp_tail({dots, _, _, _}, _Col0, _Col, _Ll, _M, _TInd, _Ind, _LD, S, _W) -> - [S | "..."]; -pp_tail({_, Len, _, _} = E, _Col0, Col, Ll, M, _TInd, _Ind, LD, S, W) - when Len + 1 < Ll - Col - (LD + 1), - Len + 1 + W + (LD + 1) =< M, - ?ATM(E) -> - [S | pWrite(E)]; -pp_tail(E, Col0, _Col, Ll, M, TInd, Ind, LD, S, _W) -> - [S, $\n, Ind | pp(E, Col0, Ll, M, TInd, Ind, LD + 1, 0)]. - -pp_element({_, Len, _, _} = E, Col, Ll, M, _TInd, _Ind, LD, W) - when Len < Ll - Col - LD, Len + W + LD =< M, ?ATM(E) -> - {pWrite(E), Len}; -pp_element(E, Col, Ll, M, TInd, Ind, LD, W) -> - {pp(E, Col, Ll, M, TInd, Ind, LD, W), Ll}. % force nl - -%% Reuse the list created by io_lib:write_binary()... -pp_binary([LT, LT, S, GT, GT], Col, Ll, M, Ind, LD, W) -> - N = erlang:max(8, erlang:min(Ll - Col, M - 4 - W) - LD), - [LT, LT, pp_binary(S, N, N, Ind), GT, GT]. - -pp_binary([BS, $, | S], N, N0, Ind) -> - Len = length(BS) + 1, - case N - Len of - N1 when N1 < 0 -> - [$\n, Ind, BS, $, | pp_binary(S, N0 - Len, N0, Ind)]; - N1 -> - [BS, $, | pp_binary(S, N1, N0, Ind)] - end; -pp_binary([BS1, $:, BS2] = S, N, _N0, Ind) - when length(BS1) + length(BS2) + 1 > N -> - [$\n, Ind, S]; -pp_binary(S, N, _N0, Ind) -> - case iolist_size(S) > N of - true -> - [$\n, Ind, S]; - false -> - S - end. - -%% write the whole thing on a single line -pWrite({{tuple, _IsTagged, L}, _, _, _}) -> - [${, write_list(L, $,), $}]; -pWrite({{list, L}, _, _, _}) -> - [$[, write_list(L, $|), $]]; -pWrite({{map, Pairs}, _, _, _}) -> - [$#, ${, write_list(Pairs, $,), $}]; -pWrite({{map_pair, _K, _V}, _, _, _} = Pair) -> - write_pair(Pair); -pWrite({{record, [{Name, _} | L]}, _, _, _}) -> - [Name, ${, write_fields(L), $}]; -pWrite({{bin, S}, _, _, _}) -> - S; -pWrite({S, _, _, _}) -> - S. - -write_pair({{map_pair, K, V}, _, _, _}) -> - [pWrite(K), " => ", pWrite(V)]. - -write_fields([]) -> - ""; -write_fields({dots, _, _, _}) -> - "..."; -write_fields([F | Fs]) -> - [write_field(F) | write_fields_tail(Fs)]. - -write_fields_tail([]) -> - ""; -write_fields_tail({dots, _, _, _}) -> - ",..."; -write_fields_tail([F | Fs]) -> - [$,, write_field(F) | write_fields_tail(Fs)]. - -write_field({{field, Name, _NameL, F}, _, _, _}) -> - [Name, " = " | pWrite(F)]. - -write_list({dots, _, _, _}, _S) -> - "..."; -write_list([E | Es], S) -> - [pWrite(E) | write_tail(Es, S)]. - -write_tail([], _S) -> - []; -write_tail([E | Es], S) -> - [$,, pWrite(E) | write_tail(Es, S)]; -write_tail({dots, _, _, _}, S) -> - [S | "..."]; -write_tail(E, S) -> - [S | pWrite(E)]. - --type more() :: fun((chars_limit(), DeltaDepth :: non_neg_integer()) -> - intermediate_format()). - --type if_list() :: maybe_improper_list(intermediate_format(), -{'dots', non_neg_integer(), - non_neg_integer(), more()}). - --type intermediate_format() :: -{chars() -| {'bin', chars()} -| 'dots' -| {'field', Name :: chars(), NameLen :: non_neg_integer(), - intermediate_format()} -| {'list', if_list()} -| {'map', if_list()} -| {'map_pair', K :: intermediate_format(), - V :: intermediate_format()} -| {'record', [{Name :: chars(), NameLen :: non_neg_integer()} -| if_list()]} -| {'tuple', IsTagged :: boolean(), if_list()}, - Len :: non_neg_integer(), - NumOfDots :: non_neg_integer(), - More :: more() | 'no_more' -}. - --spec pIntermediate(term(), depth(), pos_integer(), rec_print_fun(), - encoding(), boolean()) -> intermediate_format(). - -pIntermediate(Term, Depth, CharsLimit, RF, Enc, Str) -> - D0 = 1, - If = printTerm(Term, D0, CharsLimit, RF, Enc, Str), - case If of - {_, Len, Dots, _} when Dots =:= 0; Len > CharsLimit; Depth =:= 1 -> - If; - _ -> - find_upper(If, Term, CharsLimit, D0, 2, Depth, RF, Enc, Str) - end. - -find_upper(Lower, Term, T, Dl, Dd, D, RF, Enc, Str) -> - Dd2 = Dd * 2, - D1 = case D < 0 of - true -> Dl + Dd2; - false -> min(Dl + Dd2, D) - end, - If = expand(Lower, T, D1 - Dl), - case If of - {_, _, _Dots = 0, _} -> % even if Len > T - If; - {_, _Len = T, _, _} -> % increasing the depth is meaningless - If; - {_, Len, _, _} when Len < T, D1 < D orelse D < 0 -> - find_upper(If, Term, T, D1, Dd2, D, RF, Enc, Str); - _ -> - search_depth(Lower, If, Term, T, Dl, D1, RF, Enc, Str) - end. - -%% Lower has NumOfDots > 0 and Len =< T. -%% Upper has NumOfDots > 0 and Len > T. -search_depth(Lower, Upper, _Term, T, Dl, Du, _RF, _Enc, _Str) - when Du - Dl =:= 1 -> - %% The returned intermediate format has Len >= T. - case Lower of - {_, T, _, _} -> - Lower; - _ -> - Upper - end; -search_depth(Lower, Upper, Term, T, Dl, Du, RF, Enc, Str) -> - D1 = (Dl + Du) div 2, - If = expand(Lower, T, D1 - Dl), - case If of - {_, Len, _, _} when Len > T -> - %% Len can be greater than Upper's length. - %% This is a bit expensive since the work to - %% crate Upper is wasted. It is the price - %% to pay to get a more balanced output. - search_depth(Lower, If, Term, T, Dl, D1, RF, Enc, Str); - _ -> - search_depth(If, Upper, Term, T, D1, Du, RF, Enc, Str) - end. - - -printTerm(List, D, T, RF, Enc, Str) when is_list(List) -> - %% only flat lists are "printable" - case Str andalso visualList(List, D, T, Enc) of - true -> - %% print as string, escaping double-quotes in the list - S = write_string(List, Enc), - {S, io_lib:chars_length(S), 0, no_more}; - {true, Prefix} -> - %% Truncated lists when T < 0 could break some existing code. - S = write_string(Prefix, Enc), - %% NumOfDots = 0 to avoid looping--increasing the depth - %% does not make Prefix longer. - {[S | "..."], 3 + io_lib:chars_length(S), 0, no_more}; - false -> - case print_length_list(List, D, T, RF, Enc, Str) of - {What, Len, Dots, _More} when Dots > 0 -> - More = fun(T1, Dd) -> - ?FUNCTION_NAME(List, D + Dd, T1, RF, Enc, Str) - end, - {What, Len, Dots, More}; - If -> - If - end - end; -printTerm(Tuple, D, T, RF, Enc, Str) when is_tuple(Tuple) -> - print_length_tuple(Tuple, D, T, RF, Enc, Str); -printTerm(Map, D, T, RF, Enc, Str) when is_map(Map) -> - print_length_map(Map, D, T, RF, Enc, Str); -printTerm(<<_/bitstring>> = Bin, D, T, RF, Enc, Str) -> - D1 = D - 1, - case - Str andalso (bit_size(Bin) rem 8) =:= 0 andalso printable_bin0(Bin, D1, tsub(T, 6), Enc) of - {true, List} when is_list(List) -> - S = io_lib:write_string(List, $"), %" - {[$<, $<, S, $>, $>], 4 + length(S), 0, no_more}; - {false, List} when is_list(List) -> - S = io_lib:write_string(List, $"), %" - {[$<, $<, S, "/utf8>>"], 9 + io_lib:chars_length(S), 0, no_more}; - {true, true, Prefix} -> - S = io_lib:write_string(Prefix, $"), %" - More = fun(T1, Dd) -> - ?FUNCTION_NAME(Bin, D + Dd, T1, RF, Enc, Str) - end, - {[$<, $<, S | "...>>"], 7 + length(S), 3, More}; - {false, true, Prefix} -> - S = io_lib:write_string(Prefix, $"), %" - More = fun(T1, Dd) -> - ?FUNCTION_NAME(Bin, D + Dd, T1, RF, Enc, Str) - end, - {[$<, $<, S | "/utf8...>>"], 12 + io_lib:chars_length(S), 3, More}; - false -> - case io_lib:write_binary(Bin, D, T) of - {S, <<>>} -> - {{bin, S}, iolist_size(S), 0, no_more}; - {S, _Rest} -> - More = fun(T1, Dd) -> - ?FUNCTION_NAME(Bin, D + Dd, T1, RF, Enc, Str) - end, - {{bin, S}, iolist_size(S), 3, More} - end - end. - -print_length_map(Map, 1, _T, RF, Enc, Str) -> - More = fun(T1, Dd) -> ?FUNCTION_NAME(Map, 1 + Dd, T1, RF, Enc, Str) end, - {"#{...}", 6, 3, More}; -print_length_map(Map, D, T, RF, Enc, Str) when is_map(Map) -> - Next = maps:next(maps:iterator(Map)), - PairsS = print_length_map_pairs(Next, D, D - 1, tsub(T, 3), RF, Enc, Str), - {Len, Dots} = list_length(PairsS, 3, 0), - {{map, PairsS}, Len, Dots, no_more}. - -print_length_map_pairs(none, _D, _D0, _T, _RF, _Enc, _Str) -> - []; -print_length_map_pairs(Term, D, D0, T, RF, Enc, Str) when D =:= 1; T =:= 0 -> - More = fun(T1, Dd) -> - ?FUNCTION_NAME(Term, D + Dd, D0, T1, RF, Enc, Str) - end, - {dots, 3, 3, More}; -print_length_map_pairs({K, V, Iter}, D, D0, T, RF, Enc, Str) -> - Pair1 = print_length_map_pair(K, V, D0, tsub(T, 1), RF, Enc, Str), - {_, Len1, _, _} = Pair1, - Next = maps:next(Iter), - [Pair1 | - print_length_map_pairs(Next, D - 1, D0, tsub(T, Len1 + 1), RF, Enc, Str)]. - -print_length_map_pair(K, V, D, T, RF, Enc, Str) -> - {_, KL, KD, _} = P1 = printTerm(K, D, T, RF, Enc, Str), - KL1 = KL + 4, - {_, VL, VD, _} = P2 = printTerm(V, D, tsub(T, KL1), RF, Enc, Str), - {{map_pair, P1, P2}, KL1 + VL, KD + VD, no_more}. - -print_length_tuple(Tuple, 1, _T, RF, Enc, Str) -> - More = fun(T1, Dd) -> ?FUNCTION_NAME(Tuple, 1 + Dd, T1, RF, Enc, Str) end, - {"{...}", 5, 3, More}; -print_length_tuple(Tuple, D, T, RF, Enc, Str) -> - L = print_length_tuple1(Tuple, 1, D, tsub(T, 2), RF, Enc, Str), - IsTagged = is_atom(element(1, Tuple)) and (tuple_size(Tuple) > 1), - {Len, Dots} = list_length(L, 2, 0), - {{tuple, IsTagged, L}, Len, Dots, no_more}. - -print_length_tuple1(Tuple, I, _D, _T, _RF, _Enc, _Str) - when I > tuple_size(Tuple) -> - []; -print_length_tuple1(Tuple, I, D, T, RF, Enc, Str) when D =:= 1; T =:= 0 -> - More = fun(T1, Dd) -> ?FUNCTION_NAME(Tuple, I, D + Dd, T1, RF, Enc, Str) end, - {dots, 3, 3, More}; -print_length_tuple1(Tuple, I, D, T, RF, Enc, Str) -> - E = element(I, Tuple), - T1 = tsub(T, 1), - {_, Len1, _, _} = Elem1 = printTerm(E, D - 1, T1, RF, Enc, Str), - T2 = tsub(T1, Len1), - [Elem1 | print_length_tuple1(Tuple, I + 1, D - 1, T2, RF, Enc, Str)]. - -print_length_record(Tuple, 1, _T, RF, RDefs, Enc, Str) -> - More = fun(T1, Dd) -> - ?FUNCTION_NAME(Tuple, 1 + Dd, T1, RF, RDefs, Enc, Str) - end, - {"{...}", 5, 3, More}; -print_length_record(Tuple, D, T, RF, RDefs, Enc, Str) -> - Name = [$# | write_atom(element(1, Tuple), Enc)], - NameL = io_lib:chars_length(Name), - T1 = tsub(T, NameL + 2), - L = print_length_fields(RDefs, D - 1, T1, Tuple, 2, RF, Enc, Str), - {Len, Dots} = list_length(L, NameL + 2, 0), - {{record, [{Name, NameL} | L]}, Len, Dots, no_more}. - -print_length_fields([], _D, _T, Tuple, I, _RF, _Enc, _Str) - when I > tuple_size(Tuple) -> - []; -print_length_fields(Term, D, T, Tuple, I, RF, Enc, Str) - when D =:= 1; T =:= 0 -> - More = fun(T1, Dd) -> - ?FUNCTION_NAME(Term, D + Dd, T1, Tuple, I, RF, Enc, Str) - end, - {dots, 3, 3, More}; -print_length_fields([Def | Defs], D, T, Tuple, I, RF, Enc, Str) -> - E = element(I, Tuple), - T1 = tsub(T, 1), - Field1 = print_length_field(Def, D - 1, T1, E, RF, Enc, Str), - {_, Len1, _, _} = Field1, - T2 = tsub(T1, Len1), - [Field1 | - print_length_fields(Defs, D - 1, T2, Tuple, I + 1, RF, Enc, Str)]. - -print_length_field(Def, D, T, E, RF, Enc, Str) -> - Name = write_atom(Def, Enc), - NameL = io_lib:chars_length(Name) + 3, - {_, Len, Dots, _} = - Field = printTerm(E, D, tsub(T, NameL), RF, Enc, Str), - {{field, Name, NameL, Field}, NameL + Len, Dots, no_more}. - -print_length_list(List, D, T, RF, Enc, Str) -> - L = print_length_list1(List, D, tsub(T, 2), RF, Enc, Str), - {Len, Dots} = list_length(L, 2, 0), - {{list, L}, Len, Dots, no_more}. - -print_length_list1([], _D, _T, _RF, _Enc, _Str) -> - []; -print_length_list1(Term, D, T, RF, Enc, Str) when D =:= 1; T =:= 0 -> - More = fun(T1, Dd) -> ?FUNCTION_NAME(Term, D + Dd, T1, RF, Enc, Str) end, - {dots, 3, 3, More}; -print_length_list1([E | Es], D, T, RF, Enc, Str) -> - {_, Len1, _, _} = Elem1 = printTerm(E, D - 1, tsub(T, 1), RF, Enc, Str), - [Elem1 | print_length_list1(Es, D - 1, tsub(T, Len1 + 1), RF, Enc, Str)]; -print_length_list1(E, D, T, RF, Enc, Str) -> - printTerm(E, D - 1, T, RF, Enc, Str). - -list_length([], Acc, DotsAcc) -> - {Acc, DotsAcc}; -list_length([{_, Len, Dots, _} | Es], Acc, DotsAcc) -> - list_length_tail(Es, Acc + Len, DotsAcc + Dots); -list_length({_, Len, Dots, _}, Acc, DotsAcc) -> - {Acc + Len, DotsAcc + Dots}. - -list_length_tail([], Acc, DotsAcc) -> - {Acc, DotsAcc}; -list_length_tail([{_, Len, Dots, _} | Es], Acc, DotsAcc) -> - list_length_tail(Es, Acc + 1 + Len, DotsAcc + Dots); -list_length_tail({_, Len, Dots, _}, Acc, DotsAcc) -> - {Acc + 1 + Len, DotsAcc + Dots}. - -%% ?CHARS printable characters has depth 1. --define(CHARS, 4). - -%% only flat lists are "printable" -visualList(_L, 1, _T, _Enc) -> - false; -visualList(L, _D, T, latin1) when T < 0 -> - io_lib:printable_latin1_list(L); -visualList(L, _D, T, latin1) when T >= 0 -> - N = tsub(T, 2), - case printable_latin1_list(L, N) of - all -> - true; - 0 -> - {L1, _} = lists:split(N, L), - {true, L1}; - _NC -> - false - end; -visualList(L, _D, T, _Unicode) when T >= 0 -> - N = tsub(T, 2), - %% Be careful not to traverse more of L than necessary. - try string:slice(L, 0, N) of - "" -> - false; - Prefix -> - case is_flat(L, lists:flatlength(Prefix)) of - true -> - case string:equal(Prefix, L) of - true -> - io_lib:printable_list(L); - false -> - io_lib:printable_list(Prefix) - andalso {true, Prefix} - end; - false -> - false - end - catch _:_ -> false - end; -visualList(L, _D, T, _Uni) when T < 0 -> - io_lib:printable_list(L). - -is_flat(_L, 0) -> - true; -is_flat([C | Cs], N) when is_integer(C) -> - is_flat(Cs, N - 1); -is_flat(_, _N) -> - false. - -printable_bin0(Bin, D, T, Enc) -> - Len = case D >= 0 of - true -> - %% Use byte_size() also if Enc =/= latin1. - DChars = erlang:min(?CHARS * D, byte_size(Bin)), - case T >= 0 of - true -> - erlang:min(T, DChars); - false -> - DChars - end; - false when T < 0 -> - byte_size(Bin); - false when T >= 0 -> % cannot happen - T - end, - printable_bin(Bin, Len, D, Enc). - -printable_bin(_Bin, 0, _D, _Enc) -> - false; -printable_bin(Bin, Len, D, latin1) -> - N = erlang:min(20, Len), - L = binary_to_list(Bin, 1, N), - case printable_latin1_list(L, N) of - all when N =:= byte_size(Bin) -> - {true, L}; - all when N =:= Len -> % N < byte_size(Bin) - {true, true, L}; - all -> - case printable_bin1(Bin, 1 + N, Len - N) of - 0 when byte_size(Bin) =:= Len -> - {true, binary_to_list(Bin)}; - NC when D > 0, Len - NC >= D -> - {true, true, binary_to_list(Bin, 1, Len - NC)}; - NC when is_integer(NC) -> - false - end; - NC when is_integer(NC), D > 0, N - NC >= D -> - {true, true, binary_to_list(Bin, 1, N - NC)}; - NC when is_integer(NC) -> - false - end; -printable_bin(Bin, Len, D, _Uni) -> - case valid_utf8(Bin, Len) of - true -> - case printable_unicode(Bin, Len, [], io:printable_range()) of - {_, <<>>, L} -> - {byte_size(Bin) =:= length(L), L}; - {NC, Bin1, L} when D > 0, Len - NC >= D -> - {byte_size(Bin) - byte_size(Bin1) =:= length(L), true, L}; - {_NC, _Bin, _L} -> - false - end; - false -> - printable_bin(Bin, Len, D, latin1) - end. - -printable_bin1(_Bin, _Start, 0) -> - 0; -printable_bin1(Bin, Start, Len) -> - N = erlang:min(10000, Len), - L = binary_to_list(Bin, Start, Start + N - 1), - case printable_latin1_list(L, N) of - all -> - printable_bin1(Bin, Start + N, Len - N); - NC when is_integer(NC) -> - Len - (N - NC) - end. - -%% -> all | integer() >=0. Adopted from io_lib.erl. -printable_latin1_list([_ | _], 0) -> 0; -printable_latin1_list([C | Cs], N) when C >= $\s, C =< $~ -> - printable_latin1_list(Cs, N - 1); -printable_latin1_list([C | Cs], N) when C >= $\240, C =< $\377 -> - printable_latin1_list(Cs, N - 1); -printable_latin1_list([$\n | Cs], N) -> printable_latin1_list(Cs, N - 1); -printable_latin1_list([$\r | Cs], N) -> printable_latin1_list(Cs, N - 1); -printable_latin1_list([$\t | Cs], N) -> printable_latin1_list(Cs, N - 1); -printable_latin1_list([$\v | Cs], N) -> printable_latin1_list(Cs, N - 1); -printable_latin1_list([$\b | Cs], N) -> printable_latin1_list(Cs, N - 1); -printable_latin1_list([$\f | Cs], N) -> printable_latin1_list(Cs, N - 1); -printable_latin1_list([$\e | Cs], N) -> printable_latin1_list(Cs, N - 1); -printable_latin1_list([], _) -> all; -printable_latin1_list(_, N) -> N. - -valid_utf8(<<>>, _) -> - true; -valid_utf8(_, 0) -> - true; -valid_utf8(<<_/utf8, R/binary>>, N) -> - valid_utf8(R, N - 1); -valid_utf8(_, _) -> - false. - -printable_unicode(<> = Bin, I, L, Range) when I > 0 -> - case printable_char(C, Range) of - true -> - printable_unicode(R, I - 1, [C | L], Range); - false -> - {I, Bin, lists:reverse(L)} - end; -printable_unicode(Bin, I, L, _) -> - {I, Bin, lists:reverse(L)}. - -printable_char($\n, _) -> true; -printable_char($\r, _) -> true; -printable_char($\t, _) -> true; -printable_char($\v, _) -> true; -printable_char($\b, _) -> true; -printable_char($\f, _) -> true; -printable_char($\e, _) -> true; -printable_char(C, latin1) -> - C >= $\s andalso C =< $~ orelse - C >= 16#A0 andalso C =< 16#FF; -printable_char(C, unicode) -> - C >= $\s andalso C =< $~ orelse - C >= 16#A0 andalso C < 16#D800 orelse - C > 16#DFFF andalso C < 16#FFFE orelse - C > 16#FFFF andalso C =< 16#10FFFF. - -write_atom(A, latin1) -> - io_lib:write_atom_as_latin1(A); -write_atom(A, _Uni) -> - io_lib:write_atom(A). - -write_string(S, latin1) -> - io_lib:write_latin1_string(S, $"); %" -write_string(S, _Uni) -> - io_lib:write_string(S, $"). %" - -expand({_, _, _Dots = 0, no_more} = If, _T, _Dd) -> If; -expand({{tuple, IsTagged, L}, _Len, _, no_more}, T, Dd) -> - {NL, NLen, NDots} = expand_list(L, T, Dd, 2), - {{tuple, IsTagged, NL}, NLen, NDots, no_more}; -expand({{map, Pairs}, _Len, _, no_more}, T, Dd) -> - {NPairs, NLen, NDots} = expand_list(Pairs, T, Dd, 3), - {{map, NPairs}, NLen, NDots, no_more}; -expand({{map_pair, K, V}, _Len, _, no_more}, T, Dd) -> - {_, KL, KD, _} = P1 = expand(K, tsub(T, 1), Dd), - KL1 = KL + 4, - {_, VL, VD, _} = P2 = expand(V, tsub(T, KL1), Dd), - {{map_pair, P1, P2}, KL1 + VL, KD + VD, no_more}; -expand({{record, [{Name, NameL} | L]}, _Len, _, no_more}, T, Dd) -> - {NL, NLen, NDots} = expand_list(L, T, Dd, NameL + 2), - {{record, [{Name, NameL} | NL]}, NLen, NDots, no_more}; -expand({{field, Name, NameL, Field}, _Len, _, no_more}, T, Dd) -> - F = {_S, L, Dots, _} = expand(Field, tsub(T, NameL), Dd), - {{field, Name, NameL, F}, NameL + L, Dots, no_more}; -expand({_, _, _, More}, T, Dd) -> - More(T, Dd). - -expand_list(Ifs, T, Dd, L0) -> - L = expand_list(Ifs, tsub(T, L0), Dd), - {Len, Dots} = list_length(L, L0, 0), - {L, Len, Dots}. - -expand_list([], _T, _Dd) -> - []; -expand_list([If | Ifs], T, Dd) -> - {_, Len1, _, _} = Elem1 = expand(If, tsub(T, 1), Dd), - [Elem1 | expand_list(Ifs, tsub(T, Len1 + 1), Dd)]; -expand_list({_, _, _, More}, T, Dd) -> - More(T, Dd). - -%% Make sure T does not change sign. -tsub(T, _) when T < 0 -> T; -tsub(T, E) when T >= E -> T - E; -tsub(_, _) -> 0. - -%% Throw 'no_good' if the indentation exceeds half the line length -%% unless there is room for M characters on the line. - -cind({_S, Len, _, _}, Col, Ll, M, Ind, LD, W) when Len < Ll - Col - LD, - Len + W + LD =< M -> - Ind; -cind({{list, L}, _Len, _, _}, Col, Ll, M, Ind, LD, W) -> - cind_list(L, Col + 1, Ll, M, Ind, LD, W + 1); -cind({{tuple, true, L}, _Len, _, _}, Col, Ll, M, Ind, LD, W) -> - cind_tag_tuple(L, Col, Ll, M, Ind, LD, W + 1); -cind({{tuple, false, L}, _Len, _, _}, Col, Ll, M, Ind, LD, W) -> - cind_list(L, Col + 1, Ll, M, Ind, LD, W + 1); -cind({{map, Pairs}, _Len, _, _}, Col, Ll, M, Ind, LD, W) -> - cind_map(Pairs, Col + 2, Ll, M, Ind, LD, W + 2); -cind({{record, [{_Name, NLen} | L]}, _Len, _, _}, Col, Ll, M, Ind, LD, W) -> - cind_record(L, NLen, Col, Ll, M, Ind, LD, W + NLen + 1); -cind({{bin, _S}, _Len, _, _}, _Col, _Ll, _M, Ind, _LD, _W) -> - Ind; -cind({_S, _Len, _, _}, _Col, _Ll, _M, Ind, _LD, _W) -> - Ind. - -cind_tag_tuple([{_Tag, Tlen, _, _} | L], Col, Ll, M, Ind, LD, W) -> - TagInd = Tlen + 2, - Tcol = Col + TagInd, - if - Ind > 0, TagInd > Ind -> - Col1 = Col + Ind, - if - M + Col1 =< Ll; Col1 =< Ll div 2 -> - cind_tail(L, Col1, Tcol, Ll, M, Ind, LD, W + Tlen); - true -> - throw(no_good) - end; - M + Tcol < Ll; Tcol < Ll div 2 -> - cind_list(L, Tcol, Ll, M, Ind, LD, W + Tlen + 1); - true -> - throw(no_good) - end. - -cind_map([P | Ps], Col, Ll, M, Ind, LD, W) -> - PW = cind_pair(P, Col, Ll, M, Ind, last_depth(Ps, LD), W), - cind_pairs_tail(Ps, Col, Col + PW, Ll, M, Ind, LD, W + PW); -cind_map(_, _Col, _Ll, _M, Ind, _LD, _W) -> - Ind. % cannot happen - -cind_pairs_tail([{_, Len, _, _} = P | Ps], Col0, Col, Ll, M, Ind, LD, W) -> - LD1 = last_depth(Ps, LD), - ELen = 1 + Len, - if - LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM_PAIR(P); - LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM_PAIR(P) -> - cind_pairs_tail(Ps, Col0, Col + ELen, Ll, M, Ind, LD, W + ELen); - true -> - PW = cind_pair(P, Col0, Ll, M, Ind, LD1, 0), - cind_pairs_tail(Ps, Col0, Col0 + PW, Ll, M, Ind, LD, PW) - end; -cind_pairs_tail(_, _Col0, _Col, _Ll, _M, Ind, _LD, _W) -> - Ind. - -cind_pair({{map_pair, _Key, _Value}, Len, _, _} = Pair, Col, Ll, M, _Ind, LD, W) - when Len < Ll - Col - LD, Len + W + LD =< M -> - if - ?ATM_PAIR(Pair) -> - Len; - true -> - Ll - end; -cind_pair({{map_pair, K, V}, _Len, _, _}, Col0, Ll, M, Ind, LD, W0) -> - cind(K, Col0, Ll, M, Ind, LD, W0), - I = map_value_indent(Ind), - cind(V, Col0 + I, Ll, M, Ind, LD, 0), - Ll. - -map_value_indent(TInd) -> - case TInd > 0 of - true -> - TInd; - false -> - 4 - end. - -cind_record([F | Fs], Nlen, Col0, Ll, M, Ind, LD, W0) -> - Nind = Nlen + 1, - {Col, W} = cind_rec(Nind, Col0, Ll, M, Ind, W0), - FW = cind_field(F, Col, Ll, M, Ind, last_depth(Fs, LD), W), - cind_fields_tail(Fs, Col, Col + FW, Ll, M, Ind, LD, W + FW); -cind_record(_, _Nlen, _Col, _Ll, _M, Ind, _LD, _W) -> - Ind. - -cind_fields_tail([{_, Len, _, _} = F | Fs], Col0, Col, Ll, M, Ind, LD, W) -> - LD1 = last_depth(Fs, LD), - ELen = 1 + Len, - if - LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM_FLD(F); - LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM_FLD(F) -> - cind_fields_tail(Fs, Col0, Col + ELen, Ll, M, Ind, LD, W + ELen); - true -> - FW = cind_field(F, Col0, Ll, M, Ind, LD1, 0), - cind_fields_tail(Fs, Col0, Col + FW, Ll, M, Ind, LD, FW) - end; -cind_fields_tail(_, _Col0, _Col, _Ll, _M, Ind, _LD, _W) -> - Ind. - -cind_field({{field, _N, _NL, _F}, Len, _, _} = Fl, Col, Ll, M, _Ind, LD, W) - when Len < Ll - Col - LD, Len + W + LD =< M -> - if - ?ATM_FLD(Fl) -> - Len; - true -> - Ll - end; -cind_field({{field, _Name, NameL, F}, _Len, _, _}, Col0, Ll, M, Ind, LD, W0) -> - {Col, W} = cind_rec(NameL, Col0, Ll, M, Ind, W0 + NameL), - cind(F, Col, Ll, M, Ind, LD, W), - Ll. - -cind_rec(RInd, Col0, Ll, M, Ind, W0) -> - Nl = (Ind > 0) and (RInd > Ind), - DCol = case Nl of - true -> Ind; - false -> RInd - end, - Col = Col0 + DCol, - if - M + Col =< Ll; Col =< Ll div 2 -> - W = case Nl of - true -> 0; - false -> W0 - end, - {Col, W}; - true -> - throw(no_good) - end. - -cind_list({dots, _, _, _}, _Col0, _Ll, _M, Ind, _LD, _W) -> - Ind; -cind_list([E | Es], Col0, Ll, M, Ind, LD, W) -> - WE = cind_element(E, Col0, Ll, M, Ind, last_depth(Es, LD), W), - cind_tail(Es, Col0, Col0 + WE, Ll, M, Ind, LD, W + WE). - -cind_tail([], _Col0, _Col, _Ll, _M, Ind, _LD, _W) -> - Ind; -cind_tail([{_, Len, _, _} = E | Es], Col0, Col, Ll, M, Ind, LD, W) -> - LD1 = last_depth(Es, LD), - ELen = 1 + Len, - if - LD1 =:= 0, ELen + 1 < Ll - Col, W + ELen + 1 =< M, ?ATM(E); - LD1 > 0, ELen < Ll - Col - LD1, W + ELen + LD1 =< M, ?ATM(E) -> - cind_tail(Es, Col0, Col + ELen, Ll, M, Ind, LD, W + ELen); - true -> - WE = cind_element(E, Col0, Ll, M, Ind, LD1, 0), - cind_tail(Es, Col0, Col0 + WE, Ll, M, Ind, LD, WE) - end; -cind_tail({dots, _, _, _}, _Col0, _Col, _Ll, _M, Ind, _LD, _W) -> - Ind; -cind_tail({_, Len, _, _} = E, _Col0, Col, Ll, M, Ind, LD, W) - when Len + 1 < Ll - Col - (LD + 1), - Len + 1 + W + (LD + 1) =< M, - ?ATM(E) -> - Ind; -cind_tail(E, _Col0, Col, Ll, M, Ind, LD, _W) -> - cind(E, Col, Ll, M, Ind, LD + 1, 0). - -cind_element({_, Len, _, _} = E, Col, Ll, M, _Ind, LD, W) - when Len < Ll - Col - LD, Len + W + LD =< M, ?ATM(E) -> - Len; -cind_element(E, Col, Ll, M, Ind, LD, W) -> - cind(E, Col, Ll, M, Ind, LD, W), - Ll. - -last_depth([_ | _], _LD) -> - 0; -last_depth(_, LD) -> - LD + 1. - -while_fail([], _F, V) -> - V; -while_fail([A | As], F, V) -> - try F(A) catch _ -> while_fail(As, F, V) end. - -%% make a string of N spaces -indent(N) when is_integer(N), N > 0 -> - chars($\s, N - 1). - -%% prepend N spaces onto Ind -indent(1, Ind) -> % Optimization of common case - [$\s | Ind]; -indent(4, Ind) -> % Optimization of common case - S2 = [$\s, $\s], - [S2, S2 | Ind]; -indent(N, Ind) when is_integer(N), N > 0 -> - [chars($\s, N) | Ind]. - -%% A deep version of string:chars/2 -chars(_C, 0) -> - []; -chars(C, 2) -> - [C, C]; -chars(C, 3) -> - [C, C, C]; -chars(C, N) when (N band 1) =:= 0 -> - S = chars(C, N bsr 1), - [S | S]; -chars(C, N) -> - S = chars(C, N bsr 1), - [C, S | S]. - -get_option(Key, TupleList, Default) -> - case lists:keyfind(Key, 1, TupleList) of - false -> Default; - {Key, Value} -> Value; - _ -> Default - end.