【问题标题】:Haskell Leijen pretty-printer, can't handle indentationHaskell Leijen 漂亮的打印机,无法处理缩进
【发布时间】:2012-11-18 16:00:33
【问题描述】:

我正在为 Lua 开发一个漂亮的打印机,但我一直在处理函数语法的缩进。

我想要的是:如果函数定义足够短,所有定义都可以写在一行中,但如果不是那么短,那么所有body都需要在新行中打印并带有缩进。之后,end 关键字应换行,不要缩进。

我想我并不完全理解 Leijen Pretty-printer 的工作原理,但这就是我目前所掌握的:

import Text.PrettyPrint.Leijen
import System.IO (stdout)

sometext = text "short text" </> text "this is some long text at least long enough for my purposes"
p = parens (align (cat (punctuate (comma <> space) (map text ["first parameter", "second parameter", "third"]))))

doc3 = indent 4 (sep [text "function" </> p, indent 4 sometext, text "end"])

main :: IO ()
main = do
    displayIO stdout (renderPretty 1 133 (doc3 <$> line))
    displayIO stdout (renderPretty 1 134 (doc3 <$> line))
    displayIO stdout (renderPretty 1 40 (doc3 <$> line))

这实际上工作得很好,除了输出的第二部分:

function (first parameter, second parameter, third)
    short text this is some long text at least long enough for my purposes
end

function (first parameter, second parameter, third)     short text this is some long text at least long enough for my purposes end

function (first parameter, 
          second parameter, 
          third)
    short text
    this is some long text at least long enough for my purposes
end

在这里,我不希望在第二个输出中出现 4 个空格缩进。但是我找不到在不破坏其他输出的情况下做到这一点的方法。

任何帮助将不胜感激。

编辑:这是另一个测试用例:

doc4 = sep [ text "if" </> text "test" </> text "then"
           , indent 4 then_body
           , text "elseif" </> elseif_conditions </> text "then"
           , indent 4 elseif_body
           , text "end"
           ]

我想要的是,如果输出足够短,则在同一行打印所有内容(但没有缩进空格)。它在拆分多行时效果很好,但在打印到同一行时会留下多余的空格。

【问题讨论】:

    标签: haskell pretty-print


    【解决方案1】:

    首先让我们尝试以最大宽度开始的每个宽度的文档,并且只打印不同的文档。这将有助于检查 Doc 的行为是否符合您的期望。

    import Text.PrettyPrint.Leijen
    import System.IO (stdout)
    import Data.List
    import Data.Function
    
    testFrom :: Int -> Doc -> IO ()
    testFrom w d = mapM_ printone . nubBy ((==) `on` snd) .map (`showAt` d). reverse $ [1..w] where
       showAt n doc = (n,displayS (renderPretty 1 n doc) "")
       printone (n,xs) = putStrLn (show n ++ ":") >> putStrLn xs
    

    我(现在)认为你想要什么

    现在让我们利用 cmets 中的额外信息。对不起,我第一次误解了你。

    我认为你的意思是允许所有在一行上,但如果不是,至少应该有三个,函数头在第一行,函数体缩进,结尾不缩进。你不介意sometext 是否在断点处跨行拆分,在参数之后等,只要它不是内联的,缩进 4 即可。

    nest 函数允许我们缩进后续行(如果有),而 indent 肯定会放置空格,这就是为什么你有丑陋的额外 4 行。

    所以,你的函数分为三个部分:

    functionhead = text "function" </> p
    sometext = text "short text" 
               </> text "this is some long text at least long enough for my purposes"
    end = text "end"
    

    采用稍微重构的参数制作方式:

    params = parens.align.cat.punctuate (comma <> space).map text 
    p = params ["first parameter", "second parameter", "third"]
    

    用指定的休息时间布置

    如果需要换行,让我们指定它们的布局方式:

    tidyfunction = nest 4 (functionhead <$> sometext) <$> end
    

    这展示了我们希望换行符的方式:

    *Main> testFrom 140 tidy
    140:
    function (first parameter, second parameter, third)
        short text this is some long text at least long enough for my purposes
    end
    73:
    function (first parameter, second parameter, third)
        short text
        this is some long text at least long enough for my purposes
    end
    50:
    function (first parameter, 
              second parameter, 
              third)
        short text
        this is some long text at least long enough for my purposes
    end
    26:
    function
        (first parameter, 
         second parameter, 
         third)
        short text
        this is some long text at least long enough for my purposes
    end
    

    如果合适就全部放在一行上

    漂亮的打印库有一个组合器,如果它适合一行,则可以消除所有换行符,group。让我们将整个代码缩进 4 并分组。

    nicefunction = group $ indent 4 tidyfunction
    

    这给出了:

    *Main> testFrom 140 nicefunction
    140:
        function (first parameter, second parameter, third) short text this is some long text at least long enough for my purposes end
    129:
        function (first parameter, second parameter, third)
            short text this is some long text at least long enough for my purposes
        end
    77:
        function (first parameter, second parameter, third)
            short text
            this is some long text at least long enough for my purposes
        end
    54:
        function (first parameter, 
                  second parameter, 
                  third)
            short text
            this is some long text at least long enough for my purposes
        end
    30:
        function
            (first parameter, 
             second parameter, 
             third)
            short text
            this is some long text at least long enough for my purposes
        end
    

    【讨论】:

    • 这不是我想要的,在你的第三个输出中,end 应该与其他人在同一行(或者如果它足够长,它们应该用换行符分隔)。我认为唯一的方法是将doc "end" 与函数头和函数体放在同一个sep 列表中,但这次nest 破坏了缩进(它缩进了end)。
    • 它应该从第二个跳到第五个。
    • @sinan 完全重写。你怎么看?
    • 太好了,非常感谢。这也向我展示了&lt;$&gt;group 的使用。
    猜你喜欢
    • 2016-07-07
    • 1970-01-01
    • 2018-09-03
    • 1970-01-01
    • 2015-08-27
    • 1970-01-01
    • 2016-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多