As usual, the answer is yes. And even better, it's relatively easy.
First we'll leverage the very helpful GeneralUtilities`PrettyForm. It is implemented in terms of a function GeneralUtilities`PrettyFormBoxes that we can use to directly construct the FE boxes of the expression. And even better we can use this to set the max line width (which in general I'll set to 1 to get full unwrapping).
It puts a bit of unnecessary formatting on that we'll strip off, but other than this it's pretty direct from the boxes to the string through FrontEnd`ExportPacket:
prettyString[expr_,
elementsPerLine : _?IntegerQ : 1,
dataType : TextData | BoxData : BoxData,
cellStyle : _String : "Input",
ops : OptionsPattern[Cell]
] :=
StringDelete[
FrontEndExecute[
FrontEnd`ExportPacket[
Cell[
dataType@
GeneralUtilities`PrettyFormBoxes[expr, elementsPerLine] /.
TemplateBox[
{sym_, ___},
"DefinitionSymbol",
___
] :> sym,
cellStyle,
ops,
PageWidth -> Infinity
],
"PlainText"
]
][[1]],
"\\" ~~ "\n"
]
And making something that'd be a bit annoying to format through some clever recursive scheme.
assoc =
With[{a = AssociationThread[RandomWord[10], RandomReal[{}, 10]]},
Append[a,
"a" -> Append[Take[a, 3],
GeneralUtilities`PrettyFormBoxes -> Take[a, -3]]]
];
Then looking at the relative results:
ToString[assoc, InputForm]
"<|\"impotence\" -> 0.7912604322028038, \"enfeeble\" -> \
0.5739318538887597, \"untangling\" -> 0.7293180075252037, \
\"stitched\" -> 0.9186439937018753, \"picaresque\" -> \
0.12956430307141908, \"reinterpret\" -> 0.011950593964761058, \
\"wheedler\" -> 0.20784766900413043, \"convulse\" -> \
0.38142965804171935, \"acquisitive\" -> 0.9234994949102955, \"oration\
\" -> 0.59873274050806, \"a\" -> <|\"impotence\" -> \
0.7912604322028038, \"enfeeble\" -> 0.5739318538887597, \
\"untangling\" -> 0.7293180075252037, \
GeneralUtilities`PrettyFormBoxes -> <|\"convulse\" -> \
0.38142965804171935, \"acquisitive\" -> 0.9234994949102955, \"oration\
\" -> 0.59873274050806|>|>|>"
prettyString[assoc]
"<|
\"impotence\" -> 0.7912604322028038,
\"enfeeble\" -> 0.5739318538887597,
\"untangling\" -> 0.7293180075252037,
\"stitched\" -> 0.9186439937018753,
\"picaresque\" -> 0.12956430307141908,
\"reinterpret\" -> 0.011950593964761058,
\"wheedler\" -> 0.20784766900413043,
\"convulse\" -> 0.38142965804171935,
\"acquisitive\" -> 0.9234994949102955,
\"oration\" -> 0.59873274050806,
\"a\" -> <|
\"impotence\" -> 0.7912604322028038,
\"enfeeble\" -> 0.5739318538887597,
\"untangling\" -> 0.7293180075252037,
GeneralUtilities`PrettyFormBoxes -> <|
\"convulse\" -> \
0.38142965804171935,
\"acquisitive\" -> 0.9234994949102955,
\"oration\" -> 0.59873274050806
|>
|>
|>"
The latter is, at least to me, much more readable.
And since we're just using the FE for this we can play with the FE options to easily do things like:
prettyString[assoc,
"Output",
ExportAutoReplacements -> {
"<" -> "(-", ">" -> "-)",
"|" -> ":", " " -> "\t",
"\[Rule]" -> "-->"
}
]
"(-:
impotence --> 0.7912604322028038,
enfeeble --> 0.5739318538887597,
untangling --> 0.7293180075252037,
stitched --> 0.9186439937018753,
picaresque --> 0.12956430307141908,
reinterpret --> 0.011950593964761058,
wheedler --> 0.20784766900413043,
convulse --> 0.38142965804171935,
acquisitive --> 0.9234994949102955,
oration --> 0.59873274050806,
a --> (-:
impotence --> 0.7912604322028038,
enfeeble --> 0.5739318538887597,
untangling --> 0.7293180075252037,
GeneralUtilities`PrettyFormBoxes --> (-:
convulse --> 0.38142965804171935,
acquisitive --> 0.9234994949102955,
oration --> 0.59873274050806
:-)
:-)
:-)"
And finally this works in the same way as it always has with ToString:
prettyString@
Unevaluated@prettyString[
assoc,
"Output",
ExportAutoReplacements -> {
"<" -> "(-", ">" -> "-)",
"|" -> ":", " " -> "\t",
"\[Rule]" -> "-->"
}
]
"prettyString[
assoc,
\"Output\",
ExportAutoReplacements -> {
\"<\" -> \"(-\",
\">\" -> \"-)\",
\"|\" -> \":\",
\" \" -> \"\\t\",
\"->\" -> \"-->\"
}
]"
Keep in mind, though, that this is much, much slower and should only be used when nice formatting is necessary:
prettyString@assoc // RepeatedTiming // First
0.05
ToString[assoc, InputForm] // RepeatedTiming // First
0.000093
ExportMultipleCellsOptions. I had the"PageWidth"of that set belowInfinityfor a bit and it broke everything because everything tried to export to that width. – b3m2a1 Nov 27 '18 at 09:36