packages feed

hslua-module-doclayout (empty) → 0.1.0

raw patch · 7 files changed

+1029/−0 lines, 7 filesdep +basedep +doclayoutdep +hslua

Dependencies added: base, doclayout, hslua, hslua-module-doclayout, tasty, tasty-hunit, tasty-lua, text

Files

+ CHANGELOG.md view
@@ -0,0 +1,11 @@+# Changelog++`hslua-module-doclayout` uses [PVP Versioning][1].+The changelog is available [on GitHub][2].++## 0.1.0++* Initial release.++[1]: https://pvp.haskell.org+[2]: https://github.com/hslua/hslua-module-doclayout/releases
+ LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2020 Albert Krewinkel++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ README.md view
@@ -0,0 +1,373 @@+# hslua-module-doclayout++[![GitHub CI][CI badge]](https://github.com/hslua/hslua-module-doclayout/actions)+[![Build status][Travis badge]](https://travis-ci.org/hslua/hslua-module-doclayout)+[![Hackage][Hackage badge]](https://hackage.haskell.org/package/hslua-module-doclayout)+[![Stackage Lts][Stackage Lts badge]](http://stackage.org/lts/package/hslua-module-doclayout)+[![Stackage Nightly][Stackage Nightly badge]](http://stackage.org/nightly/package/hslua-module-doclayout)+[![MIT license][License badge]](LICENSE)++Lua module wrapping the [doclayout] Haskell package.++[doclayout]: https://hackage.haskell.org/package/doclayout+[CI badge]: https://github.com/hslua/hslua-module-doclayout/workflows/CI/badge.svg+[Travis badge]: https://img.shields.io/travis/hslua/hslua-module-doclayout.svg?logo=travis+[Hackage badge]: https://img.shields.io/hackage/v/hslua-module-doclayout.svg?logo=haskell+[Stackage Lts badge]: http://stackage.org/package/hslua-module-doclayout/badge/lts+[Stackage Nightly badge]: http://stackage.org/package/hslua-module-doclayout/badge/nightly+[License badge]: https://img.shields.io/badge/license-MIT-blue.svg+++Example+-------++``` haskell+loadProg :: Lua Status+loadProg = do+  openlibs+  preloadModule "doclayout"+  dostring $ unlines+    [ "doc = require 'doclayout'"+    , "local example_doc = (doc.literal 'Line' + 'One')"+    , "                 // (doc.literal 'Line' + 'Two')"+    , "-- prints:"+    , "-- Line One"+    , "--"+    , "-- Line Two"+    , "print(doc.render(example_doc))"+    , ""+    , "-- prints:"+    , "-- Line"+    , "-- One"+    , "--"+    , "-- Line"+    , "-- Two"+    , "local columns = 5"+    , "print(doc.render(example_doc, columns))"+    ]+```+++Documentation+-------------++### Functions++#### render++`render (doc[, colwidth])`++Render the `Doc` using the given column width.++Parameters:++`doc`+:   Doc to render++`colwidth`+:   Maximum number of characters per line++### Doc construction++All functions return a fresh `Doc` element.++#### after_break++`after_break`++Creates a `Doc` which is conditionally included only if it+comes at the beginning of a line.++#### before_non_blank++`before_non_blank (doc)`++Conditionally includes the given `Doc` unless it is followed by+a blank space.++#### blankline++`blankline`++Inserts a blank line unless one exists already.++#### blanklines++`blanklines (n)`++Insert blank lines unless they exist already.++Parameters:++`n`+:   Number of blank lines to insert.++#### braces++`braces (doc)`++Puts a `Doc` in curly braces.++#### brackets++`brackets (doc)`++Puts a `Doc` in square brackets.++#### cblock++`cblock (width, doc)`++Like `lblock` but aligned centered.++Parameters:++`width`+:   Width of the created block, in characters++`doc`+:   Contents of the block ([Doc])++#### chomp++`chomp (doc)`++Chomps trailing blank space off of a `Doc`.++#### concat++`concat (docs[, sep])`++Concatenate the given `Doc`s, interspersing `sep` if specified.++Parameters:++`docs`+:   List of `Doc`s++`sep`+:   Separator `Doc`++#### cr++A carriage return. Does nothing if we're at the beginning of a+line; otherwise inserts a newline.++#### double_quotes++`double_quotes (doc)`++Wraps a `Doc` in double quotes++#### empty++The empty document.++#### flush++`flush (doc)`++Makes a `Doc` flush against the left margin.++#### hang++`hang (indent, start, doc)`++Creates a hanging indent.++Parameters:++`indent`+:   Indentation width in characters++`start`+:   Start, printed unindented++`doc`+:   Doc which is indented by `indent` spaces on every line.++#### inside++`inside (start, end, contents)`++Encloses a `Doc` inside a start and end `Doc`.++Parameters:++`start`+:   Doc before contents++`end`+:   Doc after contents++`contents`+:   Contents Doc++#### lblock++`lblock (width, doc)`++Creates a block with the given width and content, aligned to the left.++Parameters:++`width`+:   Width of the created block, in characters++`doc`+:   Contents of the block ([Doc])+++#### literal++`literal (string)`++Creates a `Doc` from a string.++#### nest++`nest (indent)`++Indents a `Doc` by the specified number of spaces.++Parameters:++`indent`+:   Indentation width.++#### nestle++`nestle (doc)`++Removes leading blank lines from a `Doc`.++#### nowrap++`nowrap (doc)`++Makes a `Doc` non-reflowable.++#### parens++`parens (doc)`++Puts a `Doc` in parentheses.++#### prefixed++`prefixed (prefix, 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).++Parameters:++`prefix`+:   Prefix to prepend to each line++`doc`+:   Inside Doc.++#### quotes++`quotes (doc)`++Wraps a `Doc` in single quotes.++#### rblock++`rblock (indent, doc)`++Like `lblock` but aligned to the right.++Parameters:++`width`+:   Width of the created block, in characters++`doc`+:   Contents of the block ([Doc])++#### space++A breaking (reflowable) space.++#### vfill++`vfill`++Creates an expandable border that, when placed next to a box,+expands to the height of the box.++Parameters:++`text`+:   Border text++### Operators++#### `..`++Concatenate two `Doc` elements.++#### `+`++Concatenate two `Doc`s, inserting a reflowable space between them.++#### `/`++If `a` and `b` are `Doc` elements, then `a / b` puts `a` above `b`.++#### `//`++If `a` and `b` are `Doc` elements, then `a // b` puts `a` above+`b`, inserting a blank line between them.++### Document Querying++#### is_empty++`is_empty (doc)`++Returns `true` iff `doc` is the empty document, `false` otherwise.++#### min_offset++`min_offset (doc)`++Returns the minimal width of a @'Doc'@ when reflowed at breakable+spaces.++#### update_column++`update_column (doc, i)`++Returns the column that would be occupied by the last laid out character.++#### height++`height (doc)`++Returns the height of a block or other Doc.++#### real_length++`real_length (str)`++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.++#### offset++`offset (doc)`++Returns the width of a `Doc` (as number of characters).+++License+-------++This package is made available under the MIT license. See [`LICENSE`](LICENSE)+for details.
+ hslua-module-doclayout.cabal view
@@ -0,0 +1,80 @@+cabal-version:       2.0+name:                hslua-module-doclayout+version:             0.1.0+synopsis:            Lua module wrapping Text.DocLayout.+description:         Lua module wrapping @Text.DocLayout@.+homepage:            https://github.com/hslua/hslua-module-doclayout+bug-reports:         https://github.com/hslua/hslua-module-doclayout/issues+license:             MIT+license-file:        LICENSE+author:              Albert Krewinkel+maintainer:          Albert Krewinkel <albert@zeitkraut.de>+copyright:           2020 Albert Krewinkel+category:            Foreign+build-type:          Simple+extra-doc-files:     README.md+                     CHANGELOG.md+extra-source-files:  test/test-doclayout.lua+tested-with:         GHC == 8.0.2+                     GHC == 8.2.2+                     GHC == 8.4.4+                     GHC == 8.6.5+                     GHC == 8.8.3++source-repository head+  type:                git+  location:            https://github.com/hslua/hslua-module-doclayout.git++library+  build-depends:       base           >= 4.9 && < 5+                     , doclayout      >= 0.2 && < 0.4+                     , hslua          >= 1.0 && < 1.2+                     , text           >= 1.0 && < 1.3+  default-language:    Haskell2010+  hs-source-dirs:      src+  exposed-modules:     Foreign.Lua.Module.DocLayout+  ghc-options:         -Wall+                       -Wcompat+                       -Widentities+                       -Wincomplete-uni-patterns+                       -Wincomplete-record-updates+  if impl(ghc >= 8.0)+    ghc-options:       -Wredundant-constraints+  if impl(ghc >= 8.2)+    ghc-options:       -fhide-source-paths+  if impl(ghc >= 8.4)+    ghc-options:       -Wmissing-export-lists+                       -Wpartial-fields+  if impl(ghc >= 8.8)+    ghc-options:       -Wmissing-deriving-strategies++test-suite hslua-module-doclayout-test+  build-depends:       base                    >= 4.9 && < 5+                     , doclayout               >= 0.2 && < 0.4+                     , hslua+                     , hslua-module-doclayout+                     , tasty+                     , tasty-hunit+                     , tasty-lua               >= 0.2 && < 0.3+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             test-hslua-module-doclayout.hs+  build-depends:       hslua-module-doclayout+  default-language:    Haskell2010+  ghc-options:         -threaded+                       -rtsopts+                       -with-rtsopts=-N+                       -Wall+                       -Wcompat+                       -Widentities+                       -Wincomplete-uni-patterns+                       -Wincomplete-record-updates+  if impl(ghc >= 8.0)+    ghc-options:       -Wredundant-constraints+  if impl(ghc >= 8.2)+    ghc-options:       -fhide-source-paths+  if impl(ghc >= 8.4)+    ghc-options:       -Wmissing-export-lists+                       -Wpartial-fields+  if impl(ghc >= 8.8)+    ghc-options:       -Wmissing-deriving-strategies
+ src/Foreign/Lua/Module/DocLayout.hs view
@@ -0,0 +1,352 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-|+Module      : Foreign.Lua.Module.DocLayout+Copyright   : © 2020 Albert Krewinkel+License     : MIT+Maintainer  : Albert Krewinkel <albert+hslua@zeitkraut.de>+Stability   : alpha+Portability : Requires GHC 8 or later.++Provides a Lua module which wraps @'Text.DocLayout'@. The @Doc'+type is specialized to @'Text'@.++This module defines orphan instances for @Doc Text@.+-}+module Foreign.Lua.Module.DocLayout (+  -- * Module+    pushModule+  , preloadModule++  -- * Doc constructors and combinators+  , after_break+  , before_non_blank+  , blankline+  , blanklines+  , braces+  , brackets+  , cblock+  , chomp+  , concat+  , cr+  , double_quotes+  , empty+  , flush+  , hang+  , inside+  , lblock+  , literal+  , nest+  , nestle+  , nowrap+  , parens+  , prefixed+  , quotes+  , rblock+  , space+  , vfill++  -- * Rendering+  , render++  -- * Document Querying+  , is_empty+  , height+  , min_offset+  , offset+  , real_length+  , update_column++  -- * Marshaling+  , peekDoc+  , pushDoc+  )+where++import Prelude hiding (concat)+import Data.List (intersperse)+import Data.Text (Text)+import Foreign.Lua (Lua, NumResults (..), Optional,+                    Peekable, Pushable, StackIndex)+import Text.DocLayout (Doc, (<+>), ($$), ($+$))++import qualified Data.Text as T+import qualified Foreign.Lua as Lua+import qualified Foreign.Lua.Types.Peekable as Lua+import qualified Foreign.Lua.Userdata as Lua+import qualified Text.DocLayout as Doc++#if ! MIN_VERSION_base(4, 11, 0)+import Data.Monoid ((<>))+#endif++--+-- Module+--++-- | 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++-- | Add the @doclayout@ module under the given name to the table+-- of preloaded packages.+preloadModule :: String -> Lua ()+preloadModule = flip Lua.preloadhs pushModule++-- | 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++--+-- Querying+--++-- | @True@ iff the document is empty.+is_empty :: Doc Text -> Lua Bool+is_empty = return . Doc.isEmpty++-- | Returns the width of a @'Doc'@.+offset :: Doc Text -> Lua Int+offset = return . Doc.offset++-- | Returns the minimal width of a @'Doc'@ when reflowed at+-- breakable spaces.+min_offset :: Doc Text -> Lua Int+min_offset = return . Doc.minOffset++-- | 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++-- | Returns the height of a block or other Doc.+height :: Doc Text -> Lua Int+height = return . Doc.height++-- | 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++--+-- Constructors+--++-- | 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++-- | 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++-- | Insert blank lines unless they exist already.+blanklines :: Int -> Lua (Doc Text)+blanklines = return . Doc.blanklines++-- | Puts a @'Doc'@ in curly braces.+braces :: Doc Text -> Lua (Doc Text)+braces = return . Doc.braces++-- | Puts a @'Doc'@ in square brackets.+brackets :: Doc Text -> Lua (Doc Text)+brackets = return . Doc.brackets++-- | Like @'lblock'@ but aligned centered.+cblock :: Int -> Doc Text -> Lua (Doc Text)+cblock width = return . Doc.cblock width++-- | Chomps trailing blank space off of a @'Doc'@.+chomp :: Doc Text -> Lua (Doc Text)+chomp = return . Doc.chomp++-- | 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++-- | 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++-- | Makes a @'Doc'@ flush against the left margin.+flush :: Doc Text -> Lua (Doc Text)+flush = return . Doc.flush++-- | Creates a hanging indent.+hang :: Int -> Doc Text -> Doc Text -> Lua (Doc Text)+hang ind start doc = return $ Doc.hang ind start doc++-- | 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++-- | 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 @'Doc'@ from a string.+literal :: Text -> Lua (Doc Text)+literal = return . Doc.literal++-- | Indents a @'Doc'@ by the specified number of spaces.+nest :: Int -> Doc Text -> Lua (Doc Text)+nest ind = return . Doc.nest ind++-- | Removes leading blank lines from a @'Doc'@.+nestle :: Doc Text -> Lua (Doc Text)+nestle = return . Doc.nestle++-- | Makes a @'Doc'@ non-reflowable.+nowrap :: Doc Text -> Lua (Doc Text)+nowrap = return . Doc.nowrap++-- | Puts a @'Doc'@ in parentheses.+parens :: Doc Text -> Lua (Doc Text)+parens = return . Doc.parens++-- | 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)++-- | 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++vfill :: Text -> Lua (Doc Text)+vfill = return . Doc.vfill+++--+-- Marshaling+--++-- | Name used for the @Doc@ Lua userdata values.+docTypeName :: String+docTypeName = "HsLua DocLayout.Doc"++-- | 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+  Lua.TypeString   -> let stringToDoc s = if T.null s+                                          then Doc.empty+                                          else Doc.literal s+                      in stringToDoc <$> Lua.peek idx+  Lua.TypeNumber   -> Doc.literal <$> Lua.peek idx+  _                -> Lua.reportValueOnFailure docTypeName+                        (`Lua.toAnyWithName` docTypeName)+                        idx++instance Peekable (Doc Text) where+  peek = peekDoc++-- | Push a @Doc Text@ value to the Lua stack.+pushDoc :: Doc Text -> Lua ()+pushDoc = Lua.pushAnyWithMetatable pushDocMT+  where+    pushDocMT = Lua.ensureUserdataMetatable docTypeName $ do+      Lua.addfunction "__add"      __add+      Lua.addfunction "__concat"   __concat+      Lua.addfunction "__div"      __div+      Lua.addfunction "__eq"       __eq+      Lua.addfunction "__idiv"     __idiv+      Lua.addfunction "__tostring" __tostring++instance Pushable (Doc Text) where+  push = pushDoc++-- | Concatenate two @'Doc'@s, putting breakable spaces between them.+__add :: Doc Text -> Doc Text -> Lua (Doc Text)+__add a b = return (a <+> b)++-- | Concatenate two @'Doc'@.+__concat :: Doc Text -> Doc Text -> Lua (Doc Text)+__concat a b = return (a <> b)++-- | @a / b@ puts @a@ above @b@.+__div :: Doc Text -> Doc Text -> Lua (Doc Text)+__div a b = return (a $$ b)++-- | Test @'Doc'@ equality.+__eq :: Doc Text -> Doc Text -> Lua Bool+__eq a b = return (a == b)++-- | @a // b@ puts @a@ above @b@.+__idiv :: Doc Text -> Doc Text -> Lua (Doc Text)+__idiv a b = return (a $+$ b)++-- | Convert to string by rendering without reflowing.+__tostring :: Doc Text -> Lua Text+__tostring d = return $ Doc.render Nothing d
+ test/test-doclayout.lua view
@@ -0,0 +1,139 @@+--+-- Tests for the doclayout module+--+local doclayout = require 'doclayout'+local tasty = require 'tasty'++local group = tasty.test_group+local test = tasty.test_case+local assert = tasty.assert++-- Check existence static fields+return {+  group 'constructors' {+    test('empty', function ()+      assert.are_equal(type(doclayout.empty), 'userdata')+    end),+    test('blankline', function ()+      assert.are_equal(type(doclayout.blankline), 'userdata')+    end),+    test('cr', function ()+      assert.are_equal(type(doclayout.cr), 'userdata')+    end),+    test('space', function ()+      assert.are_equal(type(doclayout.space), 'userdata')+    end),++    group 'concat' {+      test('without sep', function ()+        local list = {+          doclayout.literal 'one',+          doclayout.literal 'two',+          doclayout.literal 'three'+        }+        local sep = doclayout.cr+        assert.are_equal(+          doclayout.concat(list, sep),+          list[1] .. sep .. list[2] .. sep .. list[3])+      end),+      test('without sep', function ()+        local list = {+          doclayout.literal 'one',+          doclayout.literal 'two',+          doclayout.literal 'three'+        }+        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)+  },++  group 'document querying' {+    test('is_empty', function ()+      assert.is_truthy(doclayout.is_empty(doclayout.empty))+      assert.is_truthy(doclayout.is_empty(''))+      assert.is_falsy(doclayout.is_empty('non-empty'))+    end),++    test('height', function ()+      assert.are_equal(doclayout.height(doclayout.empty), 1)+      assert.are_equal(doclayout.height('line'), 1)+      assert.are_equal(doclayout.height(doclayout.literal 'p' / 'q'), 2)+      assert.are_equal(doclayout.height(doclayout.literal 'p' // 'q'), 3)+    end),++    test('min_offset', function ()+      assert.are_equal(doclayout.min_offset 'four', 4)+      assert.are_equal(+        doclayout.min_offset(doclayout.literal 'four' + 'radio'),+        5+      )+    end),++    test('offset', function ()+      assert.are_equal(doclayout.offset 'four', 4)+      assert.are_equal(doclayout.offset(doclayout.literal 'four' / 'radio'), 5)+    end),++    test('real_length', function ()+      assert.are_equal(doclayout.real_length(''), 0)+      assert.are_equal(doclayout.real_length('a'), 1)+      assert.are_equal(doclayout.real_length('❄'), 1)+      assert.are_equal(doclayout.real_length('シ'), 2)+    end),+  },++  group 'Doc type' {+    test('empty strings equal the empty Doc', function ()+      assert.are_equal(doclayout.empty .. '', doclayout.empty)+    end),++    test('strings can be used as Doc values', function ()+      assert.are_equal(doclayout.render('hello world'), 'hello world')+    end),++    test('numbers can be used as Doc values', function ()+      assert.are_equal(doclayout.render(42), '42')+    end),++    test('equality', function ()+      assert.is_truthy(doclayout.literal "true", doclayout.literal "true")+    end),++    test('concatenate docs', function ()+      assert.are_equal(+        tostring(doclayout.literal 'Rock-' .. doclayout.literal 'Ola'),+       'Rock-Ola'+      )+    end),++    test('has tostring method', function ()+      local str = 'just a literal string for now'+      assert.are_equal(tostring(str), str)+    end),++    test('adding concatenates with space', function ()+      local helloworld = doclayout.literal 'Hello,'+        .. doclayout.space+        .. doclayout.literal 'World'+      assert.are_equal(doclayout.literal 'Hello,' + 'World', helloworld)+    end),++    test('dividing sets the first above the second', function ()+           local first = doclayout.literal 'first'+           local second = doclayout.literal 'second'+           assert.are_equal(first / second, first .. doclayout.cr .. second)+    end),++    test('// separates docs with blank line', function ()+      local first = doclayout.literal 'first'+      local second = doclayout.literal 'second'+      assert.are_equal(first // second, first .. doclayout.blankline .. second)+    end),+  }+}
+ test/test-hslua-module-doclayout.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Main+Copyright   : © 2020 Albert Krewinkel+License     : MIT+Maintainer  : Albert Krewinkel <albert+hslua@zeitkraut.de>+Stability   : alpha+Portability : Requires language extensions ForeignFunctionInterface,+              OverloadedStrings.++Tests for the `doclayout` Lua module.+-}+module Main (main) where++import Control.Monad (void)+import Foreign.Lua (Lua)+import Foreign.Lua.Module.DocLayout (preloadModule, pushModule)+import Test.Tasty (TestTree, defaultMain, testGroup)+import Test.Tasty.HUnit (assertEqual, testCase)+import Test.Tasty.Lua (translateResultsFromFile)++import qualified Foreign.Lua as Lua++main :: IO ()+main = do+  luaTestResults <- Lua.run $ do+    Lua.openlibs+    Lua.requirehs "doclayout" (void pushModule)+    translateResultsFromFile "test/test-doclayout.lua"+  defaultMain $ testGroup "hslua-module-doclayout" [tests, luaTestResults]++-- | HSpec tests for the Lua 'system' module+tests :: TestTree+tests = testGroup "HsLua doclayout module"+  [ testCase "module can be pushed to the stack" $+      Lua.run (void pushModule)++  , testCase "module can be added to the preloader" . Lua.run $ do+      Lua.openlibs+      preloadModule "doclayout"+      assertEqual' "function not added to preloader" Lua.TypeFunction =<< do+        Lua.getglobal' "package.preload.doclayout"+        Lua.ltype (-1)++  , testCase "module can be loaded via preloader" . Lua.run $ do+      Lua.openlibs+      preloadModule "doclayout"+      assertEqual' "loading the module fails " Lua.OK =<<+        Lua.dostring "require 'doclayout'"+  ]++assertEqual' :: (Show a, Eq a) => String -> a -> a -> Lua ()+assertEqual' msg expected = Lua.liftIO . assertEqual msg expected