packages feed

hslua-module-doclayout 0.1.0 → 0.2.0

raw patch · 4 files changed

+514/−134 lines, 4 filesdep ~hsluaPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: hslua

API changes (from Hackage documentation)

+ Foreign.Lua.Module.DocLayout: description :: Text
+ Foreign.Lua.Module.DocLayout: fields :: [Field]
+ Foreign.Lua.Module.DocLayout: functions :: [(Text, HaskellFunction)]
- Foreign.Lua.Module.DocLayout: after_break :: Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: after_break :: HaskellFunction
- Foreign.Lua.Module.DocLayout: before_non_blank :: Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: before_non_blank :: HaskellFunction
- Foreign.Lua.Module.DocLayout: blankline :: Doc Text
+ Foreign.Lua.Module.DocLayout: blankline :: Field
- Foreign.Lua.Module.DocLayout: blanklines :: Int -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: blanklines :: HaskellFunction
- Foreign.Lua.Module.DocLayout: braces :: Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: braces :: HaskellFunction
- Foreign.Lua.Module.DocLayout: brackets :: Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: brackets :: HaskellFunction
- Foreign.Lua.Module.DocLayout: cblock :: Int -> Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: cblock :: HaskellFunction
- Foreign.Lua.Module.DocLayout: chomp :: Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: chomp :: HaskellFunction
- Foreign.Lua.Module.DocLayout: concat :: [Doc Text] -> Optional (Doc Text) -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: concat :: HaskellFunction
- Foreign.Lua.Module.DocLayout: cr :: Doc Text
+ Foreign.Lua.Module.DocLayout: cr :: Field
- Foreign.Lua.Module.DocLayout: double_quotes :: Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: double_quotes :: HaskellFunction
- Foreign.Lua.Module.DocLayout: empty :: Doc Text
+ Foreign.Lua.Module.DocLayout: empty :: Field
- Foreign.Lua.Module.DocLayout: flush :: Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: flush :: HaskellFunction
- Foreign.Lua.Module.DocLayout: hang :: Int -> Doc Text -> Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: hang :: HaskellFunction
- Foreign.Lua.Module.DocLayout: height :: Doc Text -> Lua Int
+ Foreign.Lua.Module.DocLayout: height :: HaskellFunction
- Foreign.Lua.Module.DocLayout: inside :: Doc Text -> Doc Text -> Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: inside :: HaskellFunction
- Foreign.Lua.Module.DocLayout: is_empty :: Doc Text -> Lua Bool
+ Foreign.Lua.Module.DocLayout: is_empty :: HaskellFunction
- Foreign.Lua.Module.DocLayout: lblock :: Int -> Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: lblock :: HaskellFunction
- Foreign.Lua.Module.DocLayout: literal :: Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: literal :: HaskellFunction
- Foreign.Lua.Module.DocLayout: min_offset :: Doc Text -> Lua Int
+ Foreign.Lua.Module.DocLayout: min_offset :: HaskellFunction
- Foreign.Lua.Module.DocLayout: nest :: Int -> Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: nest :: HaskellFunction
- Foreign.Lua.Module.DocLayout: nestle :: Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: nestle :: HaskellFunction
- Foreign.Lua.Module.DocLayout: nowrap :: Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: nowrap :: HaskellFunction
- Foreign.Lua.Module.DocLayout: offset :: Doc Text -> Lua Int
+ Foreign.Lua.Module.DocLayout: offset :: HaskellFunction
- Foreign.Lua.Module.DocLayout: parens :: Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: parens :: HaskellFunction
- Foreign.Lua.Module.DocLayout: peekDoc :: StackIndex -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: peekDoc :: Peeker (Doc Text)
- Foreign.Lua.Module.DocLayout: prefixed :: Text -> Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: prefixed :: HaskellFunction
- Foreign.Lua.Module.DocLayout: pushDoc :: Doc Text -> Lua ()
+ Foreign.Lua.Module.DocLayout: pushDoc :: Pusher (Doc Text)
- Foreign.Lua.Module.DocLayout: quotes :: Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: quotes :: HaskellFunction
- Foreign.Lua.Module.DocLayout: rblock :: Int -> Doc Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: rblock :: HaskellFunction
- Foreign.Lua.Module.DocLayout: real_length :: Text -> Lua Int
+ Foreign.Lua.Module.DocLayout: real_length :: HaskellFunction
- Foreign.Lua.Module.DocLayout: render :: Doc Text -> Optional Int -> Lua Text
+ Foreign.Lua.Module.DocLayout: render :: HaskellFunction
- Foreign.Lua.Module.DocLayout: space :: Doc Text
+ Foreign.Lua.Module.DocLayout: space :: Field
- Foreign.Lua.Module.DocLayout: update_column :: Doc Text -> Int -> Lua Int
+ Foreign.Lua.Module.DocLayout: update_column :: HaskellFunction
- Foreign.Lua.Module.DocLayout: vfill :: Text -> Lua (Doc Text)
+ Foreign.Lua.Module.DocLayout: vfill :: HaskellFunction

