I'm trying to generate Javascript using Text.PrettyPrint
. The problem is that nest
produces huge indentation when put next to another prettyprinted element. For example, in this code:
import Text.PrettyPrint fun :: Doc fun = vcat [ text "function" <+> lbrace , nest 4 $ vcat $ replicate 5 $ text "// foo" , rbrace ] var :: Doc var = text "var" <+> text "x" test :: Doc test = var <+> equals <+> fun <> semi
fun
starts on column 9 in test
(because of var <+> equals <> empty
to the left of it), and thus its subsequent lines are indented by 9+4=13 columns:
var x = function { // foo // foo // foo // foo // foo };
Is there a way to render indentations from the left margin, so that the above would be rendered instead as
var x = function { // foo // foo // foo // foo // foo };
?
3 Answers
Answers 1
The solution is indeed to use wl-pprint
(and replace nest
with indent
). Then, the code given yields
var x = function { // foo // foo // foo // foo // foo };
as desired. For anyone still intent on working something with trying to hack on pretty
, note that although the constructors for Doc
aren't exposed, you can still get at them via Generic
with -XPatternSynonyms
:
-- | Means of exposing the data constructors of `Doc` from `pretty` pattern GEmpty = M1 (L1 (L1 (L1 (M1 U1)))) pattern GNilAbove doc = M1 (L1 (L1 (R1 (M1 (M1 (K1 doc)))))) pattern GTextBeside d doc = M1 (L1 (R1 (L1 (M1 (M1 (K1 d) :*: M1 (K1 doc)))))) pattern GNest n doc = M1 (L1 (R1 (R1 (M1 (M1 (K1 n) :*: M1 (K1 doc)))))) pattern GUnion ldoc rdoc = M1 (R1 (L1 (L1 (M1 (M1 (K1 ldoc) :*: M1 (K1 rdoc)))))) pattern GNoDoc = M1 (R1 (L1 (R1 (M1 U1)))) pattern GBeside ldoc s rdoc = M1 (R1 (R1 (L1 (M1 (M1 (K1 ldoc) :*: M1 (K1 s) :*: M1 (K1 rdoc)))))) pattern GAbove ldoc b rdoc = M1 (R1 (R1 (R1 (M1 (M1 (K1 ldoc) :*: M1 (K1 b) :*: M1 (K1 rdoc))))))
The problem is mostly not violating any of the many invariants the library has under the hood.
As a side note, I also found wl-pprint-annotated
, a modern re-write of wl-pprint
, with which one has access to underlying data constructors (at the cost of needing to keep in mind the invariants involved). This is actually the package I will end up using.
In particular, it lets me make this sort of brace block such that if it is small enough it will go on only one line:
-- | Asserts a 'Doc a' cannot render on multiple lines. oneLine :: Doc a -> Bool oneLine (WL.FlatAlt d _) = oneLine d oneLine (WL.Cat a b) = oneLine a && oneLine b oneLine (WL.Union a b) = oneLine a && oneLine b oneLine (WL.Annotate _ d) = oneLine d oneLine WL.Line = False oneLine _ = True -- | Make a curly-brace delimited block. When possible, permit fitting everything on one line block :: Doc a -> Doc a block b | oneLine b = hsep ["{", b, "}"] `WL.Union` vsep [ "{", indent 2 b, "}" ] | otherwise = vsep [ "{", indent 2 b, "}" ]
Then I get nice results that automatically do or don't span multiple lines:
ghci> "function" <> parens "x" <+> block ("return" <+> "x" <> semi) function(x) { return x; } ghci> "function" <> parens "x" <+> block ("x" <> "++" <> semi <#> "return" <+> "x" <> semi) function(x) { x++; return x; }
Answers 2
offset = 1 + length (render $ var <+> equals) hang empty (negate offset) test
Answers 3
You could achieve the desired result by applying vcat
to a list where the first item includes also the variable definition and assignment.
Example:
fun :: Doc fun = vcat [ var <+> equals <+> text "function" <+> lbrace , nest 4 $ vcat $ replicate 5 $ text "// foo" , rbrace ] var :: Doc var = text "var" <+> text "x" test :: Doc test = fun <> semi
0 comments:
Post a Comment