2012-03-15 83 views
8

我正在尝试使用Text.PrettyPrint生成Javascript。问题在于nest与另一个打印的元素相邻时会产生巨大的缩进。例如,在这样的代码:在第9栏第Text.PrettyPrint:从左边距开始缩进

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开始于test(因为var <+> equals <> empty到它的左侧),因此,它的随后的线被9 + 4 = 13列缩进:

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

有没有一种方法来呈现从左边距缺口,因此,上述将被代替呈现为

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

+2

Daan Leijen漂亮的打印机'wl-pprint'比Hughes Peyton-Jones漂亮的打印机具有更灵活的缩进处理。您可能需要考虑使用它。有关文档,请参阅手册,它比Haddock文档更详细。 – 2012-03-15 09:06:14

+1

我认为'wl-pprint'将会变成正确的解决方案 - 我已经准备好接受这个作为答案,如果你这样发布的话。 – Cactus 2012-03-18 12:53:55

+0

@Cactus你有没有使用'wl-pprint'工作?如果是这样,为什么不添加一个答案呢? – Alec 2016-12-29 04:46:53

回答

2

的解决方案是确实使用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-annotatedwl-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; 
} 
2
 
offset = 1 + length (render $ var <+> equals) 
hang empty (negate offset) test 
+0

似乎没有工作;事实上,它看起来就像没有额外的“挂起”。另外,它会不会有一个可怕的性能影响? – Cactus 2012-03-15 08:43:38

+0

@Cactus - grr,降价吃了我的'<+>'运营商。固定 – 2012-03-15 21:48:38

+0

不,这不是'<+>'缺乏',我想出了自己的一部分。但我得到相同的输出。 – Cactus 2012-03-18 12:52:55

1

你可以实现通过应用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 
+1

当然,我可以做到这一点 - 问题是我希望整个'function {...}'仍然是一个'Doc'(不是一行代码)。这样,'漂亮'仍然可以为我选择一个漂亮的布局(即'function {..}'是否需要跨越多行)。 – Alec 2016-12-30 21:48:46

+0

没错,但我找不到另一种(更优雅)的方式。这里的主要问题是你把'var'' Doc''beside_放在'fun'' Doc'中,这会根据已连接的文档的长度来设置'fun'的缩进偏移量。相反,将两个文档连接在一起会使文档中的缩进偏移保持不变,除非两个文档中的一个是“Nest”。 – 2016-12-31 21:02:44