Files

CHANGELOG.md view
@@ -3,6 +3,13 @@ `hslua-module-doclayout` uses [PVP Versioning][1]. The changelog is available [on GitHub][2]. +## 0.2.0++* Full test coverage of all provided Lua functions.+* Use documented module, including all function docs in the+  exported Haskell structure.+* Test with all GHC 8 major versions.+ ## 0.1.0  * Initial release.
hslua-module-doclayout.cabal view
@@ -1,6 +1,6 @@ cabal-version:       2.0 name:                hslua-module-doclayout-version:             0.1.0+version:             0.2.0 synopsis:            Lua module wrapping Text.DocLayout. description:         Lua module wrapping @Text.DocLayout@. homepage:            https://github.com/hslua/hslua-module-doclayout@@ -19,7 +19,8 @@                      GHC == 8.2.2                      GHC == 8.4.4                      GHC == 8.6.5-                     GHC == 8.8.3+                     GHC == 8.8.4+                     GHC == 8.10.1  source-repository head   type:                git@@ -28,7 +29,7 @@ library   build-depends:       base           >= 4.9 && < 5                      , doclayout      >= 0.2 && < 0.4-                     , hslua          >= 1.0 && < 1.2+                     , hslua          >= 1.2 && < 1.3                      , text           >= 1.0 && < 1.3   default-language:    Haskell2010   hs-source-dirs:      src
src/Foreign/Lua/Module/DocLayout.hs view
@@ -1,6 +1,8 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {-| Module      : Foreign.Lua.Module.DocLayout@@ -19,6 +21,9 @@   -- * Module     pushModule   , preloadModule+  , description+  , fields+  , functions    -- * Doc constructors and combinators   , after_break@@ -66,14 +71,20 @@ where  import Prelude hiding (concat)+import Control.Monad (forM_) import Data.List (intersperse) import Data.Text (Text)-import Foreign.Lua (Lua, NumResults (..), Optional,-                    Peekable, Pushable, StackIndex)+import Foreign.Lua (Lua, NumResults (..), Peekable, Pushable, StackIndex)+import Foreign.Lua.Call hiding (render)+import Foreign.Lua.Module hiding (preloadModule, pushModule, render)+import Foreign.Lua.Peek+  (Peeker, peekIntegral, peekList, peekString, peekText, toPeeker)+import Foreign.Lua.Push (Pusher, pushBool, pushIntegral, pushText) import Text.DocLayout (Doc, (<+>), ($$), ($+$))  import qualified Data.Text as T import qualified Foreign.Lua as Lua+import qualified Foreign.Lua.Module as Module import qualified Foreign.Lua.Types.Peekable as Lua import qualified Foreign.Lua.Userdata as Lua import qualified Text.DocLayout as Doc@@ -86,91 +97,193 @@ -- Module -- +-- | Textual description of the "doclayout" module.+description :: Text+description = "Plain-text document layouting."++-- | Self-documenting module.+documentedModule :: Module+documentedModule = Module+  { moduleName = "doclayout"+  , moduleFields = fields+  , moduleDescription = description+  , moduleFunctions = functions+  }+ -- | Pushes the @doclayout@ module to the Lua stack. pushModule :: Lua NumResults-pushModule = do-  Lua.newtable-  -- constructors-  Lua.addfield "empty"     empty-  Lua.addfield "blankline" blankline-  Lua.addfield "cr"        cr-  Lua.addfield "space"     space-  Lua.addfunction "after_break" after_break-  Lua.addfunction "before_non_blank" before_non_blank-  Lua.addfunction "blanklines" blanklines-  Lua.addfunction "braces"     braces-  Lua.addfunction "brackets"   brackets-  Lua.addfunction "cblock"     cblock-  Lua.addfunction "chomp"      chomp-  Lua.addfunction "concat"     concat-  Lua.addfunction "double_quotes" double_quotes-  Lua.addfunction "flush"      flush-  Lua.addfunction "hang"       hang-  Lua.addfunction "inside"     inside-  Lua.addfunction "lblock"     lblock-  Lua.addfunction "literal"    literal-  Lua.addfunction "nest"       nest-  Lua.addfunction "nestle"     nestle-  Lua.addfunction "nowrap"     nowrap-  Lua.addfunction "parens"     parens-  Lua.addfunction "quotes"     quotes-  Lua.addfunction "prefixed"   prefixed-  Lua.addfunction "rblock"     rblock-  Lua.addfunction "vfill"      vfill-  -- querying-  Lua.addfunction "is_empty"   is_empty-  Lua.addfunction "offset"     offset-  Lua.addfunction "height"     height-  Lua.addfunction "min_offset" min_offset-  Lua.addfunction "offset"     offset-  Lua.addfunction "real_length" real_length-  Lua.addfunction "update_column" update_column-  -- rendering-  Lua.addfunction "render" render-  return 1+pushModule = 1 <$ pushModule' documentedModule +pushModule' :: Module -> Lua ()+pushModule' mdl = do+  Module.pushModule mdl+  forM_ (moduleFields mdl) $ \field -> do+    pushText (fieldName field)+    fieldPushValue field+    Lua.rawset (Lua.nth 3)+ -- | Add the @doclayout@ module under the given name to the table -- of preloaded packages. preloadModule :: String -> Lua ()-preloadModule = flip Lua.preloadhs pushModule+preloadModule name = Module.preloadModule $+  documentedModule { moduleName = T.pack name } +--+-- Fields+--++-- | Exposed fields.+fields :: [Field]+fields =+  [ blankline+  , cr+  , empty+  , space+  ]++-- | Wrapped and documented 'Doc.blankline' value.+blankline :: Field+blankline = Field+  { fieldName = "blankline"+  , fieldDescription = "Inserts a blank line unless one exists already."+  , fieldPushValue = pushDoc Doc.blankline+  }++-- | Wrapped and documented 'Doc.cr' value.+cr :: Field+cr = Field+  { fieldName = "cr"+  , fieldDescription = "A carriage return. Does nothing if we're at " <>+                       "the beginning of a line; " <>+                       "otherwise inserts a newline."+  , fieldPushValue = pushDoc Doc.cr+  }++-- | Wrapped and documented 'Doc.empty' value.+empty :: Field+empty = Field+  { fieldName = "empty"+  , fieldDescription = "The empty document."+  , fieldPushValue = pushDoc Doc.empty+  }++-- | Wrapped and documented 'Doc.space' value.+space :: Field+space = Field+  { fieldName = "space"+  , fieldDescription = "A breaking (reflowable) space."+  , fieldPushValue = pushDoc Doc.space+  }++--+-- Functions+--++-- | Exposed module functions.+functions :: [(Text, HaskellFunction)]+functions =+  [ -- Constructors+    ("after_break", after_break)+  , ("before_non_blank", before_non_blank)+  , ("blanklines", blanklines)+  , ("braces", braces)+  , ("brackets", brackets)+  , ("cblock", cblock)+  , ("chomp", chomp)+  , ("concat", concat)+  , ("double_quotes", double_quotes)+  , ("flush", flush)+  , ("hang", hang)+  , ("inside", inside)+  , ("lblock", lblock)+  , ("literal", literal)+  , ("nest", nest)+  , ("nestle", nestle)+  , ("nowrap", nowrap)+  , ("parens", parens)+  , ("prefixed", prefixed)+  , ("quotes", quotes)+  , ("rblock", rblock)+  , ("vfill", vfill)+    -- rendering+  , ("render", render)+    -- querying+  , ("is_empty", is_empty)+  , ("height", height)+  , ("min_offset", min_offset)+  , ("offset", offset)+  , ("real_length", real_length)+  , ("update_column", update_column)+  ]++ -- | Render a @'Doc'@. The text is reflowed on breakable spaces -- to match the given line length. Text is not reflowed if the -- line length parameter is omitted or nil.-render :: Doc Text -> Optional Int -> Lua Text-render doc optLength = return $ Doc.render (Lua.fromOptional optLength) doc+render :: HaskellFunction+render = toHsFnPrecursor (flip Doc.render)+  <#> docParam "doc"+  <#> optionalParameter (peekIntegral @Int) "integer" "colwidth" ""+  =#> functionResult pushText "Doc" "rendered doc"+  #? ("Render a @'Doc'@. The text is reflowed on breakable spaces" <>+      "to match the given line length. Text is not reflowed if the" <>+      "line length parameter is omitted or nil.")  -- -- Querying --  -- | @True@ iff the document is empty.-is_empty :: Doc Text -> Lua Bool-is_empty = return . Doc.isEmpty+is_empty :: HaskellFunction+is_empty = toHsFnPrecursor Doc.isEmpty+  <#> docParam "doc"+  =#> booleanResult "`true` iff `doc` is the empty document, `false` otherwise."+  #? "Checks whether a doc is empty."  -- | Returns the width of a @'Doc'@.-offset :: Doc Text -> Lua Int-offset = return . Doc.offset+offset :: HaskellFunction+offset = toHsFnPrecursor Doc.offset+  <#> docParam "doc"+  =#> intResult "doc width"+  #? "Returns the width of a `Doc` as number of characters."  -- | Returns the minimal width of a @'Doc'@ when reflowed at -- breakable spaces.-min_offset :: Doc Text -> Lua Int-min_offset = return . Doc.minOffset+min_offset :: HaskellFunction+min_offset = toHsFnPrecursor Doc.minOffset+  <#> docParam "doc"+  =#> intResult "minimal possible width"+  #? ("Returns the minimal width of a `Doc` when reflowed at " <>+      "breakable spaces.")  -- | Returns the column that would be occupied by the last laid -- out character.-update_column :: Doc Text -> Int -> Lua Int-update_column doc = return . Doc.updateColumn doc+update_column :: HaskellFunction+update_column = toHsFnPrecursor Doc.updateColumn+  <#> docParam "doc"+  <#> intParam "i"+  =#> intResult "column number"+  #? ("Returns the column that would be occupied by the last " <>+      "laid out character.")  -- | Returns the height of a block or other Doc.-height :: Doc Text -> Lua Int-height = return . Doc.height+height :: HaskellFunction+height = toHsFnPrecursor Doc.height+  <#> docParam "doc"+  =#> intResult "doc height"+  #? "Returns the height of a block or other Doc." + -- | Returns the real length of a string in a monospace font: 0 -- for a combining character, 1, for a regular character, 2 for -- an East Asian wide character.-real_length :: Text -> Lua Int-real_length = return . Doc.realLength+real_length :: HaskellFunction+real_length = toHsFnPrecursor Doc.realLength+  <#> textParam "str"+  =#> intResult "text length"+  #? ("Returns the real length of a string in a monospace font: " <>+      "0 for a combining chaeracter, 1 for a regular character, " <>+      "2 for an East Asian wide character.")  -- -- Constructors@@ -178,115 +291,188 @@  -- | Creates a @'Doc'@ which is conditionally included only if it -- comes at the beginning of a line.-after_break :: Text -> Lua (Doc Text)-after_break = return . Doc.afterBreak+after_break :: HaskellFunction+after_break = toHsFnPrecursor Doc.afterBreak+  <#> textParam "text"+  =#> docResult "new doc"+  #? ("Creates a `Doc` which is conditionally included only if it" <>+      "comes at the beginning of a line.")  -- | Conditionally includes the given @'Doc'@ unless it is -- followed by a blank space.-before_non_blank :: Doc Text -> Lua (Doc Text)-before_non_blank = return . Doc.beforeNonBlank---- | Inserts a blank line unless one exists already.-blankline :: Doc Text-blankline = Doc.blankline+before_non_blank :: HaskellFunction+before_non_blank = toHsFnPrecursor Doc.beforeNonBlank+  <#> docParam "doc"+  =#> docResult "conditional doc"+  #? ("Conditionally includes the given `doc` unless it is " <>+      "followed by a blank space.")  -- | Insert blank lines unless they exist already.-blanklines :: Int -> Lua (Doc Text)-blanklines = return . Doc.blanklines+blanklines :: HaskellFunction+blanklines = toHsFnPrecursor Doc.blanklines+  <#> intParam "n"+  =#> docResult "conditional blank lines"+  #? "Inserts blank lines unless they exist already."  -- | Puts a @'Doc'@ in curly braces.-braces :: Doc Text -> Lua (Doc Text)-braces = return . Doc.braces+braces :: HaskellFunction+braces = toHsFnPrecursor Doc.braces+  <#> docParam "doc"+  =#> docResult "doc enclosed by {}."+  #? "Puts the `doc` in curly braces."  -- | Puts a @'Doc'@ in square brackets.-brackets :: Doc Text -> Lua (Doc Text)-brackets = return . Doc.brackets+brackets :: HaskellFunction -- Doc Text -> Lua (Doc Text)+brackets = toHsFnPrecursor Doc.brackets+  <#> docParam "doc"+  =#> docResult "doc enclosed by []."+  #? "Puts the `doc` in square brackets"  -- | Like @'lblock'@ but aligned centered.-cblock :: Int -> Doc Text -> Lua (Doc Text)-cblock width = return . Doc.cblock width+cblock :: HaskellFunction+cblock = toHsFnPrecursor Doc.cblock+  <#> parameter peekIntegral "integer" "width" "block width in chars"+  <#> docParam "doc"+  =#> docResult ("doc, aligned centered in a block with max" <>+                 "`width` chars per line.")+  #? ("Creates a block with the given width and content, " <>+      "aligned centered.")  -- | Chomps trailing blank space off of a @'Doc'@.-chomp :: Doc Text -> Lua (Doc Text)-chomp = return . Doc.chomp+chomp :: HaskellFunction+chomp = toHsFnPrecursor Doc.chomp+  <#> docParam "doc"+  =#> docResult "`doc` without trailing blanks"+  #? "Chomps trailing blank space off of the `doc`."  -- | Concatenates a list of @'Doc'@s.-concat :: [Doc Text] -> Optional (Doc Text) -> Lua (Doc Text)-concat docs optSep = return $-  case Lua.fromOptional optSep of-    Nothing  -> mconcat docs-    Just sep -> mconcat $ intersperse sep docs---- | A carriage return. Does nothing if we're at the beginning of--- a line; otherwise inserts a newline.-cr :: Doc Text-cr = Doc.cr+concat :: HaskellFunction+concat = toHsFnPrecursor (\docs optSep -> mconcat $+                           maybe docs (`intersperse` docs) optSep)+  <#> parameter (peekList peekDoc) "`{Doc,...}`" "docs" "list of Docs"+  <#> optionalParameter peekDoc "Doc" "sep" "separator"+  =#> docResult "concatenated doc"+  #? "Concatenates a list of `Doc`s."  -- | Wraps a @'Doc'@ in double quotes-double_quotes :: Doc Text -> Lua (Doc Text)-double_quotes = return . Doc.doubleQuotes---- | The empty document.-empty :: Doc Text-empty = Doc.empty+double_quotes :: HaskellFunction+double_quotes = toHsFnPrecursor Doc.doubleQuotes+  <#> docParam "doc"+  =#> docResult "`doc` enclosed by `\"` chars"+  #? "Wraps a `Doc` in double quotes."  -- | Makes a @'Doc'@ flush against the left margin.-flush :: Doc Text -> Lua (Doc Text)-flush = return . Doc.flush+flush :: HaskellFunction+flush = toHsFnPrecursor Doc.flush+  <#> docParam "doc"+  =#> docResult "flushed `doc`"+  #? "Makes a `Doc` flush against the left margin."  -- | Creates a hanging indent.-hang :: Int -> Doc Text -> Doc Text -> Lua (Doc Text)-hang ind start doc = return $ Doc.hang ind start doc+hang :: HaskellFunction+hang = toHsFnPrecursor Doc.hang+  <#> parameter peekIntegral "integer" "ind" "indentation width"+  <#> docParam "start"+  <#> docParam "doc"+  =#> docResult ("`doc` prefixed by `start` on the first line, " <>+                 "subsequent lines indented by `ind` spaces.")+  #? "Creates a hanging indent."  -- | Encloses a @'Doc'@ inside a start and end @'Doc'@.-inside :: Doc Text -> Doc Text -> Doc Text -> Lua (Doc Text)-inside start end contents = return $ Doc.inside start end contents+inside :: HaskellFunction+inside = toHsFnPrecursor Doc.inside+  <#> docParam "start"+  <#> docParam "end"+  <#> docParam "contents"+  =#> docResult "enclosed contents"+  #? "Encloses a `Doc` inside a start and end `Doc`." --- | Creates a block with the given width and content, aligned to the left.-lblock :: Int -> Doc Text -> Lua (Doc Text)-lblock width = return . Doc.lblock width+-- | Creates a block with the given width and content, aligned to+-- the left.+lblock :: HaskellFunction+lblock = toHsFnPrecursor Doc.lblock+  <#> parameter peekIntegral "integer" "width" "block width in chars"+  <#> docParam "doc"+  =#> docResult "doc put into block with max `width` chars per line."+  #? ("Creates a block with the given width and content, " <>+      "aligned to the left.")  -- | Creates a @'Doc'@ from a string.-literal :: Text -> Lua (Doc Text)-literal = return . Doc.literal+literal :: HaskellFunction+literal = toHsFnPrecursor Doc.literal+  <#> textParam "string"+  =#> docResult "doc contatining just the literal string"+  #? "Creates a `Doc` from a string."  -- | Indents a @'Doc'@ by the specified number of spaces.-nest :: Int -> Doc Text -> Lua (Doc Text)-nest ind = return . Doc.nest ind+nest :: HaskellFunction+nest = toHsFnPrecursor Doc.nest+  <#> parameter peekIntegral "integer" "ind" "indentation size"+  <#> docParam "doc"+  =#> docResult "`doc` indented by `ind` spaces"+  #? "Indents a `Doc` by the specified number of spaces."  -- | Removes leading blank lines from a @'Doc'@.-nestle :: Doc Text -> Lua (Doc Text)-nestle = return . Doc.nestle+nestle :: HaskellFunction+nestle = toHsFnPrecursor Doc.nestle+  <#> docParam "doc"+  =#> docResult "`doc` with leading blanks removed"+  #? "Removes leading blank lines from a `Doc`."  -- | Makes a @'Doc'@ non-reflowable.-nowrap :: Doc Text -> Lua (Doc Text)-nowrap = return . Doc.nowrap+nowrap :: HaskellFunction+nowrap = toHsFnPrecursor Doc.nowrap+  <#> docParam "doc"+  =#> docResult "same as input, but non-reflowable"+  #? "Makes a `Doc` non-reflowable."  -- | Puts a @'Doc'@ in parentheses.-parens :: Doc Text -> Lua (Doc Text)-parens = return . Doc.parens+parens :: HaskellFunction+parens = toHsFnPrecursor Doc.parens+  <#> docParam "doc"+  =#> docResult "doc enclosed by ()."+  #? "Puts the `doc` in parentheses." + -- | Uses the specified string as a prefix for every line of the -- inside document (except the first, if not at the beginning of -- the line).-prefixed :: Text -> Doc Text -> Lua (Doc Text)-prefixed prefix = return . Doc.prefixed (T.unpack prefix)+prefixed :: HaskellFunction+prefixed = toHsFnPrecursor Doc.prefixed+  <#> parameter peekString "string" "prefix" "prefix for each line"+  <#> docParam "doc"+  =#> docResult "prefixed `doc`"+  #? ("Uses the specified string as a prefix for every line of " <>+      "the inside document (except the first, if not at the " <>+      "beginning of the line).")  -- | Wraps a @'Doc'@ in single quotes.-quotes :: Doc Text -> Lua (Doc Text)-quotes = return . Doc.quotes---- | Like @'lblock'@ but aligned to the right.-rblock :: Int -> Doc Text -> Lua (Doc Text)-rblock width = return . Doc.rblock width---- | A breaking (reflowable) space.-space :: Doc Text-space = Doc.space+quotes :: HaskellFunction+quotes = toHsFnPrecursor Doc.quotes+  <#> docParam "doc"+  =#> docResult "doc enclosed in `'`."+  #? "Wraps a `Doc` in single quotes." -vfill :: Text -> Lua (Doc Text)-vfill = return . Doc.vfill+-- | Like @'rblock'@ but aligned to the right.+rblock :: HaskellFunction+rblock = toHsFnPrecursor Doc.rblock+  <#> parameter peekIntegral "integer" "width" "block width in chars"+  <#> docParam "doc"+  =#> docResult ("doc, right aligned in a block with max" <>+                 "`width` chars per line.")+  #? ("Creates a block with the given width and content, " <>+      "aligned to the right.") +-- | An expandable border that, when placed next to a box,+-- expands to the height of the box.  Strings cycle through the+-- list provided.+vfill :: HaskellFunction+vfill = toHsFnPrecursor Doc.vfill+  <#> textParam "border"+  =#> docResult "automatically expanding border Doc"+  #? ("An expandable border that, when placed next to a box, " <>+      "expands to the height of the box.  Strings cycle through the " <>+      "list provided.")  -- -- Marshaling@@ -298,8 +484,8 @@  -- | Retrieve a @Doc Text@ value from the Lua stack. Strings are -- converted to plain @'Doc'@ values.-peekDoc :: StackIndex -> Lua (Doc Text)-peekDoc idx = Lua.ltype idx >>= \case+peekDoc' :: StackIndex -> Lua (Doc Text)+peekDoc' idx = Lua.ltype idx >>= \case   Lua.TypeString   -> let stringToDoc s = if T.null s                                           then Doc.empty                                           else Doc.literal s@@ -309,11 +495,14 @@                         (`Lua.toAnyWithName` docTypeName)                         idx +peekDoc :: Peeker (Doc Text)+peekDoc = toPeeker peekDoc'+ instance Peekable (Doc Text) where-  peek = peekDoc+  peek = peekDoc'  -- | Push a @Doc Text@ value to the Lua stack.-pushDoc :: Doc Text -> Lua ()+pushDoc :: Pusher (Doc Text) pushDoc = Lua.pushAnyWithMetatable pushDocMT   where     pushDocMT = Lua.ensureUserdataMetatable docTypeName $ do@@ -350,3 +539,39 @@ -- | Convert to string by rendering without reflowing. __tostring :: Doc Text -> Lua Text __tostring d = return $ Doc.render Nothing d++--+-- Parameters+--++-- | @Doc@ typed function parameter.+docParam :: Text -> Parameter (Doc Text)+docParam name = parameter peekDoc "Doc" name ""++-- | @Int@ typed function parameter.+intParam :: Text -> Parameter Int+intParam name = parameter (peekIntegral @Int) "integer "name ""++-- | @Text@ typed function parameter.+textParam :: Text -> Parameter Text+textParam name = parameter peekText "string" name ""++--+-- Results+--++-- | Boolean function result.+booleanResult :: Text -- ^ Description+              -> FunctionResults Bool+booleanResult = functionResult pushBool "boolean"++-- | Function result of type @'Doc'@.+docResult :: Text -- ^ Description+          -> FunctionResults (Doc Text)+docResult = functionResult pushDoc "Doc"++-- | Function result of type @'Int'@.+intResult :: Text -- ^ Description+          -> FunctionResults Int+intResult = functionResult (pushIntegral @Int) "integer"+
test/test-doclayout.lua view
@@ -24,8 +24,39 @@       assert.are_equal(type(doclayout.space), 'userdata')     end), +    test('chomp', function ()+      local doc = 'a' .. doclayout.blanklines(2)+      assert.are_equal(doclayout.chomp(doc), doclayout.literal 'a')+    end),+    test('nestle', function ()+      local doc = doclayout.blanklines(3) .. 'a'+      assert.are_equal(doclayout.nestle(doc), doclayout.literal 'a')+    end),+    group 'enclosing' {+      test('quotes', function ()+        local doc = doclayout.literal 'single'+        assert.are_equal(+          doclayout.quotes(doc),+          "'" .. doclayout.literal 'single' .. "'"+        )+      end),+      test('double quotes', function ()+        local doc = doclayout.literal 'double'+        assert.are_equal(+          doclayout.double_quotes(doc),+          '"' .. doclayout.literal 'double' .. '"'+        )+      end),+      test('inside', function ()+        local doc = doclayout.literal 'Hello,' + 'World!'+        assert.are_equal(+          doclayout.inside('Yo! ', ' Wassup?', doc),+          'Yo! ' .. doc .. ' Wassup?'+        )+      end),+    },     group 'concat' {-      test('without sep', function ()+      test('with sep', function ()         local list = {           doclayout.literal 'one',           doclayout.literal 'two',@@ -44,13 +75,122 @@         }         assert.are_equal(doclayout.concat(list), list[1] .. list[2] .. list[3])       end),-    }+    },   },    group 'render' {     test('empty doc', function ()       assert.are_equal(doclayout.render(doclayout.empty), '')-    end)+    end),+    test('reflow', function ()+      local greeting = doclayout.literal 'Hi!' .. doclayout.space+        .. doclayout.literal 'How' .. doclayout.space+        .. doclayout.literal 'are' .. doclayout.space+        .. doclayout.literal 'you?'+      assert.are_equal(doclayout.render(greeting, 7), 'Hi! How\nare\nyou?')+    end),+    test('after_break', function ()+      local doc = doclayout.literal 'hi'+        + doclayout.after_break '!'+        .. doclayout.after_break '?'+        .. doclayout.literal 'x'+        .. doclayout.after_break '?'+      assert.are_equal(doclayout.render(doc, 2), 'hi\n!x')+    end),+    test('before_non_blank', function ()+      local doc = doclayout.before_non_blank '!!' .. ' ab'+        / doclayout.before_non_blank '!!' .. 'a b'+      assert.are_equal(doclayout.render(doc), ' ab\n!!a b')+    end),+    test('blanks at beginning', function ()+      local doc = doclayout.blanklines(2) .. 'aa'+      -- only one newline, as the top of doc is treated as implicit blank+      assert.are_equal(doclayout.render(doc), '\naa')+    end),+    test('blanklines', function ()+      local doc = 'aa' .. doclayout.blanklines(2) .. 'bb'+      assert.are_equal(doclayout.render(doc), 'aa\n\n\nbb')+    end),+    test('braces', function ()+      local doc = doclayout.braces 'maybe'+      assert.are_equal(doclayout.render(doc), '{maybe}')+    end),+    test('brackets', function ()+      local doc = doclayout.brackets '1'+      assert.are_equal(doclayout.render(doc), '[1]')+    end),+    test('flush', function ()+      local doc = doclayout.flush(doclayout.nest(2, 'hi'))+      assert.are_equal(doclayout.render(doc), 'hi')+    end),+    test('hang', function ()+      local doc = doclayout.hang(4, '  - ', 'aa\nbb\ncc')+      assert.are_equal(+        doclayout.render(doc),+        table.concat{+          '  - aa\n',+          '    bb\n',+          '    cc',+        }+      )+    end),+    test('nest', function ()+      local doc = doclayout.nest(2, 'aa\n\nbb\ncc')+      assert.are_equal(+        doclayout.render(doc),+        table.concat{+          '  aa\n',+          '\n',+          '  bb\n',+          '  cc'+        }+      )+    end),+    test('nowrap', function()+      local doc = doclayout.nowrap(doclayout.literal 'first' + 'second')+      assert.are_equal(doclayout.render(doc, 8), 'first second')+    end),+    test('parens', function ()+      local doc = doclayout.parens 'lisp'+      assert.are_equal(doclayout.render(doc), '(lisp)')+    end),+    test('prefixed', function ()+      local doc = doclayout.prefixed('# ', doclayout.literal 'aa' // 'bb')+      assert.are_equal(doclayout.render(doc), '# aa\n#\n# bb')+    end),+    group 'table helpers' {+      test('cblock', function ()+        local doc = doclayout.cblock(2, '| ')+          .. doclayout.cblock(4, 'aa')+          .. doclayout.cblock(2, ' |')+        assert.are_equal(doclayout.render(doc), '|  aa  |')+      end),+      test('lblock', function ()+        local doc = doclayout.lblock(2, '| ')+          .. doclayout.lblock(4, 'aa')+          .. doclayout.lblock(2, ' |')+        assert.are_equal(doclayout.render(doc), '| aa   |')+      end),+      test('rblock', function ()+        local doc = doclayout.rblock(2, '| ')+          .. doclayout.rblock(4, 'aa')+          .. doclayout.rblock(2, ' |')+        assert.are_equal(doclayout.render(doc), '|   aa |')+      end),+      test('vfill', function ()+        local doc = doclayout.vfill '| '+          .. doclayout.lblock(4, doclayout.literal 'aa' // 'bbb')+          .. doclayout.vfill(' |')+        assert.are_equal(+          doclayout.render(doc),+          table.concat{+            '| aa   |\n',+            '|      |\n',+            '| bbb  |'+          }+        )+      end)+    }   },    group 'document querying' {@@ -86,6 +226,13 @@       assert.are_equal(doclayout.real_length('❄'), 1)       assert.are_equal(doclayout.real_length('シ'), 2)     end),++    test('update_column', function ()+      local doc = 'long' .. doclayout.cr .. 'longer'+      assert.are_equal(doclayout.update_column(doc, 0), 6)+      assert.are_equal(doclayout.update_column(doclayout.empty, 42), 0)+      assert.are_equal(doclayout.update_column('four', 4), 8)+    end)   },    group 'Doc type' {