diff --git a/src/trunc_io.erl b/src/trunc_io.erl index 2ddc433..1a9d272 100644 --- a/src/trunc_io.erl +++ b/src/trunc_io.erl @@ -40,8 +40,9 @@ format(String, Args, Max) -> Maxlen = Max - length(String), format(Parts, Args, Maxlen, [], []). -format([], _Args, _Max, Acc, ArgAcc) -> - io_lib:format(lists:flatten(lists:reverse(Acc)), lists:reverse(ArgAcc)); +format([], _Args, Max, Acc, ArgAcc) -> + FmtArgs = resolve_futures(Max, ArgAcc), + io_lib:format(lists:flatten(lists:reverse(Acc)), lists:reverse(FmtArgs)); format([[] | T], Args, Max, Acc, ArgAcc) -> % discard the null list generated by split format(T, Args, Max, Acc, ArgAcc); @@ -62,21 +63,21 @@ format([[$~|H]| T], [AH | AT], Max, Acc, ArgAcc) when length(H) == 1 -> case H of _ when H == "p"; H == "w"; H == "s" -> %okay, these are prime candidates for rewriting - {String, Length} = case print(AH, Max) of + case print(AH, Max) of {_Res, Max} when AT /= [] -> - % this isn't the last argument, but it consumed all available space, give it half instead - print(AH, Max div 2); - {Res, Len} -> - {Res, Len} - end, - {Value, RealLen} = case H of - "s" -> - % strip off the doublequotes - {string:substr(String, 2, length(String) -2), Length -2}; - _ -> - {String, Length} - end, - format(T, AT, Max + 2 - RealLen, ["~s" | Acc], [Value | ArgAcc]); + % this isn't the last argument, but it consumed all available space + % delay calculating the print size until the end + format(T, AT, Max + 2, ["~s" | Acc], [{future, AH} | ArgAcc]); + {String, Length} -> + {Value, RealLen} = case H of + "s" -> + % strip off the doublequotes + {string:substr(String, 2, length(String) -2), Length -2}; + _ -> + {String, Length} + end, + format(T, AT, Max + 2 - RealLen, ["~s" | Acc], [Value | ArgAcc]) + end; _ -> % whatever, just pass them on through format(T, AT, Max, [[$~ | H], Acc], [AH | ArgAcc]) @@ -87,22 +88,26 @@ format([[$~|H]| T], [AH | AT], Max, Acc, ArgAcc) -> case re:run(H, "^(?:-??(\\d+|\\*)\\.|)(?:-??(\\d+|\\*)\\.|)(-??\\d+|\\*|)(t|)([cfegswpWPBX#bx+ni])$", [{capture, all_but_first, list}]) of {match, [_F, _P, _Pad, _Mod, C]} when C == "p"; C=="w"; C=="s" -> %okay, these are prime candidates for rewriting - {String, Length} = case print(AH, Max) of + case print(AH, Max) of {_Res, Max} when AT /= [] -> - % this isn't the last argument, but it consumed all available space, give it half instead - print(AH, Max div 2); - {Res, Len} -> - {Res, Len} - end, - {Value, RealLen} = case H of - "s" -> - % strip off the doublequotes - {string:substr(String, 2, length(String) -2), Length -2}; - _ -> - {String, Length} - end, - format(T, AT, Max + length(H) + 1 - RealLen, ["~s" | Acc], [Value | ArgAcc]); + % this isn't the last argument, but it consumed all available space + % delay calculating the print size until the end + format(T, AT, Max + length(H) + 1, ["~s" | Acc], [{future, AH} | ArgAcc]); + {String, Length} -> + {Value, RealLen} = case H of + "s" -> + % strip off the doublequotes + {string:substr(String, 2, length(String) -2), Length -2}; + _ -> + {String, Length} + end, + format(T, AT, Max + length(H) + 1 - RealLen, ["~s" | Acc], [Value | ArgAcc]) + end; {match, [_F, _P, _Pad, _Mod, C]} when C == "P"; C=="W" -> + % these crazy ones consume TWO arguments, just pass them through + % because W and P implicitly limit size so we trust the user knows + % what they're doing. Unfortunately we can't change Max at all because + % depth based limits are not of known length. [AH2 | AT2] = AT, format(T, AT2, Max, [[$~|H]|Acc], [AH2, AH |ArgAcc]); {match, _} -> @@ -114,6 +119,17 @@ format([[$~|H]| T], [AH | AT], Max, Acc, ArgAcc) -> format([H | T], Args, Max, Acc, ArgAcc) -> format(T, Args, Max, [H | Acc], ArgAcc). +%% for all the really big terms encountered in a format/3 call, try to give each of them an equal share +resolve_futures(Max, Args) -> + Count = length(lists:filter(fun({future, _}) -> true; (_) -> false end, Args)), + case Count of + 0 -> + Args; + _ -> + SingleFmt = Max div Count, + lists:map(fun({future, Value}) -> element(1, print(Value, SingleFmt)); (X) -> X end, Args) + end. + %% @doc Returns an flattened list containing the ASCII representation of the given %% term. -spec fprint(term(), pos_integer()) -> string().