Text.PrettyPrint:从左边距开始缩进



我正在尝试使用Text.PrettyPrint生成Javascript。问题是nest在放在另一个预打印的元素旁边时会产生巨大的缩进。例如,在此代码中:

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

funtest中的第9列开始(因为var <+> equals <> empty在它的左边),因此它的后续行缩进9+4=13列:

var x = function {
            // foo
            // foo
            // foo
            // foo
            // foo
        };

有没有一种方法可以从左边距呈现缩进,这样上面的内容就会被呈现为

var x = function {
    // foo
    // foo
    // foo
    // foo
    // foo
};

解决方案实际上是使用wl-pprint(并用indent替换nest)。然后,给定的代码产生

var x = function {
    // foo
    // foo
    // foo
    // foo
    // foo
};

根据需要。对于那些仍然想破解pretty的人来说,请注意,尽管Doc的构造函数没有公开,但你仍然可以通过Generic-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))))))

这个问题主要是没有违反库中的许多不变量中的任何一个。


顺便说一句,我还发现了wl-pprint-annotated,这是对wl-pprint的现代重写,使用它可以访问底层数据构造函数(代价是需要记住所涉及的不变量)。这就是我最终要使用的软件包。

特别是,它让我可以制作这种支撑块,这样,如果它足够小,它只会出现在一行:

-- | 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, "}" ]

然后我得到了很好的结果,可以自动跨多行或不跨多行:

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;
}
offset=1+长度(渲染$var<+>等于)挂起空(抵消偏移)测试

您可以通过将vcat应用于第一项还包括变量定义和赋值的列表来实现所需的结果。

示例:

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

最新更新