packages feed

pandoc-lua-engine 0.1.1 → 0.2

raw patch · 22 files changed

+998/−252 lines, 22 filesdep +aesondep +hslua-repldep −hslua-aesondep −hslua-coredep ~hsluadep ~hslua-module-doclayoutdep ~hslua-module-path

Dependencies added: aeson, hslua-repl

Dependencies removed: hslua-aeson, hslua-core

Dependency ranges changed: hslua, hslua-module-doclayout, hslua-module-path, hslua-module-system, hslua-module-text, hslua-module-version, hslua-module-zip, lpeg, pandoc, pandoc-lua-marshal, tasty-lua

Files

pandoc-lua-engine.cabal view
@@ -1,6 +1,6 @@ cabal-version:       2.4 name:                pandoc-lua-engine-version:             0.1.1+version:             0.2 build-type:          Simple license:             GPL-2.0-or-later license-file:        COPYING.md@@ -81,12 +81,14 @@                      , Text.Pandoc.Lua.Marshal.WriterOptions                      , Text.Pandoc.Lua.Module.CLI                      , Text.Pandoc.Lua.Module.Format+                     , Text.Pandoc.Lua.Module.JSON                      , Text.Pandoc.Lua.Module.MediaBag                      , Text.Pandoc.Lua.Module.Pandoc                      , Text.Pandoc.Lua.Module.Scaffolding                      , Text.Pandoc.Lua.Module.Structure                      , Text.Pandoc.Lua.Module.System                      , Text.Pandoc.Lua.Module.Template+                     , Text.Pandoc.Lua.Module.Text                      , Text.Pandoc.Lua.Module.Types                      , Text.Pandoc.Lua.Module.Utils                      , Text.Pandoc.Lua.Orphans@@ -95,6 +97,7 @@                      , Text.Pandoc.Lua.Writer.Scaffolding    build-depends:       SHA                   >= 1.6     && < 1.7+                     , aeson                      , bytestring            >= 0.9     && < 0.12                      , citeproc              >= 0.8     && < 0.9                      , containers            >= 0.6.0.1 && < 0.7@@ -102,19 +105,18 @@                      , doclayout             >= 0.4     && < 0.5                      , doctemplates          >= 0.11    && < 0.12                      , exceptions            >= 0.8     && < 0.11-                     , hslua                 >= 2.2.1   && < 2.3-                     , hslua-aeson           >= 2.2.1   && < 2.3-                     , hslua-core            >= 2.2.1   && < 2.3-                     , hslua-module-doclayout>= 1.0.4   && < 1.1-                     , hslua-module-path     >= 1.0.3   && < 1.1-                     , hslua-module-system   >= 1.0     && < 1.1-                     , hslua-module-text     >= 1.0.3   && < 1.1-                     , hslua-module-version  >= 1.0.3   && < 1.1-                     , hslua-module-zip      >= 1.0.0   && < 1.1-                     , lpeg                  >= 1.0.1   && < 1.1+                     , hslua                 >= 2.3     && < 2.4+                     , hslua-module-doclayout>= 1.1     && < 1.2+                     , hslua-module-path     >= 1.1     && < 1.2+                     , hslua-module-system   >= 1.1     && < 1.2+                     , hslua-module-text     >= 1.1     && < 1.2+                     , hslua-module-version  >= 1.1     && < 1.2+                     , hslua-module-zip      >= 1.1     && < 1.2+                     , hslua-repl            >= 0.1.1   && < 0.2+                     , lpeg                  >= 1.0.4   && < 1.1                      , mtl                   >= 2.2     && < 2.4-                     , pandoc                >= 3.1     && < 3.2-                     , pandoc-lua-marshal    >= 0.2     && < 0.3+                     , pandoc                >= 3.1.2   && < 3.2+                     , pandoc-lua-marshal    >= 0.2.2   && < 0.3                      , pandoc-types          >= 1.22    && < 1.24                      , parsec                >= 3.1     && < 3.2                      , text                  >= 1.1.1   && < 2.1@@ -131,13 +133,13 @@                      , data-default                      , exceptions            >= 0.8     && < 0.11                      , filepath-                     , hslua                 >= 2.1     && < 2.3+                     , hslua                 >= 2.3     && < 2.4                      , pandoc                      , pandoc-types          >= 1.22    && < 1.24                      , tasty                      , tasty-golden                      , tasty-hunit-                     , tasty-lua             >= 1.0     && < 1.1+                     , tasty-lua             >= 1.1     && < 1.2                      , text                  >= 1.1.1   && < 2.1   other-modules:       Tests.Lua                      , Tests.Lua.Module
src/Text/Pandoc/Lua/Custom.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE LambdaCase          #-} {-# LANGUAGE OverloadedStrings   #-} {-# LANGUAGE TypeApplications    #-}-{-# LANGUAGE TupleSections       #-} {- |    Module      : Text.Pandoc.Lua.Custom    Copyright   : © 2021-2023 Albert Krewinkel, John MacFarlane@@ -16,7 +15,6 @@ import Control.Monad.IO.Class (MonadIO) import Data.Maybe (fromMaybe) import HsLua as Lua hiding (Operation (Div))-import HsLua.Core.Run (GCManagedState, newGCManagedState, withGCManagedState) import Text.Pandoc.Class (PandocMonad, findFileWithDataFallback) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Global (Global (..), setGlobals)@@ -40,7 +38,7 @@   either throw pure <=< runLuaWith luaState $ do     let globals = [ PANDOC_SCRIPT_FILE luaFile ]     setGlobals globals-    dofileTrace luaFile' >>= \case+    dofileTrace (Just luaFile') >>= \case       OK -> pure ()       _  -> throwErrorAsException 
src/Text/Pandoc/Lua/Filter.hs view
@@ -34,7 +34,7 @@ runFilterFile :: FilePath -> Pandoc -> LuaE PandocError Pandoc runFilterFile filterPath doc = do   oldtop <- gettop-  stat <- dofileTrace filterPath+  stat <- dofileTrace (Just filterPath)   if stat /= Lua.OK     then throwErrorAsException     else do
src/Text/Pandoc/Lua/Init.hs view
@@ -21,8 +21,8 @@ import Control.Monad.Catch (throwM, try) import Control.Monad.Trans (MonadIO (..)) import Data.Maybe (catMaybes)+import Data.Version (makeVersion) import HsLua as Lua hiding (status, try)-import HsLua.Core.Run as Lua import Text.Pandoc.Class (PandocMonad (..)) import Text.Pandoc.Data (readDataFile) import Text.Pandoc.Error (PandocError (PandocLuaError))@@ -35,16 +35,17 @@ import qualified HsLua.Aeson import qualified HsLua.Module.DocLayout as Module.Layout import qualified HsLua.Module.Path as Module.Path-import qualified HsLua.Module.Text as Module.Text import qualified HsLua.Module.Zip as Module.Zip import qualified Text.Pandoc.Lua.Module.CLI as Pandoc.CLI import qualified Text.Pandoc.Lua.Module.Format as Pandoc.Format+import qualified Text.Pandoc.Lua.Module.JSON as Pandoc.JSON import qualified Text.Pandoc.Lua.Module.MediaBag as Pandoc.MediaBag import qualified Text.Pandoc.Lua.Module.Pandoc as Module.Pandoc import qualified Text.Pandoc.Lua.Module.Scaffolding as Pandoc.Scaffolding import qualified Text.Pandoc.Lua.Module.Structure as Pandoc.Structure import qualified Text.Pandoc.Lua.Module.System as Pandoc.System import qualified Text.Pandoc.Lua.Module.Template as Pandoc.Template+import qualified Text.Pandoc.Lua.Module.Text as Pandoc.Text import qualified Text.Pandoc.Lua.Module.Types as Pandoc.Types import qualified Text.Pandoc.Lua.Module.Utils as Pandoc.Utils @@ -86,18 +87,26 @@ loadedModules =   [ Pandoc.CLI.documentedModule   , Pandoc.Format.documentedModule+  , Pandoc.JSON.documentedModule   , Pandoc.MediaBag.documentedModule   , Pandoc.Scaffolding.documentedModule   , Pandoc.Structure.documentedModule   , Pandoc.System.documentedModule   , Pandoc.Template.documentedModule+  , Pandoc.Text.documentedModule   , Pandoc.Types.documentedModule   , Pandoc.Utils.documentedModule   , Module.Layout.documentedModule { moduleName = "pandoc.layout" }+    `allSince` [2,18]   , Module.Path.documentedModule { moduleName = "pandoc.path" }-  , Module.Text.documentedModule+    `allSince` [2,12]   , Module.Zip.documentedModule { moduleName = "pandoc.zip" }+    `allSince` [3,0]   ]+ where+  allSince mdl version = mdl+    { moduleFunctions = map (`since` makeVersion version) $ moduleFunctions mdl+    }  -- | Initialize the lua state with all required values initLuaState :: PandocLua ()@@ -116,6 +125,13 @@     -- load modules and add them to the `pandoc` module table.     forM_ loadedModules $ \mdl -> do       registerModule mdl+      -- pandoc.text must be require-able as 'text' for backwards compat.+      when (moduleName mdl == "pandoc.text") $ do+        getfield registryindex loaded+        pushvalue (nth  2)+        setfield (nth 2) "text"+        pop 1 -- _LOADED+      -- Shorten name, drop everything before the first dot (if any).       let fieldname (Name mdlname) = Name .             maybe mdlname snd . Char8.uncons . snd $             Char8.break (== '.') mdlname@@ -187,6 +203,7 @@ initJsonMetatable :: PandocLua () initJsonMetatable = liftPandocLua $ do   newListMetatable HsLua.Aeson.jsonarray (pure ())+  Lua.pop 1  -- | Evaluate a @'PandocLua'@ computation, running all contained Lua -- operations.
src/Text/Pandoc/Lua/Marshal/CommonState.hs view
@@ -19,6 +19,7 @@ import Text.Pandoc.Class (CommonState (..)) import Text.Pandoc.Logging (LogMessage, showLogMessage) import Text.Pandoc.Lua.Marshal.List (pushPandocList)+import qualified Data.Aeson as Aeson  -- | Lua type used for the @CommonState@ object. typeCommonState :: LuaError e => DocumentedType e CommonState@@ -64,5 +65,9 @@       ### liftPure showLogMessage       <#> udparam typeLogMessage "msg" "object"       =#> functionResult pushText "string" "stringified log message"+  , operation (CustomOperation "__tojson") $ lambda+      ### liftPure Aeson.encode+      <#> udparam typeLogMessage "msg" "object"+      =#> functionResult pushLazyByteString "string" "JSON encoded object"   ]   mempty -- no members
src/Text/Pandoc/Lua/Module/CLI.hs view
@@ -13,10 +13,9 @@   ) where  import Control.Applicative ((<|>))-import HsLua ( Field (..), Module (..), (###), (<#>), (=#>), (#?)-             , defun, failLua, functionResult, liftIO, parameter, pop-             , pushViaJSON, rawgeti, top)-import HsLua.Marshalling (lastly, liftLua, peekList, peekString)+import Data.Version (makeVersion)+import HsLua+import HsLua.REPL (defaultConfig, replWithEnv, setup) import Text.Pandoc.App (defaultOpts, options, parseOptionsFromArgs) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.PandocLua ()@@ -31,6 +30,7 @@   , moduleFields =       [ Field         { fieldName = "default_options"+        , fieldType = "table"         , fieldDescription = "Default CLI options, using a JSON-like " <>           "representation."         , fieldPushValue = pushViaJSON defaultOpts@@ -48,8 +48,12 @@            , "Typically this function will be used in stand-alone pandoc Lua"            , "scripts, taking the list of arguments from the global `arg`."            ]+        `since` makeVersion [3, 0]++      , repl `since` makeVersion [3, 1, 2]       ]   , moduleOperations = []+  , moduleTypeInitializers = []   }  where   peekArgs idx =@@ -62,3 +66,60 @@     \case       Left e     -> failLua $ "Cannot process info option: " ++ show e       Right opts -> pure opts++-- | Starts a REPL.+repl :: DocumentedFunction PandocError+repl = defun "repl"+  ### (\menvIdx -> do+          let repl' = case menvIdx of+                        Nothing -> replWithEnv Nothing+                        Just envIdx -> do+                          settop envIdx+                          fillWithGlobals envIdx+                          replWithEnv . Just =<< ref registryindex+          setup defaultConfig+          repl')+  <#> opt (parameter (typeChecked "table" istable pure) "table" "env"+           ("Extra environment; the global environment is merged into this" <>+           " table."))+  =?> T.unlines+      [ "The result(s) of the last evaluated input, or nothing if the last"+      , "input resulted in an error."+      ]+  #? T.unlines+  [ "Starts a read-eval-print loop (REPL). The function returns all"+  , "values of the last evaluated input. Exit the REPL by pressing"+  , "`ctrl-d` or `ctrl-c`; press `F1` to get a list of all key"+  , "bindings."+  , ""+  , "The REPL is started in the global namespace, unless the `env`"+  , "parameter is specified. In that case, the global namespace is"+  , "merged into the given table and the result is used as `_ENV` value"+  , "for the repl."+  , ""+  , "Specifically, local variables *cannot* be accessed, unless they"+  , "are explicitly passed via the `env` parameter; e.g."+  , ""+  , "    function Pandoc (doc)"+  , "      -- start repl, allow to access the `doc` parameter"+  , "      -- in the repl"+  , "      return pandoc.cli.repl{ doc = doc }"+  , "    end"+  , ""+  , "**Note**: it seems that the function exits immediately on Windows,"+  , "without prompting for user input."+  ]+ where+  fillWithGlobals idx = do+    -- Copy all global values into the table+    pushglobaltable+    pushnil+    let copyval = next (nth 2) >>= \case+          False -> return ()+          True -> do+            pushvalue (nth 2)+            insert (nth 2)+            rawset idx+            copyval+    copyval+    pop 1  -- global table
src/Text/Pandoc/Lua/Module/Format.hs view
@@ -11,10 +11,11 @@   ( documentedModule   ) where +import Data.Version (makeVersion) import HsLua import Text.Pandoc.Error (PandocError) import Text.Pandoc.Extensions (getAllExtensions, getDefaultExtensions)-import Text.Pandoc.Format (getExtensionsConfig)+import Text.Pandoc.Format (formatFromFilePaths, formatName, getExtensionsConfig) import Text.Pandoc.Lua.Marshal.Format (pushExtensions, pushExtensionsConfig) import Text.Pandoc.Lua.PandocLua () @@ -25,28 +26,18 @@ documentedModule = Module   { moduleName = "pandoc.format"   , moduleDescription = T.unlines-    [ "Pandoc formats and their extensions."+    [ "Information about the formats supported by pandoc."     ]   , moduleFields = []   , moduleOperations = []   , moduleFunctions = functions+  , moduleTypeInitializers = []   }  -- | Extension module functions. functions :: [DocumentedFunction PandocError] functions =-  [ defun "default_extensions"-     ### liftPure getDefaultExtensions-     <#> parameter peekText "string" "format" "format name"-     =#> functionResult pushExtensions "FormatExtensions"-           "default extensions enabled for `format`"-     #? T.unlines-        [ "Returns the list of default extensions of the given format; this"-        , "function does not check if the format is supported, it will return"-        , "a fallback list of extensions even for unknown formats."-        ]--  , defun "all_extensions"+  [ defun "all_extensions"      ### liftPure getAllExtensions      <#> parameter peekText "string" "format" "format name"      =#> functionResult pushExtensions "FormatExtensions"@@ -57,7 +48,20 @@         , "can have an effect when reading a format but not when"         , "writing it, or *vice versa*."         ]+     `since` makeVersion [3,0] +  , defun "default_extensions"+     ### liftPure getDefaultExtensions+     <#> parameter peekText "string" "format" "format name"+     =#> functionResult pushExtensions "FormatExtensions"+           "default extensions enabled for `format`"+     #? T.unlines+        [ "Returns the list of default extensions of the given format; this"+        , "function does not check if the format is supported, it will return"+        , "a fallback list of extensions even for unknown formats."+        ]+     `since` makeVersion [3,0]+   , defun "extensions"      ### liftPure getExtensionsConfig      <#> textParam "format" "format identifier"@@ -72,4 +76,14 @@         , "This function can be used to assign a value to the `Extensions`"         , "global in custom readers and writers."         ]+     `since` makeVersion [3,0]++  , defun "from_path"+      ### liftPure formatFromFilePaths+      <#> parameter (choice [ fmap (:[]) . peekString, peekList peekString])+            "string|{string,...}" "path" "file path, or list of paths"+      =#> functionResult (maybe pushnil (pushText . formatName))+            "string|nil"+            "format determined by heuristic"+      `since` makeVersion [3,1,2]   ]
+ src/Text/Pandoc/Lua/Module/JSON.hs view
@@ -0,0 +1,120 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Text.Pandoc.Lua.Module.JSON+Copyright   : © 2022-2023 Albert Krewinkel+License     : MIT+Maintainer  : Albert Krewinkel <albert@hslua.org>++Lua module to work with JSON.+-}+module Text.Pandoc.Lua.Module.JSON (+  -- * Module+    documentedModule++  -- ** Functions+  , decode+  , encode+  )+where++import Prelude hiding (null)+import Data.Maybe (fromMaybe)+import Data.Monoid (Alt (..))+import Data.Version (makeVersion)+import HsLua.Aeson+import HsLua.Core+import HsLua.Marshalling+import HsLua.Packaging+import Text.Pandoc.Error (PandocError)+import Text.Pandoc.Lua.PandocLua ()+import Text.Pandoc.Lua.Marshal.AST++import qualified Data.Aeson as Aeson+import qualified Data.Text as T++-- | The @aeson@ module specification.+documentedModule :: Module PandocError+documentedModule = Module+  { moduleName = "pandoc.json"+  , moduleDescription = "JSON module to work with JSON; " <>+                        "based on the Aeson Haskell package."+  , moduleFields = fields+  , moduleFunctions = functions+  , moduleOperations = []+  , moduleTypeInitializers = []+  }++--+-- Fields+--++-- | Exported fields.+fields :: LuaError e => [Field e]+fields =+  [ null+  ]++-- | The value used to represent the JSON @null@.+null :: LuaError e => Field e+null = Field+  { fieldName = "null"+  , fieldType = "light userdata"+  , fieldDescription = "Value used to represent the `null` JSON value."+  , fieldPushValue = pushValue Aeson.Null+  }++--+-- Functions+--++functions :: [DocumentedFunction PandocError]+functions =+  [ decode `since` makeVersion [3, 1, 1]+  , encode `since` makeVersion [3, 1, 1]+  ]++-- | Decode a JSON string into a Lua object.+decode :: DocumentedFunction PandocError+decode = defun "decode"+  ### (\str usePandocTypes ->+         fromMaybe pushnil . getAlt . mconcat . map Alt $+         (if usePandocTypes == Just False+          then []+          else [ pushInline  <$> Aeson.decode str+               , pushBlock   <$> Aeson.decode str+               , pushPandoc  <$> Aeson.decode str+               , pushInlines <$> Aeson.decode str+               , pushBlocks  <$> Aeson.decode str+               ])+         ++ [pushValue <$> Aeson.decode str])+  <#> parameter peekLazyByteString "string" "str" "JSON string"+  <#> opt (parameter peekBool "boolean" "pandoc_types"+           "whether to use pandoc types when possible.")+  =#> functionResult pure "any" "decoded object"+  #? T.unlines+     [ "Creates a Lua object from a JSON string. The function returns an"+     , "[[Inline]], [[Block]], [[Pandoc]], [[Inlines]], or [[Blocks]] element"+     , "if the input can be decoded into represent any of those types."+     , "Otherwise the default decoding is applied, using tables, booleans,"+     , "numbers, and [null](#pandoc.json.null) to represent the JSON value."+     , ""+     , "The special handling of AST elements can be disabled by setting"+     , "`pandoc_types` to `false`."+     ]++-- | Encode a Lua object as JSON.+encode :: LuaError e => DocumentedFunction e+encode = defun "encode"+  ### liftPure Aeson.encode+  <#> parameter peekValue "any" "object" "object to convert"+  =#> functionResult pushLazyByteString "string"+        "JSON encoding of the given `object`"+  #? T.unlines+     ["Encodes a Lua object as JSON string."+     , ""+     , "If the object has a metamethod with name `__tojson`, then the"+     , "result is that of a call to that method with `object` passed as"+     , "the sole argument. The result of that call is expected to be a"+     , "valid JSON string, but this not checked."+     ]
src/Text/Pandoc/Lua/Module/MediaBag.hs view
@@ -39,20 +39,31 @@ documentedModule :: Module PandocError documentedModule = Module   { moduleName = "pandoc.mediabag"-  , moduleDescription = "mediabag access"+  , moduleDescription = T.unlines+    [ "The `pandoc.mediabag` module allows accessing pandoc's media"+    , "storage. The \"media bag\" is used when pandoc is called with the"+    , "`--extract-media` or (for HTML only) `--embed-resources` option."+    , ""+    , "The module is loaded as part of module `pandoc` and can either"+    , "be accessed via the `pandoc.mediabag` field, or explicitly"+    , "required, e.g.:"+    , ""+    , "    local mb = require 'pandoc.mediabag'"+    ]   , moduleFields = []   , moduleFunctions =-      [ delete-      , empty-      , fetch-      , fill-      , insert-      , items-      , list-      , lookup-      , write+      [ delete  `since` makeVersion [2,7,3]+      , empty   `since` makeVersion [2,7,3]+      , fetch   `since` makeVersion [2,0]+      , fill    `since` makeVersion [2,19]+      , insert  `since` makeVersion [2,0]+      , items   `since` makeVersion [2,7,3]+      , list    `since` makeVersion [2,0]+      , lookup  `since` makeVersion [2,0]+      , write   `since` makeVersion [3,0]       ]   , moduleOperations = []+  , moduleTypeInitializers = []   }  -- | Delete a single item from the media bag.@@ -60,15 +71,18 @@ delete = defun "delete"   ### (\fp -> unPandocLua $ modifyCommonState               (\st -> st { stMediaBag = MB.deleteMedia fp (stMediaBag st) }))-  <#> stringParam "filepath" "filename of item to delete"+  <#> stringParam "filepath"+      ("Filename of the item to deleted. The media bag will be " <>+       "left unchanged if no entry with the given filename exists.")   =#> []-+  #? "Removes a single entry from the media bag."  -- | Delete all items from the media bag. empty :: DocumentedFunction PandocError empty = defun "empty"   ### unPandocLua (modifyCommonState (\st -> st { stMediaBag = mempty }))   =#> []+  #? "Clear-out the media bag, deleting all items."  -- | Fill the mediabag with all images in the document that aren't -- present yet.@@ -81,7 +95,7 @@   #? ("Fills the mediabag with the images in the given document.\n" <>       "An image that cannot be retrieved will be replaced with a Span\n" <>       "of class \"image\" that contains the image description.\n" <>-      "" <>+      "\n" <>       "Images for which the mediabag already contains an item will\n" <>       "not be processed again.") @@ -92,40 +106,108 @@           mb <- getMediaBag           setMediaBag $ MB.insertMedia fp mmime contents mb           return (Lua.NumResults 0))-  <#> stringParam "filepath" "item file path"-  <#> opt (textParam "mimetype" "the item's MIME type")-  <#> parameter Lua.peekLazyByteString "string" "contents" "binary contents"+  <#> stringParam "filepath" "filename and path relative to the output folder."+  <#> opt (textParam "mimetype"+           "the item's MIME type; omit if unknown or unavailable.")+  <#> parameter Lua.peekLazyByteString "string" "contents"+        "the binary contents of the file."   =#> []+  #? T.unlines+  [ "Adds a new entry to pandoc's media bag. Replaces any existing"+  , "media bag entry the same `filepath`."+  , ""+  , "Usage:"+  , ""+  , "    local fp = 'media/hello.txt'"+  , "    local mt = 'text/plain'"+  , "    local contents = 'Hello, World!'"+  , "    pandoc.mediabag.insert(fp, mt, contents)"+  ]  -- | Returns iterator values to be used with a Lua @for@ loop. items :: DocumentedFunction PandocError items = defun "items"   ### (do-          mb <-unPandocLua getMediaBag+          mb <- unPandocLua getMediaBag           let pushItem (fp, mimetype, contents) = do                 Lua.pushString fp                 Lua.pushText mimetype                 Lua.pushByteString $ BL.toStrict contents                 return (Lua.NumResults 3)           Lua.pushIterator pushItem (MB.mediaItems mb))-  =?> "Iterator triple"+  =?> T.unlines+  [ "Iterator triple:"+  , ""+  , "-   The iterator function; must be called with the iterator"+  , "    state and the current iterator value."+  , "-   Iterator state -- an opaque value to be passed to the"+  , "    iterator function."+  , "-   Initial iterator value."+  ]+  #? T.unlines+  [ "Returns an iterator triple to be used with Lua's generic `for`"+  , "statement. The iterator returns the filepath, MIME type, and"+  , "content of a media bag item on each invocation. Items are"+  , "processed one-by-one to avoid excessive memory use."+  , ""+  , "This function should be used only when full access to all items,"+  , "including their contents, is required. For all other cases,"+  , "[`list`](#pandoc.mediabag.list) should be preferred."+  , ""+  , "Usage:"+  , ""+  , "    for fp, mt, contents in pandoc.mediabag.items() do"+  , "      -- print(fp, mt, contents)"+  , "    end"+  ]  -- | Function to lookup a value in the mediabag. lookup :: DocumentedFunction PandocError lookup = defun "lookup"-  ### (\fp -> unPandocLua (MB.lookupMedia fp <$> getMediaBag) >>= \case-          Nothing   -> 1 <$ Lua.pushnil-          Just item -> 2 <$ do-            Lua.pushText $ MB.mediaMimeType item-            Lua.pushLazyByteString $ MB.mediaContents item)-  <#> stringParam "filepath" "path of item to lookup"-  =?> "MIME type and contents"+  ### (\fp -> unPandocLua (MB.lookupMedia fp <$> getMediaBag))+  <#> stringParam "filepath" "name of the file to look up."+  =#> mconcat+      [ functionResult+          (maybe Lua.pushnil (Lua.pushText . MB.mediaMimeType))+          "string"+          "The entry's MIME type, or nil if the file was not found."+      , functionResult+          (maybe Lua.pushnil (Lua.pushLazyByteString . MB.mediaContents))+          "string"+          "Contents of the file, or nil if the file was not found."+      ]+  #? T.unlines+  [ "Lookup a media item in the media bag, and return its MIME type"+  , "and contents."+  , ""+  , "Usage:"+  , ""+  , "    local filename = 'media/diagram.png'"+  , "    local mt, contents = pandoc.mediabag.lookup(filename)"+  ]  -- | Function listing all mediabag items. list :: DocumentedFunction PandocError list = defun "list"   ### (unPandocLua (MB.mediaDirectory <$> getMediaBag))-  =#> functionResult (pushPandocList pushEntry) "table" "list of entry triples"+  =#> functionResult (pushPandocList pushEntry) "table"+        ("A list of elements summarizing each entry in the media\n" <>+         "bag. The summary item contains the keys `path`, `type`, and\n" <>+         "`length`, giving the filepath, MIME type, and length of\n" <>+         "contents in bytes, respectively.")+  #? T.unlines+  [ "Get a summary of the current media bag contents."+  , ""+  , "Usage:"+  , ""+  , "    -- calculate the size of the media bag."+  , "    local mb_items = pandoc.mediabag.list()"+  , "    local sum = 0"+  , "    for i = 1, #mb_items do"+  , "        sum = sum + mb_items[i].length"+  , "    end"+  , "    print(sum)"+  ]  where   pushEntry :: (FilePath, MimeType, Int) -> LuaE PandocError ()   pushEntry (fp, mimeType, contentLength) = do@@ -137,13 +219,29 @@ -- | Lua function to retrieve a new item. fetch :: DocumentedFunction PandocError fetch = defun "fetch"-  ### (\src -> do-          (bs, mimeType) <- unPandocLua $ fetchItem src-          Lua.pushText $ fromMaybe "" mimeType-          Lua.pushByteString bs-          return 2)-  <#> textParam "src" "URI to fetch"-  =?> "Returns two string values: the fetched contents and the mimetype."+  ### (unPandocLua . fetchItem)+  <#> textParam "source" "path to a resource; either a local file path or URI"+  =#> ( functionResult (Lua.pushText . fromMaybe "" . snd) "string"+        "The entry's MIME type, or `nil` if the file was not found."+        <>+        functionResult (Lua.pushByteString . fst) "string"+        "Contents of the file, or `nil` if the file was not found."+      )+  #? T.unlines+  [ "Fetches the given source from a URL or local file. Returns two"+  , "values: the contents of the file and the MIME type (or an empty"+  , "string)."+  , ""+  , "The function will first try to retrieve `source` from the"+  , "mediabag; if that fails, it will try to download it or read it"+  , "from the local file system while respecting pandoc's \"resource"+  , "path\" setting."+  , ""+  , "Usage:"+  , ""+  , "    local diagram_url = 'https://pandoc.org/diagram.jpg'"+  , "    local mt, contents = pandoc.mediabag.fetch(diagram_url)"+  ]  -- | Extract the mediabag or just a single entry. write :: DocumentedFunction PandocError
src/Text/Pandoc/Lua/Module/Pandoc.hs view
@@ -74,12 +74,18 @@       , inlineConstructors       , metaValueConstructors       ]+  , moduleTypeInitializers =+    [ initType typePandoc+    , initType typeBlock+    , initType typeInline+    ]   }  -- | Set of input formats accepted by @read@. readersField :: Field PandocError readersField = Field   { fieldName = "readers"+  , fieldType = "table"   , fieldDescription = T.unlines     [ "Set of formats that pandoc can parse. All keys in this table can"     , "be used as the `format` value in `pandoc.read`."@@ -92,6 +98,7 @@ writersField :: Field PandocError writersField = Field   { fieldName = "writers"+  , fieldType = "table"   , fieldDescription = T.unlines     [ "Set of formats that pandoc can generate. All keys in this table"     , "can be used as the `format` value in `pandoc.write`."@@ -104,6 +111,7 @@ inlineField :: Field PandocError inlineField = Field   { fieldName = "Inline"+  , fieldType = "table"   , fieldDescription = "Inline constructors, nested under 'constructors'."   -- the nesting happens for historical reasons and should probably be   -- changed.@@ -114,6 +122,7 @@ blockField :: Field PandocError blockField = Field   { fieldName = "Block"+  , fieldType = "table"   , fieldDescription = "Inline constructors, nested under 'constructors'."   -- the nesting happens for historical reasons and should probably be   -- changed.@@ -176,6 +185,7 @@         ]       toField s = Field         { fieldName = T.pack s+        , fieldType = "string"         , fieldDescription = T.pack s         , fieldPushValue = pushString s         }
src/Text/Pandoc/Lua/Module/Scaffolding.hs view
@@ -26,12 +26,14 @@   , moduleFields = [writerScaffolding]   , moduleOperations = []   , moduleFunctions = []+  , moduleTypeInitializers = []   }  -- | Template module functions. writerScaffolding :: Field PandocError writerScaffolding = Field   { fieldName = "Writer"+  , fieldType = "table"   , fieldDescription = T.unlines     [ "An object to be used as a `Writer` function; the construct handles"     , "most of the boilerplate, expecting only render functions for all"
src/Text/Pandoc/Lua/Module/Structure.hs view
@@ -15,11 +15,12 @@ import Control.Applicative ((<|>), optional) import Data.Default (Default (..)) import Data.Maybe (fromMaybe)+import Data.Version (makeVersion) import HsLua ( DocumentedFunction, LuaError, Module (..), Peeker              , (###), (<#>), (=#>), (#?)              , defun, functionResult, getfield, isnil, lastly, liftLua              , opt, liftPure, parameter , peekBool, peekIntegral-             , peekFieldRaw, peekText, pop, pushIntegral, top )+             , peekFieldRaw, peekText, pop, pushIntegral, since, top ) import Text.Pandoc.Chunks ( ChunkedDoc (..), PathTemplate (..)                           , tocToList, splitIntoChunks ) import Text.Pandoc.Definition (Pandoc (..), Block)@@ -41,16 +42,17 @@ documentedModule = Module   { moduleName = "pandoc.structure"   , moduleDescription =-    "Access to the higher-level document structure, including" <>+    "Access to the higher-level document structure, including " <>     "hierarchical sections and the table of contents."   , moduleFields = []   , moduleFunctions =-      [ make_sections-      , slide_level-      , split_into_chunks-      , table_of_contents+      [ make_sections     `since` makeVersion [3,0]+      , slide_level       `since` makeVersion [3,0]+      , split_into_chunks `since` makeVersion [3,0]+      , table_of_contents `since` makeVersion [3,0]       ]   , moduleOperations = []+  , moduleTypeInitializers = []   }  make_sections :: LuaError e => DocumentedFunction e@@ -63,12 +65,13 @@                        Just sl -> prepSlides sl blks                        Nothing -> blks          in pure $ Shared.makeSections numSects baseLevel blks')-  <#> parameter peekBodyBlocks "Blocks" "blocks" "document blocks to process"+  <#> parameter peekBodyBlocks "Blocks|Pandoc" "blocks"+        "document blocks to process"   <#> opt (parameter peekOpts "table" "opts" "options")-  =#> functionResult pushBlocks "list of Blocks"+  =#> functionResult pushBlocks "Blocks"         "processed blocks"   #? T.unlines-     [ "Puts [Blocks] into a hierarchical structure: a list of sections"+     [ "Puts [[Blocks]] into a hierarchical structure: a list of sections"      , "(each a Div with class \"section\" and first element a Header)."      , ""      , "The optional `opts` argument can be a table; two settings are"@@ -79,9 +82,18 @@      , "shifted by the given value. Finally, an integer `slide_level`"      , "value triggers the creation of slides at that heading level."      , ""-     , "Note that a [WriterOptions][] object can be passed as the opts"+     , "Note that a [[WriterOptions]] object can be passed as the opts"      , "table; this will set the `number_section` and `slide_level` values"      , "to those defined on the command line."+     , ""+     , "Usage:"+     , ""+     , "    local blocks = {"+     , "      pandoc.Header(2, pandoc.Str 'first'),"+     , "      pandoc.Header(2, pandoc.Str 'second'),"+     , "    }"+     , "    local opts = PANDOC_WRITER_OPTIONS"+     , "    local newblocks = pandoc.structure.make_sections(blocks, opts)"      ]   where     defNumSec = False@@ -100,7 +112,7 @@ slide_level :: LuaError e => DocumentedFunction e slide_level = defun "slide_level"   ### liftPure getSlideLevel-  <#> parameter peekBodyBlocks "Pandoc|Blocks" "blocks" "document body"+  <#> parameter peekBodyBlocks "Blocks|Pandoc" "blocks" "document body"   =#> functionResult pushIntegral "integer" "slide level"   #? T.unlines   [ "Find level of header that starts slides (defined as the least"@@ -120,7 +132,7 @@   <#> opt (parameter peekSplitOpts "table" "opts" optionsDescr)   =#> functionResult pushChunkedDoc "ChunkedDoc" ""   #? T.unlines-     [ "Converts a `Pandoc` document into a `ChunkedDoc`." ]+     [ "Converts a [[Pandoc]] document into a [[ChunkedDoc]]." ]  where   defPathTmpl = PathTemplate "chunk-%n"   defNumSects = False@@ -135,7 +147,9 @@     True  -> pure defaultValue     False -> p idx'   optionsDescr = T.unlines-    [ "The following options are supported:"+    [ "Splitting options."+    , ""+    , "The following options are supported:"     , ""     , "    `path_template`"     , "    :   template used to generate the chunks' filepaths"
src/Text/Pandoc/Lua/Module/System.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications    #-} {- |    Module      : Text.Pandoc.Lua.Module.System    Copyright   : © 2019-2023 Albert Krewinkel@@ -14,28 +15,35 @@   ( documentedModule   ) where +import Data.Version (makeVersion) import HsLua import HsLua.Module.System-  (arch, env, getwd, ls, mkdir, os, rmdir, with_env, with_tmpdir, with_wd)+  ( arch, cputime, env, getwd, ls, mkdir, os, rmdir+  , with_env, with_tmpdir, with_wd)+import qualified HsLua.Module.System as MSys  -- | Push the pandoc.system module on the Lua stack.-documentedModule :: LuaError e => Module e+documentedModule :: forall e. LuaError e => Module e documentedModule = Module   { moduleName = "pandoc.system"-  , moduleDescription = "system functions"+  , moduleDescription = moduleDescription @e MSys.documentedModule   , moduleFields =       [ arch       , os       ]   , moduleFunctions =-      [ setName "environment" env-      , setName "get_working_directory" getwd-      , setName "list_directory" ls-      , setName "make_directory" mkdir-      , setName "remove_directory" rmdir-      , setName "with_environment" with_env-      , setName "with_temporary_directory" with_tmpdir-      , setName "with_working_directory" with_wd+      [ cputime                                        `since` v[3,1,1]+      , setName "environment" env                      `since` v[2,7,3]+      , setName "get_working_directory" getwd          `since` v[2,8]+      , setName "list_directory" ls                    `since` v[2,19]+      , setName "make_directory" mkdir                 `since` v[2,19]+      , setName "remove_directory" rmdir               `since` v[2,19]+      , setName "with_environment" with_env            `since` v[2,7,3]+      , setName "with_temporary_directory" with_tmpdir `since` v[2,8]+      , setName "with_working_directory" with_wd       `since` v[2,7,3]       ]   , moduleOperations = []+  , moduleTypeInitializers = []   }+ where+  v = makeVersion
src/Text/Pandoc/Lua/Module/Template.hs view
@@ -11,12 +11,13 @@   ( documentedModule   ) where +import Data.Version (makeVersion) import HsLua import HsLua.Module.DocLayout (peekDoc, pushDoc) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.Marshal.AST (peekMeta, pushBlocks, pushInlines) import Text.Pandoc.Lua.Marshal.Context (peekContext, pushContext)-import Text.Pandoc.Lua.Marshal.Template (peekTemplate, pushTemplate)+import Text.Pandoc.Lua.Marshal.Template (typeTemplate, peekTemplate, pushTemplate) import Text.Pandoc.Lua.PandocLua (PandocLua (unPandocLua), liftPandocLua) import Text.Pandoc.Writers.Shared (metaToContext') import Text.Pandoc.Templates@@ -35,6 +36,7 @@   , moduleFields = []   , moduleOperations = []   , moduleFunctions = functions+  , moduleTypeInitializers = [initType typeTemplate]   }  -- | Template module functions.@@ -52,6 +54,7 @@      , "table as values, where the table can be either be a list of the"      , "aforementioned types, or a nested context."      ]+    `since` makeVersion [3,0]    , defun "compile"      ### (\template mfilepath -> unPandocLua $@@ -63,6 +66,7 @@      <#> opt (stringParam "templ_path" "template path")      =#> functionResult (either failLua pushTemplate) "pandoc Template"            "compiled template"+    `since` makeVersion [2,17]    , defun "default"      ### (\mformat -> unPandocLua $ do@@ -75,6 +79,7 @@               "writer for which the template should be returned.")      =#> functionResult pushText "string"            "string representation of the writer's default template"+    `since` makeVersion [2,17]    , defun "meta_to_context"      ### (\meta blockWriterIdx inlineWriterIdx -> unPandocLua $ do@@ -100,4 +105,5 @@      , "data, using the given functions to convert [Blocks] and [Inlines]"      , "to [Doc] values."      ]+    `since` makeVersion [3,0]   ]
+ src/Text/Pandoc/Lua/Module/Text.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Text.Pandoc.Lua.Module.Text+Copyright   : © 2023 Albert Krewinkel+License     : MIT+Maintainer  : Albert Krewinkel <albert@hslua.org>++Lua module to work with UTF-8 strings.+-}+module Text.Pandoc.Lua.Module.Text+  ( documentedModule+  ) where++import Data.Version (makeVersion)+import HsLua+import Text.Pandoc.Error (PandocError)+import Text.Pandoc.Lua.PandocLua ()++import qualified Data.Text as T+import qualified HsLua.Module.Text as TM++-- | The @aeson@ module specification.+documentedModule :: Module PandocError+documentedModule = TM.documentedModule+  { moduleName = "pandoc.text"+  , moduleFunctions =+    [ TM.fromencoding `since` v[3,0]+    , TM.len          `since` v[2,0,3]+    , TM.lower        `since` v[2,0,3]+    , TM.reverse      `since` v[2,0,3]+    , TM.sub          `since` v[2,0,3]+    , TM.toencoding   `since` v[3,0]+    , TM.upper        `since` v[2,0,3]+    ]+  , moduleDescription = T.unlines+    [ "UTF-8 aware text manipulation functions, implemented in Haskell."+    , ""+    , "The text module can also be loaded under the name `text`, although"+    , "this is discouraged and deprecated."+    , ""+    , "``` lua"+    , "-- uppercase all regular text in a document:"+    , "function Str (s)"+    , "  s.text = pandoc.text.upper(s.text)"+    , "  return s"+    , "end"+    , "```"+    ]+  }+ where+  v = makeVersion
src/Text/Pandoc/Lua/Module/Types.hs view
@@ -13,8 +13,9 @@   ( documentedModule   ) where +import Data.Version (makeVersion) import HsLua ( Module (..), (###), (<#>), (=#>)-             , defun, functionResult, parameter)+             , defun, functionResult, parameter, since) import HsLua.Module.Version (peekVersionFuzzy, pushVersion) import Text.Pandoc.Error (PandocError) import Text.Pandoc.Lua.PandocLua ()@@ -37,6 +38,8 @@                        , "or a Version object"                        ])         =#> functionResult pushVersion "Version" "A new Version object."+        `since` makeVersion [2,7,3]       ]   , moduleOperations = []+  , moduleTypeInitializers = []   }
src/Text/Pandoc/Lua/Module/Utils.hs view
@@ -22,7 +22,7 @@ import Data.Data (showConstr, toConstr) import Data.Default (def) import Data.Maybe (fromMaybe)-import Data.Version (Version)+import Data.Version (Version, makeVersion) import HsLua as Lua import HsLua.Module.Version (peekVersionFuzzy, pushVersion) import Text.Pandoc.Citeproc (getReferences, processCitations)@@ -46,58 +46,27 @@ documentedModule :: Module PandocError documentedModule = Module   { moduleName = "pandoc.utils"-  , moduleDescription = "pandoc utility functions"+  , moduleDescription = T.unlines+    [ "This module exposes internal pandoc functions and utility"+    , "functions."+    ]   , moduleFields = []   , moduleOperations = []-  , moduleFunctions =-    [ defun "blocks_to_inlines"-      ### (\blks mSep -> do-              let sep = maybe Shared.defaultBlocksSeparator B.fromList mSep-              return $ B.toList (Shared.blocksToInlinesWithSep sep blks))-      <#> parameter (peekList peekBlock) "list of blocks"-            "blocks" ""-      <#> opt (parameter (peekList peekInline) "Inlines" "sep" "")-      =#> functionResult pushInlines "list of inlines" ""--    , defun "citeproc"-      ### unPandocLua . processCitations-      <#> parameter peekPandoc "Pandoc" "doc" "document"-      =#> functionResult pushPandoc "Pandoc" "processed document"-      #?  T.unwords-          [ "Process the citations in the file, replacing them with "-          , "rendered citations and adding a bibliography. "-          , "See the manual section on citation rendering for details."-          ]--    , defun "equals"-      ### equal-      <#> parameter pure "AST element" "elem1" ""-      <#> parameter pure "AST element" "elem2" ""-      =#> functionResult pushBool "boolean" "true iff elem1 == elem2"--    , defun "make_sections"-      ### liftPure3 Shared.makeSections-      <#> parameter peekBool "boolean" "numbering" "add header numbers"-      <#> parameter (\i -> (Nothing <$ peekNil i) <|> (Just <$!> peekIntegral i))-                    "integer or nil" "baselevel" ""-      <#> parameter (peekList peekBlock) "list of blocks"-            "blocks" "document blocks to process"-      =#> functionResult pushBlocks "list of Blocks"-            "processes blocks"--    , defun "normalize_date"-      ### liftPure Shared.normalizeDate-      <#> parameter peekText "string" "date" "the date string"-      =#> functionResult (maybe pushnil pushText) "string or nil"-            "normalized date, or nil if normalization failed."-      #? T.unwords-      [ "Parse a date and convert (if possible) to \"YYYY-MM-DD\" format. We"-      , "limit years to the range 1601-9999 (ISO 8601 accepts greater than"-      , "or equal to 1583, but MS Word only accepts dates starting 1601)."-      , "Returns nil instead of a string if the conversion failed."-      ]--    , sha1+  , moduleTypeInitializers = []+  , moduleFunctions = -- FIXME: order alphabetically+    [ blocks_to_inlines `since` v[2,2,3]+    , citeproc          `since` v[2,19,1]+    , equals            `since` v[2,5]+    , from_simple_table `since` v[2,11]+    , make_sections     `since` v[2,8]+    , references        `since` v[2,17]+    , run_json_filter   `since` v[2,1,1]+    , normalize_date    `since` v[2,0,6]+    , sha1              `since` v[2,0,6]+    , stringify         `since` v[2,0,6]+    , to_roman_numeral  `since` v[2,0,6]+    , to_simple_table   `since` v[2,11]+    , type'             `since` v[2,17]      , defun "Version"       ### liftPure (id @Version)@@ -106,92 +75,225 @@             "v" "version description"       =#> functionResult pushVersion "Version" "new Version object"       #? "Creates a Version object."+    ]+  }+ where+  v = makeVersion -    , defun "references"-      ### (unPandocLua . getReferences Nothing)-      <#> parameter peekPandoc "Pandoc" "doc" "document"-      =#> functionResult (pushPandocList pushReference) "table"-            "lift of references"-      #? mconcat-         [ "Get references defined inline in the metadata and via an external "-         , "bibliography.  Only references that are actually cited in the "-         , "document (either with a genuine citation or with `nocite`) are "-         , "returned. URL variables are converted to links."-         ]+blocks_to_inlines :: LuaError e => DocumentedFunction e+blocks_to_inlines = defun "blocks_to_inlines"+  ### (\blks mSep -> do+          let sep = maybe Shared.defaultBlocksSeparator B.fromList mSep+          return $ B.toList (Shared.blocksToInlinesWithSep sep blks))+  <#> parameter (peekList peekBlock) "Blocks"+        "blocks"+        "List of [[Block]] elements to be flattened."+  <#> opt (parameter (peekList peekInline) "Inlines" "sep"+           ("List of [[Inline]] elements inserted as separator between\n" <>+            "two consecutive blocks; defaults to `{pandoc.LineBreak()}`."))+  =#> functionResult pushInlines "Inlines" ""+  #? T.unlines+  [ "Squash a list of blocks into a list of inlines."+  , ""+  , "Usage"+  , ""+  , "    local blocks = {"+  , "      pandoc.Para{ pandoc.Str 'Paragraph1' },"+  , "      pandoc.Para{ pandoc.Emph 'Paragraph2' }"+  , "    }"+  , "    local inlines = pandoc.utils.blocks_to_inlines(blocks)"+  , "    assert("+  , "      inlines == pandoc.Inlines {"+  , "        pandoc.Str 'Paragraph1',"+  , "        pandoc.Linebreak(),"+  , "        pandoc.Emph{ pandoc.Str 'Paragraph2' }"+  , "      }"+  , "    )"+  ] -    , defun "run_json_filter"-      ### (\doc filterPath margs -> do-              args <- case margs of-                        Just xs -> return xs-                        Nothing -> do-                          Lua.getglobal "FORMAT"-                          (forcePeek ((:[]) <$!> peekString top) <* pop 1)-              applyJSONFilter def args filterPath doc-          )-      <#> parameter peekPandoc "Pandoc" "doc" "input document"-      <#> parameter peekString "filepath" "filter_path" "path to filter"-      <#> opt (parameter (peekList peekString) "list of strings"-               "args" "arguments to pass to the filter")-      =#> functionResult pushPandoc "Pandoc" "filtered document"+citeproc :: DocumentedFunction PandocError+citeproc = defun "citeproc"+  ### unPandocLua . processCitations+  <#> parameter peekPandoc "Pandoc" "doc" "document"+  =#> functionResult pushPandoc "Pandoc" "processed document"+  #?  T.unlines+      [ "Process the citations in the file, replacing them with "+      , "rendered citations and adding a bibliography. "+      , "See the manual section on citation rendering for details."+      , ""+      , "Usage:"+      , ""+      , "    -- Lua filter that behaves like `--citeproc`"+      , "    function Pandoc (doc)"+      , "      return pandoc.utils.citeproc(doc)"+      , "    end"+      ] -    , defun "stringify"-      ### stringify-      <#> parameter pure "AST element" "elem" "some pandoc AST element"-      =#> functionResult pushText "string" "stringified element"+equals :: LuaError e => DocumentedFunction e+equals = defun "equals"+  ### equal+  <#> parameter pure "any" "element1" ""+  <#> parameter pure "any" "element2" ""+  =#> functionResult pushBool "boolean"+        "Whether the two objects represent the same element"+  #? T.unlines+  [ "Test equality of AST elements. Elements in Lua are considered"+  , "equal if and only if the objects obtained by unmarshaling are"+  , "equal."+  , ""+  , "**This function is deprecated.** Use the normal Lua `==` equality"+  , "operator instead."+  ] -    , defun "from_simple_table"-      ### from_simple_table-      <#> parameter peekSimpleTable "SimpleTable" "simple_tbl" ""-      =?> "Simple table"+-- | Converts an old/simple table into a normal table block element.+from_simple_table :: LuaError e => DocumentedFunction e+from_simple_table = defun "from_simple_table"+  ### liftPure+      (\(SimpleTable capt aligns widths head' body) ->+          Table+          nullAttr+          (Caption Nothing [Plain capt | not (null capt)])+          (zipWith (\a w -> (a, toColWidth w)) aligns widths)+          (TableHead nullAttr [blockListToRow head' | not (null head') ])+          [TableBody nullAttr 0 [] $ map blockListToRow body | not (null body)]+          (TableFoot nullAttr []))+  <#> parameter peekSimpleTable "SimpleTable" "simple_tbl" ""+  =#> functionResult pushBlock "Block" "table block element"+  #? T.unlines+  [ "Creates a [[Table]] block element from a [[SimpleTable]]. This is"+  , "useful for dealing with legacy code which was written for pandoc"+  , "versions older than 2.10."+  , ""+  , "Usage:"+  , ""+  , "    local simple = pandoc.SimpleTable(table)"+  , "    -- modify, using pre pandoc 2.10 methods"+  , "    simple.caption = pandoc.SmallCaps(simple.caption)"+  , "    -- create normal table block again"+  , "    table = pandoc.utils.from_simple_table(simple)"+  ]+ where+  blockListToRow :: [[Block]] -> Row+  blockListToRow = Row nullAttr . map (B.simpleCell . B.fromList) -    , defun "to_roman_numeral"-      ### liftPure Shared.toRomanNumeral-      <#> parameter (peekIntegral @Int) "integer" "n" "number smaller than 4000"-      =#> functionResult pushText "string" "roman numeral"-      #? "Converts a number < 4000 to uppercase roman numeral."+  toColWidth :: Double -> ColWidth+  toColWidth 0 = ColWidthDefault+  toColWidth w = ColWidth w -    , defun "to_simple_table"-      ### to_simple_table-      <#> parameter peekTable "Block" "tbl" "a table"-      =#> functionResult pushSimpleTable "SimpleTable" "SimpleTable object"-      #? "Converts a table into an old/simple table."+make_sections :: LuaError e => DocumentedFunction e+make_sections = defun "make_sections"+  ### liftPure3 Shared.makeSections+  <#> parameter peekBool "boolean" "number_sections"+      ("whether section divs should get an additional `number`\n" <>+       "attribute containing the section number.")+  <#> parameter (\i -> (Nothing <$ peekNil i) <|> (Just <$!> peekIntegral i))+        "integer|nil" "baselevel"+        "shift top-level headings to this level"+  <#> parameter (peekList peekBlock) "Blocks"+        "blocks" "list of blocks to process"+  =#> functionResult pushBlocks "Blocks"+        "blocks with sections"+  #? T.unlines+  [ "Converts a list of [[Block]] elements into sections."+  , "`Div`s will be created beginning at each `Header`"+  , "and containing following content until the next `Header`"+  , "of comparable level.  If `number_sections` is true,"+  , "a `number` attribute will be added to each `Header`"+  , "containing the section number. If `base_level` is"+  , "non-null, `Header` levels will be reorganized so"+  , "that there are no gaps, and so that the base level"+  , "is the level specified."+  ] -    , defun "type"-      ### (\idx -> getmetafield idx "__name" >>= \case-              TypeString -> fromMaybe mempty <$> tostring top-              _ -> ltype idx >>= typename)-      <#> parameter pure "any" "object" ""-      =#> functionResult pushByteString "string" "type of the given value"-    #? ("Pandoc-friendly version of Lua's default `type` function, " <>-        "returning the type of a value. If the argument has a " <>-        "string-valued metafield `__name`, then it gives that string. " <>-        "Otherwise it behaves just like the normal `type` function.")-    ]-  }+normalize_date :: DocumentedFunction e+normalize_date = defun "normalize_date"+  ### liftPure Shared.normalizeDate+  <#> parameter peekText "string" "date" "the date string"+  =#> functionResult (maybe pushnil pushText) "string or nil"+        "normalized date, or nil if normalization failed."+  #? T.unwords+  [ "Parse a date and convert (if possible) to \"YYYY-MM-DD\" format. We"+  , "limit years to the range 1601-9999 (ISO 8601 accepts greater than"+  , "or equal to 1583, but MS Word only accepts dates starting 1601)."+  , "Returns nil instead of a string if the conversion failed."+  ] +-- | List of references in CSL format.+references :: DocumentedFunction PandocError+references = defun "references"+  ### (unPandocLua . getReferences Nothing)+  <#> parameter peekPandoc "Pandoc" "doc" "document"+  =#> functionResult (pushPandocList pushReference) "table"+       "lift of references."+  #? T.unlines+  [ "Get references defined inline in the metadata and via an external"+  , "bibliography. Only references that are actually cited in the"+  , "document (either with a genuine citation or with `nocite`) are"+  , "returned. URL variables are converted to links."+  , ""+  , "The structure used represent reference values corresponds to that"+  , "used in CSL JSON; the return value can be use as `references`"+  , "metadata, which is one of the values used by pandoc and citeproc"+  , "when generating bibliographies."+  , ""+  , "Usage:"+  , ""+  , "    -- Include all cited references in document"+  , "    function Pandoc (doc)"+  , "      doc.meta.references = pandoc.utils.references(doc)"+  , "      doc.meta.bibliography = nil"+  , "      return doc"+  , "    end"+  ]++run_json_filter :: DocumentedFunction PandocError+run_json_filter = defun "run_json_filter"+  ### (\doc filterPath margs -> do+          args <- case margs of+                    Just xs -> return xs+                    Nothing -> do+                      Lua.getglobal "FORMAT"+                      (forcePeek ((:[]) <$!> peekString top) <* pop 1)+          applyJSONFilter def args filterPath doc+      )+  <#> parameter peekPandoc "Pandoc" "doc" "the Pandoc document to filter"+  <#> parameter peekString "string" "filter" "filter to run"+  <#> opt (parameter (peekList peekString) "{string,...}" "args"+           "list of arguments passed to the filter. Defaults to `{FORMAT}`.")+  =#> functionResult pushPandoc "Pandoc" "filtered document"+  #? "Filter the given doc by passing it through a JSON filter."+ -- | Documented Lua function to compute the hash of a string. sha1 :: DocumentedFunction e sha1 = defun "sha1"   ### liftPure (SHA.showDigest . SHA.sha1)   <#> parameter (fmap BSL.fromStrict . peekByteString) "string" "input" ""   =#> functionResult pushString "string" "hexadecimal hash value"-  #? "Compute the hash of the given string value."-+  #? "Computes the SHA1 hash of the given string input."  -- | Convert pandoc structure to a string with formatting removed. -- Footnotes are skipped (since we don't want their contents in link -- labels).-stringify :: LuaError e => StackIndex -> LuaE e T.Text-stringify idx = forcePeek . retrieving "stringifyable element" $-  choice-  [ (fmap Shared.stringify . peekPandoc)-  , (fmap Shared.stringify . peekInline)-  , (fmap Shared.stringify . peekBlock)-  , (fmap Shared.stringify . peekCitation)-  , (fmap stringifyMetaValue . peekMetaValue)-  , (fmap (const "") . peekAttr)-  , (fmap (const "") . peekListAttributes)-  ] idx+stringify :: LuaError e => DocumentedFunction e+stringify = defun "stringify"+  ### (\idx ->+         forcePeek . retrieving "stringifyable element" $+         choice+         [ (fmap Shared.stringify . peekPandoc)+         , (fmap Shared.stringify . peekInline)+         , (fmap Shared.stringify . peekBlock)+         , (fmap Shared.stringify . peekCitation)+         , (fmap stringifyMetaValue . peekMetaValue)+         , (fmap (const "") . peekAttr)+         , (fmap (const "") . peekListAttributes)+         ] idx)+  <#> parameter pure "AST element" "element" "some pandoc AST element"+  =#> functionResult pushText "string"+        "A plain string representation of the given element."+  #? T.unlines+  [ "Converts the given element (Pandoc, Meta, Block, or Inline) into"+  , "a string with all formatting removed."+  ]  where   stringifyMetaValue :: MetaValue -> T.Text   stringifyMetaValue mv = case mv of@@ -201,39 +303,77 @@     MetaMap m    -> mconcat $ map (stringifyMetaValue . snd) (Map.toList m)     _            -> Shared.stringify mv --- | Converts an old/simple table into a normal table block element.-from_simple_table :: SimpleTable -> LuaE PandocError NumResults-from_simple_table (SimpleTable capt aligns widths head' body) = do-  pushBlock $ Table-    nullAttr-    (Caption Nothing [Plain capt | not (null capt)])-    (zipWith (\a w -> (a, toColWidth w)) aligns widths)-    (TableHead nullAttr [blockListToRow head' | not (null head') ])-    [TableBody nullAttr 0 [] $ map blockListToRow body | not (null body)]-    (TableFoot nullAttr [])-  return (NumResults 1)-  where-    blockListToRow :: [[Block]] -> Row-    blockListToRow = Row nullAttr . map (B.simpleCell . B.fromList) -    toColWidth :: Double -> ColWidth-    toColWidth 0 = ColWidthDefault-    toColWidth w = ColWidth w+to_roman_numeral :: LuaError e => DocumentedFunction e+to_roman_numeral = defun "to_roman_numeral"+  ### liftPure Shared.toRomanNumeral+  <#> parameter (peekIntegral @Int) "integer" "n"+        "positive integer below 4000"+  =#> functionResult pushText "string" "A roman numeral."+  #? T.unlines+  [ "Converts an integer < 4000 to uppercase roman numeral."+  , ""+  , "Usage:"+  , ""+  , "    local to_roman_numeral = pandoc.utils.to_roman_numeral"+  , "    local pandoc_birth_year = to_roman_numeral(2006)"+  , "    -- pandoc_birth_year == 'MMVI'"+  ]  -- | Converts a table into an old/simple table.-to_simple_table :: Block -> LuaE PandocError SimpleTable-to_simple_table = \case-  Table _attr caption specs thead tbodies tfoot -> do-    let (capt, aligns, widths, headers, rows) =-          Shared.toLegacyTable caption specs thead tbodies tfoot-    return $ SimpleTable capt aligns widths headers rows-  blk -> Lua.failLua $ mconcat-         [ "Expected Table, got ", showConstr (toConstr blk), "." ]+to_simple_table :: DocumentedFunction PandocError+to_simple_table = defun "to_simple_table"+  ### (\case+          Table _attr caption specs thead tbodies tfoot -> do+            let (capt, aligns, widths, headers, rows) =+                  Shared.toLegacyTable caption specs thead tbodies tfoot+            return $ SimpleTable capt aligns widths headers rows+          blk -> Lua.failLua $ mconcat+                 [ "Expected Table, got ", showConstr (toConstr blk), "." ])+  <#> parameter peekTable "Block" "tbl" "a table"+  =#> functionResult pushSimpleTable "SimpleTable" "SimpleTable object"+  #? T.unlines+  [ "Converts a table into an old/simple table."+  , ""+  , "Usage:"+  , ""+  , "    local simple = pandoc.utils.to_simple_table(table)"+  , "    -- modify, using pre pandoc 2.10 methods"+  , "    simple.caption = pandoc.SmallCaps(simple.caption)"+  , "    -- create normal table block again"+  , "    table = pandoc.utils.from_simple_table(simple)"+  ]+ where+  peekTable :: LuaError e => Peeker e Block+  peekTable idx = peekBlock idx >>= \case+    t@(Table {}) -> return t+    b -> Lua.failPeek $ mconcat+         [ "Expected Table, got "+         , UTF8.fromString $ showConstr (toConstr b)+         , "." ] -peekTable :: LuaError e => Peeker e Block-peekTable idx = peekBlock idx >>= \case-  t@(Table {}) -> return t-  b -> Lua.failPeek $ mconcat-       [ "Expected Table, got "-       , UTF8.fromString $ showConstr (toConstr b)-       , "." ]+type' :: DocumentedFunction e+type' = defun "type"+  ### (\idx -> getmetafield idx "__name" >>= \case+          TypeString -> fromMaybe mempty <$> tostring top+          _ -> ltype idx >>= typename)+  <#> parameter pure "any" "value" "any Lua value"+  =#> functionResult pushByteString "string" "type of the given value"+  #? T.unlines+  [ "Pandoc-friendly version of Lua's default `type` function, returning"+  , "type information similar to what is presented in the manual."+  , ""+  , "The function works by checking the metafield `__name`. If the"+  , "argument has a string-valued metafield `__name`, then it returns"+  , "that string. Otherwise it behaves just like the normal `type`"+  , "function."+  , ""+  , "Usage:"+  , "    -- Prints one of 'string', 'boolean', 'Inlines', 'Blocks',"+  , "    -- 'table', and 'nil', corresponding to the Haskell constructors"+  , "    -- MetaString, MetaBool, MetaInlines, MetaBlocks, MetaMap,"+  , "    -- and an unset value, respectively."+  , "    function Meta (meta)"+  , "      print('type of metavalue `author`:', pandoc.utils.type(meta.author))"+  , "    end"+  ]
src/Text/Pandoc/Lua/PandocLua.hs view
@@ -78,7 +78,7 @@    getCommonState = PandocLua $ do     Lua.getglobal "PANDOC_STATE"-    forcePeek $ peekCommonState Lua.top+    forcePeek $ peekCommonState Lua.top `lastly` pop 1   putCommonState cst = PandocLua $ do     pushCommonState cst     Lua.setglobal "PANDOC_STATE"
test/Tests/Lua/Module.hs view
@@ -25,6 +25,8 @@                   ("lua" </> "module" </> "pandoc-list.lua")   , testPandocLua "pandoc.format"                   ("lua" </> "module" </> "pandoc-format.lua")+  , testPandocLua "pandoc.json"+                  ("lua" </> "module" </> "pandoc-json.lua")   , testPandocLua "pandoc.mediabag"                   ("lua" </> "module" </> "pandoc-mediabag.lua")   , testPandocLua "pandoc.path"@@ -33,6 +35,8 @@                   ("lua" </> "module" </> "pandoc-structure.lua")   , testPandocLua "pandoc.template"                   ("lua" </> "module" </> "pandoc-template.lua")+  , testPandocLua "pandoc.text"+                  ("lua" </> "module" </> "pandoc-text.lua")   , testPandocLua "pandoc.types"                   ("lua" </> "module" </> "pandoc-types.lua")   , testPandocLua "pandoc.utils"
+ test/lua/module/pandoc-json.lua view
@@ -0,0 +1,107 @@+--+-- Tests for the system module+--+local json = require 'pandoc.json'+local tasty = require 'tasty'++local group = tasty.test_group+local test = tasty.test_case+local assert = tasty.assert++return {+  -- Check existence of static fields+  group 'static fields' {+    test('null', function ()+      assert.are_equal(type(json.null), 'userdata')+    end),+  },++  group 'encode' {+    test('string', function ()+      assert.are_equal(json.encode 'one\ntwo', '"one\\ntwo"')+    end),+    test('null', function ()+      assert.are_equal(json.encode(json.null), 'null')+    end),+    test('number', function ()+      assert.are_equal(json.encode(42), '42')+    end),+    test('object', function ()+      assert.are_same(json.encode{a = 5}, '{"a":5}')+    end),+    test('object with metamethod', function ()+      local obj = setmetatable(+        {title = 23},+        {+          __tojson = function (obj)+            return '"Nichts ist so wie es scheint"'+          end+        }+      )+      assert.are_same(json.encode(obj), [["Nichts ist so wie es scheint"]])+    end),+    test('Inline (Space)', function ()+      assert.are_equal(+        json.encode(pandoc.Space()),+        '{"t":"Space"}'+      )+    end),+    test('Block (HorizontalRule)', function ()+      assert.are_equal(+        json.encode(pandoc.HorizontalRule()),+        '{"t":"HorizontalRule"}'+      )+    end),+    test('Inlines list', function ()+      assert.are_equal(+        json.encode(pandoc.Inlines{pandoc.Space()}),+        '[{"t":"Space"}]'+      )+    end),+    test('Pandoc', function ()+      assert.are_equal(+        type(json.encode(pandoc.Pandoc{'Hello from Lua!'})),+        'string'+      )+    end),+    test('Nested Inline', function ()+      assert.are_equal(+        json.encode({spc = pandoc.Space()}),+        '{"spc":{"t":"Space"}}'+      )+    end)+  },++  group 'decode' {+    test('string', function ()+      assert.are_equal(json.decode '"one\\ntwo"', 'one\ntwo')+    end),+    test('null', function ()+      assert.are_equal(json.decode 'null', json.null)+    end),+    test('number', function ()+      assert.are_equal(json.decode '42', 42)+    end),+    test('object', function ()+      assert.are_same(json.decode '{"a":5}', {a = 5})+    end),+    test('Inline (Space)', function ()+      assert.are_equal(json.decode '{"t":"Space"}', pandoc.Space())+    end),+    test('Inline (Str)', function ()+      assert.are_equal(json.decode '{"t":"Str", "c":"a"}', pandoc.Str 'a')+    end),+    test('disabled AST check', function ()+      assert.are_same(+        json.decode('{"t":"Str", "c":"a"}', false),+        {t = 'Str', c = 'a'}+      )+    end),+    test('Inlines list', function ()+      assert.are_equal(+        json.decode '[{"t":"Space"}]',+        pandoc.Inlines{pandoc.Space()}+      )+    end)+  },+}
test/lua/module/pandoc-mediabag.lua view
@@ -49,6 +49,30 @@     end),   }, +  group 'fetch' {+    test('populates media bag', function ()+      local filename = 'lua/module/sample.svg'+      local mime, contents = mediabag.fetch(filename)+      assert.are_equal(mime, 'image/svg+xml')+      assert.are_equal(contents:sub(1,5), '<?xml')+      mediabag.empty() -- clean up+    end),+  },++  group 'fill' {+    test('populates media bag', function ()+      local filename = 'lua/module/sample.svg'+      local doc = pandoc.Pandoc {+        pandoc.Image('testing', filename)+      }+      mediabag.fill(doc)+      local mime, contents = mediabag.lookup(filename)+      assert.are_equal(mime, 'image/svg+xml')+      assert.are_equal(contents:sub(1,5), '<?xml')+      mediabag.empty() -- clean up+    end),+  },+   group 'items' {     test('iterates over all items', function ()       local input_items = {@@ -68,5 +92,16 @@       assert.are_same(seen_items, input_items)       mediabag.empty() -- clean up     end)-  }+  },++  group 'lookup' {+    test('returns MIME type and contents', function ()+      mediabag.insert('test.html', 'text/html', '<aside>Who cares?</aside>')+      local mime, contents = mediabag.lookup('test.html')+      assert.are_equal(mime, 'text/html')+      assert.are_equal(contents, '<aside>Who cares?</aside>')+      mediabag.empty() -- clean up+    end),+  },+ }
+ test/lua/module/pandoc-text.lua view
@@ -0,0 +1,51 @@+--+-- Tests for the pandoc.text module+--+local text = require 'pandoc.text'+local tasty = require 'tasty'++local group = tasty.test_group+local test = tasty.test_case+local assert = tasty.assert++assert.is_function = function (x)+  assert.are_equal(type(x), 'function')+end+-- We rely mostly on the tests in the `hslua-module-text` module. The+-- only thing we need to test is whether `pandoc.text` is available,+-- whether all functions are defined, and whether `require 'text'` works+-- (for backwards compatibility).+return {+  group 'module' {+    test('is table', function ()+      assert.are_equal(type(text), 'table')+    end),+    test('can be required as "text"', function ()+      assert.are_equal(require 'text', require 'pandoc.text')+    end)+  },++  group 'functions' {+    test('fromencoding', function ()+      assert.is_function(text.fromencoding)+    end),+    test('len', function ()+      assert.is_function(text.len)+    end),+    test('lower', function ()+      assert.is_function(text.lower)+    end),+    test('reverse', function ()+      assert.is_function(text.reverse)+    end),+    test('sub', function ()+      assert.is_function(text.sub)+    end),+    test('toencoding', function ()+      assert.is_function(text.toencoding)+    end),+    test('upper', function ()+      assert.is_function(text.upper)+    end),+  },+}