git.iko.soy/typeable/pandoc
on master help sign in
Lua: allow access to pandoc state (#5015)

* Lua: allow access to pandoc state Lua filters and custom writers now have read-only access to most fields of pandoc's internal state via the global variable `PANDOC_STATE`. * Lua: allow iterating through fields of PANDOC_STATE * Lua filters doc: describe CommonState * Lua filters doc: mention global variable PANDOC_STATE * Lua: add access to logs Log messages can currently only be printed, but not decomposed.

commit096cbe698746d621bfee9607b1ab826240082a10 authorAlbert Krewinkel <albert+github@zeitkraut.de> dateFri 26 Oct 2018, 07:12 (8 years ago) parent8f9ab3d
6 files changed+168−6
doc/lua-filters.md+47 pandoc.cabal+1 src/Text/Pandoc/Lua/Init.hs+10−5 src/Text/Pandoc/Lua/StackInstances.hs+85−1 test/command/lua-pandoc-state.lua+11 test/command/lua-pandoc-state.md+14
doc/lua-filters.md+47
@@ -171,6 +171,12 @@ variables.
171171: The name used to involve the filter. This value can be used
172172 to find files relative to the script file. This variable is
173173 also set in custom writers.
174+
175+`PANDOC_STATE`
176+: The state shared by all readers and writers. It is used by
177+ pandoc to collect and pass information. The value of this
178+ variable is of type [CommonState](#type-ref-CommonState) and
179+ is read-only.
174180
175181# Pandoc Module
176182
@@ -1280,6 +1286,46 @@ Pandoc reader options
12801286: track changes setting for docx; one of `AcceptChanges`,
12811287 `RejectChanges`, and `AllChanges` (string)
12821288
1289+## CommonState {#type-ref-CommonState}
1290+
1291+The state used by pandoc to collect information and make it
1292+available to readers and writers.
1293+
1294+`input_files`
1295+: List of input files from command line ([List] of strings)
1296+
1297+`output_file`
1298+: Output file from command line (string or nil)
1299+
1300+`log`
1301+: A list of log messages in reverse order ([List] of [LogMessage]s)
1302+
1303+`request_headers`
1304+: Headers to add for HTTP requests; table with header names as
1305+ keys and header contents as value (table)
1306+
1307+`resource_path`
1308+: Path to search for resources like included images ([List] of
1309+ strings)
1310+
1311+`source_url`
1312+: Absolute URL or directory of first source file (string or
1313+ nil)
1314+
1315+`user_data_dir`
1316+: Directory to search for data files (string or nil)
1317+
1318+`trace`
1319+: Whether tracing messages are issued (boolean)
1320+
1321+`verbosity`
1322+: Verbosity level; one of `INFO`, `WARNING`, `ERROR` (string)
1323+
1324+## LogMessage {#type-ref-LogMessage}
1325+
1326+A pandoc log message. Object have no fields, but can be converted
1327+to a string via `tostring`.
1328+
12831329[Block]: #type-ref-Block
12841330[List]: #module-pandoc.list
12851331[MetaValue]: #type-ref-MetaValue
@@ -1287,6 +1333,7 @@ Pandoc reader options
12871333[Attr]: #type-ref-Attr
12881334[Attributes]: #type-ref-Attributes
12891335[citations]: #type-ref-Citation
1336+[LogMessage]: #type-ref-LogMessage
12901337
12911338# Module text
12921339
pandoc.cabal+1
@@ -191,6 +191,7 @@ extra-source-files:
191191 test/command/SVG_logo.svg
192192 test/command/corrupt.svg
193193 test/command/inkscape-cube.svg
194+ test/command/lua-pandoc-state.lua
194195 test/command/sub-file-chapter-1.tex
195196 test/command/sub-file-chapter-2.tex
196197 test/command/bar.tex
src/Text/Pandoc/Lua/Init.hs+10−5
@@ -43,8 +43,8 @@ import Data.Version (Version (versionBranch))
4343import Foreign.Lua (Lua)
4444import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8)
4545import Paths_pandoc (version)
46−import Text.Pandoc.Class (PandocIO, getCommonState, getUserDataDir, getMediaBag,
47− setMediaBag)
46+import Text.Pandoc.Class (CommonState, PandocIO, getCommonState,
47+ getUserDataDir, getMediaBag, setMediaBag)
4848import Text.Pandoc.Definition (pandocTypesVersion)
4949import Text.Pandoc.Lua.Packages (LuaPackageParams (..),
5050 installPandocPackageSearcher)
@@ -61,9 +61,12 @@ newtype LuaException = LuaException String deriving (Show)
6161-- initialization.
6262runPandocLua :: Lua a -> PandocIO (Either LuaException a)
6363runPandocLua luaOp = do
64+ commonState <- getCommonState
6465 luaPkgParams <- luaPackageParams
6566 enc <- liftIO $ getForeignEncoding <* setForeignEncoding utf8
66− res <- liftIO $ Lua.runEither (initLuaState luaPkgParams *> luaOp)
67+ res <- liftIO . Lua.runEither $ do
68+ initLuaState commonState luaPkgParams
69+ luaOp
6770 liftIO $ setForeignEncoding enc
6871 newMediaBag <- liftIO (readIORef (luaPkgMediaBag luaPkgParams))
6972 setMediaBag newMediaBag
@@ -84,14 +87,16 @@ luaPackageParams = do
8487 }
8588
8689-- Initialize the lua state with all required values
87−initLuaState :: LuaPackageParams -> Lua ()
88−initLuaState luaPkgParams = do
90+initLuaState :: CommonState -> LuaPackageParams -> Lua ()
91+initLuaState commonState luaPkgParams = do
8992 Lua.openlibs
9093 Lua.preloadTextModule "text"
9194 Lua.push (versionBranch version)
9295 Lua.setglobal "PANDOC_VERSION"
9396 Lua.push (versionBranch pandocTypesVersion)
9497 Lua.setglobal "PANDOC_API_VERSION"
98+ Lua.push commonState
99+ Lua.setglobal "PANDOC_STATE"
95100 installPandocPackageSearcher luaPkgParams
96101 loadScriptFromDataDir (luaPkgDataDir luaPkgParams) "init.lua"
97102 putConstructorsInRegistry
src/Text/Pandoc/Lua/StackInstances.hs+85−1
@@ -38,14 +38,18 @@ import Prelude
3838import Control.Applicative ((<|>))
3939import Data.Data (showConstr, toConstr)
4040import Foreign.Lua (Lua, Peekable, Pushable, StackIndex)
41+import Foreign.Lua.Types.Peekable (reportValueOnFailure)
4142import Foreign.Lua.Userdata ( ensureUserdataMetatable, pushAnyWithMetatable
42− , metatableName)
43+ , toAnyWithName, metatableName)
44+import Text.Pandoc.Class (CommonState (..))
4345import Text.Pandoc.Definition
4446import Text.Pandoc.Extensions (Extensions)
47+import Text.Pandoc.Logging (LogMessage, showLogMessage)
4548import Text.Pandoc.Lua.Util (defineHowTo, pushViaConstructor)
4649import Text.Pandoc.Options (ReaderOptions (..), TrackChanges)
4750import Text.Pandoc.Shared (Element (Blk, Sec))
4851
52+import qualified Data.Map as Map
4953import qualified Data.Set as Set
5054import qualified Foreign.Lua as Lua
5155import qualified Text.Pandoc.Lua.Util as LuaUtil
@@ -386,5 +390,85 @@ instance Pushable ReaderOptions where
386390-- | Dummy type to allow values of arbitrary Lua type.
387391newtype AnyValue = AnyValue StackIndex
388392
393+--
394+-- TODO: Much of the following should be abstracted, factored out
395+-- and go into HsLua.
396+--
397+
389398instance Peekable AnyValue where
390399 peek = return . AnyValue
400+
401+-- | Name used by Lua for the @CommonState@ type.
402+commonStateTypeName :: String
403+commonStateTypeName = "Pandoc CommonState"
404+
405+instance Peekable CommonState where
406+ peek idx = reportValueOnFailure commonStateTypeName
407+ (`toAnyWithName` commonStateTypeName) idx
408+
409+instance Pushable CommonState where
410+ push st = pushAnyWithMetatable pushCommonStateMetatable st
411+ where
412+ pushCommonStateMetatable = ensureUserdataMetatable commonStateTypeName $ do
413+ LuaUtil.addFunction "__index" indexCommonState
414+ LuaUtil.addFunction "__pairs" pairsCommonState
415+
416+indexCommonState :: CommonState -> AnyValue -> Lua Lua.NumResults
417+indexCommonState st (AnyValue idx) = Lua.ltype idx >>= \case
418+ Lua.TypeString -> 1 <$ (Lua.peek idx >>= pushField)
419+ _ -> 1 <$ Lua.pushnil
420+ where
421+ pushField :: String -> Lua ()
422+ pushField name = case lookup name commonStateFields of
423+ Just pushValue -> pushValue st
424+ Nothing -> Lua.pushnil
425+
426+pairsCommonState :: CommonState -> Lua Lua.NumResults
427+pairsCommonState st = do
428+ Lua.pushHaskellFunction nextFn
429+ Lua.pushnil
430+ Lua.pushnil
431+ return 3
432+ where
433+ nextFn :: AnyValue -> AnyValue -> Lua Lua.NumResults
434+ nextFn _ (AnyValue idx) =
435+ Lua.ltype idx >>= \case
436+ Lua.TypeNil -> case commonStateFields of
437+ [] -> 2 <$ (Lua.pushnil *> Lua.pushnil)
438+ (key, pushValue):_ -> 2 <$ (Lua.push key *> pushValue st)
439+ Lua.TypeString -> do
440+ key <- Lua.peek idx
441+ case tail $ dropWhile ((/= key) . fst) commonStateFields of
442+ [] -> 2 <$ (Lua.pushnil *> Lua.pushnil)
443+ (nextKey, pushValue):_ -> 2 <$ (Lua.push nextKey *> pushValue st)
444+ _ -> 2 <$ (Lua.pushnil *> Lua.pushnil)
445+
446+commonStateFields :: [(String, CommonState -> Lua ())]
447+commonStateFields =
448+ [ ("input_files", Lua.push . stInputFiles)
449+ , ("output_file", Lua.push . Lua.Optional . stOutputFile)
450+ , ("log", Lua.push . stLog)
451+ , ("request_headers", Lua.push . Map.fromList . stRequestHeaders)
452+ , ("resource_path", Lua.push . stResourcePath)
453+ , ("source_url", Lua.push . Lua.Optional . stSourceURL)
454+ , ("user_data_dir", Lua.push . Lua.Optional . stUserDataDir)
455+ , ("trace", Lua.push . stTrace)
456+ , ("verbosity", Lua.push . show . stVerbosity)
457+ ]
458+
459+-- | Name used by Lua for the @CommonState@ type.
460+logMessageTypeName :: String
461+logMessageTypeName = "Pandoc LogMessage"
462+
463+instance Peekable LogMessage where
464+ peek idx = reportValueOnFailure logMessageTypeName
465+ (`toAnyWithName` logMessageTypeName) idx
466+
467+instance Pushable LogMessage where
468+ push msg = pushAnyWithMetatable pushLogMessageMetatable msg
469+ where
470+ pushLogMessageMetatable = ensureUserdataMetatable logMessageTypeName $
471+ LuaUtil.addFunction "__tostring" tostringLogMessage
472+
473+tostringLogMessage :: LogMessage -> Lua String
474+tostringLogMessage = return . showLogMessage
test/command/lua-pandoc-state.luanew file+11
@@ -0,0 +1,11 @@
1+function report (what, value)
2+ print(string.format('%16s: %s', what, value))
3+end
4+report('# input files', #PANDOC_STATE.input_files)
5+report('output file', PANDOC_STATE.output_file)
6+report('# request header', #PANDOC_STATE.request_headers)
7+report('resource path', table.concat(PANDOC_STATE.resource_path, ', '))
8+report('source URL', PANDOC_STATE.source_url)
9+report('user data dir', PANDOC_STATE.user_data_dir and 'defined' or 'unset')
10+report('trace', PANDOC_STATE.trace)
11+report('verbosity', PANDOC_STATE.verbosity)
test/command/lua-pandoc-state.mdnew file+14
@@ -0,0 +1,14 @@
1+```
2+% pandoc --lua-filter=command/lua-pandoc-state.lua
3+Hello
4+^D
5+ # input files: 0
6+ output file: nil
7+# request header: 0
8+ resource path: .
9+ source URL: nil
10+ user data dir: defined
11+ trace: false
12+ verbosity: WARNING
13+<p>Hello</p>
14+```