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 +11/−0
- LICENSE +21/−0
- README.md +373/−0
- hslua-module-doclayout.cabal +80/−0
- src/Foreign/Lua/Module/DocLayout.hs +352/−0
- test/test-doclayout.lua +139/−0
- test/test-hslua-module-doclayout.hs +53/−0
+ 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