Refactor pretty printing to handle the white space at the AST level

We previously determined when to add the whitespace at the token level. That lead to a bunch of complicate switch statements. With this we handle the formatting at the AST level which simplifies the code significantly and it also allows us nicer result.