This is an article about writing the official Dart autoformatter.
Ocaml has a nice library for doing pretty printing that fits how I do pretty printing manually. The abstraction is something called boxes and they can be arbitrarily deeply nested. And boxes have the property of what column they start on and their max column length and how line breaks should be added to it if the column length is exceeded. And then when one writes data into boxes they can give break hints, where it’s safe to add a line break. So a box can have the property that all break hints do not become new lines unless the line exceeds the column length and then add a new line at every break hint (that’s how I do my own indenting, just making rows or columns).
I’ve used it for some code gen and it works pretty well, some of the semantics can be confusing at first but as long as your indentation rules fit into that style it Just Works, IME.
What you have in mind sounds like a greedy algorithm. Sometimes there are cases where it is better to insert a line break earlier to have better options later. Example:
void f(int x, int (f)(
int x), int y)
Should be formatted like this:
void f(int x,
int (f)(int x),
Thus, we actually need some A* search algorithm for the best line breaks, which makes it similar to the line breaking algorithm used by TeX.
(as blog article)
It depends on how you design your boxes. I would make the parameters of the function a box, then each parameter its own box with no break hints, and between each parameter box is a break hint. In this way I can get the version you described. The box break strategy allows it go backwards and reformat a whole box if the column width has been exceeded.
I’ve recently dealt with source-to-source transformations, so this was a pleasure to read. Thanks for sharing.
Author is a great writer.