git.iko.soy/typeable/pandoc
on master help sign in
Merge branch 'master' of github.com:jgm/pandoc
4962220John MacFarlane
data/pandoc.lua+2−37
@@ -23,7 +23,7 @@ THIS SOFTWARE.
23-- @copyright © 2017 Albert Krewinkel
23-- @copyright © 2017 Albert Krewinkel
24-- @license MIT
24-- @license MIT
25local M = {
25local M = {
26− _VERSION = "0.3.0"
26+ _VERSION = "0.4.0"
27}
27}
28
28
29local List = require 'pandoc.List'
29local List = require 'pandoc.List'
@@ -657,7 +657,6 @@ M.Superscript = M.Inline:create_constructor(
657
657
658------------------------------------------------------------------------
658------------------------------------------------------------------------
659-- Helpers
659-- Helpers
660−-- @section helpers
661
660
662local function assoc_key_equals (x)
661local function assoc_key_equals (x)
663 return function (y) return y[1] == x end
662 return function (y) return y[1] == x end
@@ -671,7 +670,7 @@ local function lookup(alist, key)
671 return (List.find_if(alist, assoc_key_equals(key)) or {})[2]
670 return (List.find_if(alist, assoc_key_equals(key)) or {})[2]
672end
671end
673
672
674−--- Return an iterator which returns key-value pairs of an associative list.
673+-- Return an iterator which returns key-value pairs of an associative list.
675-- @function apairs
674-- @function apairs
676-- @tparam {{key, value},...} alist associative list
675-- @tparam {{key, value},...} alist associative list
677local apairs = function (alist)
676local apairs = function (alist)
@@ -869,40 +868,6 @@ M.LowerAlpha = "LowerAlpha"
869-- @see OrderedList
868-- @see OrderedList
870M.UpperAlpha = "UpperAlpha"
869M.UpperAlpha = "UpperAlpha"
871
870
872−
873−------------------------------------------------------------------------
874−-- Helper Functions
875−-- @section helpers
876−
877−--- Use functions defined in the global namespace to create a pandoc filter.
878−-- All globally defined functions which have names of pandoc elements are
879−-- collected into a new table.
880−-- @return A list of filter functions
881−-- @usage
882−-- -- within a file defining a pandoc filter:
883−-- function Str(text)
884−-- return pandoc.Str(utf8.upper(text))
885−-- end
886−--
887−-- return {pandoc.global_filter()}
888−-- -- the above is equivallent to
889−-- -- return {{Str = Str}}
890−function M.global_filter()
891− local res = {}
892− function is_filter_function(k)
893− return M.Inline.constructor[k] or
894− M.Block.constructor[k] or
895− k == "Meta" or k == "Doc" or k == "Pandoc" or
896− k == "Block" or k == "Inline"
897− end
898− for k, v in pairs(_G) do
899− if is_filter_function(k) then
900− res[k] = v
901− end
902− end
903− return res
904−end
905−
906------------------------------------------------------------------------
871------------------------------------------------------------------------
907-- Functions which have moved to different modules
872-- Functions which have moved to different modules
908local utils = require 'pandoc.utils'
873local utils = require 'pandoc.utils'
doc/lua-filters.md+67−24
@@ -1172,18 +1172,6 @@ Lua functions for pandoc scripts.
1172
1172
1173 Returns: strong element
1173 Returns: strong element
1174
1174
1175−## Helpers
1176−
1177−[`apairs (value)`]{#apairs}
1178−
1179−: Return an iterator which returns key-value pairs of an
1180− associative list.
1181−
1182− Parameters:
1183−
1184− `value`:
1185− : },\...} alist associative list
1186−
1187[`Attr ([identifier[, classes[, attributes]]])`]{#Attr}
1175[`Attr ([identifier[, classes[, attributes]]])`]{#Attr}
1188
1176
1189: Create a new set of attributes (Attr).
1177: Create a new set of attributes (Attr).
@@ -1335,25 +1323,80 @@ Lua functions for pandoc scripts.
1335
1323
1336 See also: [OrderedList](#OrderedList)
1324 See also: [OrderedList](#OrderedList)
1337
1325
1338−## Helper Functions
1326+## Helper functions
1327+
1328+[`pipe (command, args, input)`]{#pipe}
1329+
1330+: Runs command with arguments, passing it some input, and
1331+ returns the output.
1332+
1333+ Returns:
1339
1334
1340−[`global_filter ()`]{#global_filter}
1335+ - Output of command.
1341
1336
1342−: Use functions defined in the global namespace to create a
1337+ Raises:
1343− pandoc filter.
1344
1338
1345− Returns: A list of filter functions
1339+ - A table containing the keys `command`, `error_code`, and
1340+ `output` is thrown if the command exits with a non-zero
1341+ error code.
1346
1342
1347 Usage:
1343 Usage:
1348
1344
1349− -- within a file defining a pandoc filter:
1345+ local output = pandoc.pipe("sed", {"-e","s/a/b/"}, "abc")
1350− function Str(text)
1346+
1351− return pandoc.Str(utf8.upper(text))
1347+[`walk_block (element, filter)`]{#walk_block}
1352− end
1348+
1349+: Apply a filter inside a block element, walking its contents.
1350+
1351+ Parameters:
1352+
1353+ `element`:
1354+ : the block element
1355+
1356+ `filter`:
1357+ : a lua filter (table of functions) to be applied within
1358+ the block element
1359+
1360+ Returns: the transformed block element
1361+
1362+[`walk_inline (element, filter)`]{#walk_inline}
1363+
1364+: Apply a filter inside an inline element, walking its
1365+ contents.
1366+
1367+ Parameters:
1368+
1369+ `element`:
1370+ : the inline element
1371+
1372+ `filter`:
1373+ : a lua filter (table of functions) to be applied within
1374+ the inline element
1375+
1376+ Returns: the transformed inline element
1377+
1378+[`read (markup[, format])`]{#read}
1379+
1380+: Parse the given string into a Pandoc document.
1381+
1382+ Parameters:
1383+
1384+ `markup`:
1385+ : the markup to be parsed
1386+
1387+ `format`:
1388+ : format specification, defaults to \"markdown\".
1389+
1390+ Returns: pandoc document
1391+
1392+ Usage:
1353
1393
1354− return {pandoc.global_filter()}
1394+ local org_markup = "/emphasis/" -- Input to be read
1355− -- the above is equivallent to
1395+ local document = pandoc.read(org_markup, "org")
1356− -- return {{Str = Str}}
1396+ -- Get the first block of the document
1397+ local block = document.blocks[1]
1398+ -- The inline element in that block is an `Emph`
1399+ assert(block.content[1].t == "Emph")
1357
1400
1358# Module pandoc.utils
1401# Module pandoc.utils
1359
1402
pandoc.cabal+2−1
@@ -622,7 +622,8 @@ test-suite test-pandoc
622 QuickCheck >= 2.4 && < 2.11,
622 QuickCheck >= 2.4 && < 2.11,
623 containers >= 0.4.2.1 && < 0.6,
623 containers >= 0.4.2.1 && < 0.6,
624 executable-path >= 0.0 && < 0.1,
624 executable-path >= 0.0 && < 0.1,
625− zip-archive >= 0.2.3.4 && < 0.4
625+ zip-archive >= 0.2.3.4 && < 0.4,
626+ xml >= 1.3.12 && < 1.4
626 if flag(old-locale)
627 if flag(old-locale)
627 build-depends: old-locale >= 1 && < 1.1,
628 build-depends: old-locale >= 1 && < 1.1,
628 time >= 1.2 && < 1.5
629 time >= 1.2 && < 1.5
src/Text/Pandoc/Lua.hs+7−17
@@ -29,17 +29,16 @@ module Text.Pandoc.Lua
29 ( LuaException (..)
29 ( LuaException (..)
30 , runLuaFilter
30 , runLuaFilter
31 , runPandocLua
31 , runPandocLua
32− , pushPandocModule
33 ) where
32 ) where
34
33
35−import Control.Monad (when, (>=>))
34+import Control.Monad ((>=>))
36import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..),
35import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..),
37 Status (OK), ToLuaStack (push))
36 Status (OK), ToLuaStack (push))
38import Text.Pandoc.Class (PandocIO)
37import Text.Pandoc.Class (PandocIO)
39import Text.Pandoc.Definition (Pandoc)
38import Text.Pandoc.Definition (Pandoc)
40import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter)
39import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter)
41import Text.Pandoc.Lua.Init (runPandocLua)
40import Text.Pandoc.Lua.Init (runPandocLua)
42−import Text.Pandoc.Lua.Module.Pandoc (pushModule) -- TODO: remove
41+import Text.Pandoc.Lua.Util (popValue)
43import qualified Foreign.Lua as Lua
42import qualified Foreign.Lua as Lua
44
43
45-- | Run the Lua filter in @filterPath@ for a transformation to target
44-- | Run the Lua filter in @filterPath@ for a transformation to target
@@ -63,25 +62,16 @@ runLuaFilter' filterPath format pd = do
63 Lua.throwLuaError luaErrMsg
62 Lua.throwLuaError luaErrMsg
64 else do
63 else do
65 newtop <- Lua.gettop
64 newtop <- Lua.gettop
66− -- Use the implicitly defined global filter if nothing was returned
65+ -- Use the returned filters, or the implicitly defined global filter if
67− when (newtop - top < 1) pushGlobalFilter
66+ -- nothing was returned.
67+ luaFilters <- if (newtop - top >= 1)
68− luaFilters <- peek (-1)
68+ then peek (-1)
69+ else Lua.getglobal "_G" *> fmap (:[]) popValue
69 runAll luaFilters pd
70 runAll luaFilters pd
70 where
71 where
71 registerFormat = do
72 registerFormat = do
72 push format
73 push format
73 Lua.setglobal "FORMAT"
74 Lua.setglobal "FORMAT"
74
75
75−pushGlobalFilter :: Lua ()
76−pushGlobalFilter = do
77− Lua.newtable
78− Lua.getglobal' "pandoc.global_filter"
79− Lua.call 0 1
80− Lua.rawseti (-2) 1
81−
82runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc
76runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc
83runAll = foldr ((>=>) . walkMWithLuaFilter) return
77runAll = foldr ((>=>) . walkMWithLuaFilter) return
84−
85−-- | DEPRECATED: Push the pandoc module to the Lua Stack.
86−pushPandocModule :: Maybe FilePath -> Lua Lua.NumResults
87−pushPandocModule = pushModule
src/Text/Pandoc/Lua/Filter.hs−2
@@ -164,5 +164,3 @@ singleElement x = do
164 Lua.throwLuaError $
164 Lua.throwLuaError $
165 "Error while trying to get a filter's return " ++
165 "Error while trying to get a filter's return " ++
166 "value from lua stack.\n" ++ err
166 "value from lua stack.\n" ++ err
167−
168−
src/Text/Pandoc/Lua/Util.hs+1
@@ -37,6 +37,7 @@ module Text.Pandoc.Lua.Util
37 , setRawInt
37 , setRawInt
38 , addRawInt
38 , addRawInt
39 , raiseError
39 , raiseError
40+ , popValue
40 , OrNil (..)
41 , OrNil (..)
41 , PushViaCall
42 , PushViaCall
42 , pushViaCall
43 , pushViaCall
test/Tests/Writers/Powerpoint.hs+86−18
@@ -5,27 +5,35 @@ module Tests.Writers.Powerpoint (tests) where
5import Control.Exception (throwIO)
5import Control.Exception (throwIO)
6import Text.Pandoc
6import Text.Pandoc
7import Text.Pandoc.Builder
7import Text.Pandoc.Builder
8+import Text.Pandoc.Arbitrary ()
9+import Text.Pandoc.Walk
8import Test.Tasty
10import Test.Tasty
9import Test.Tasty.HUnit
11import Test.Tasty.HUnit
12+import Test.Tasty.QuickCheck
10import Codec.Archive.Zip
13import Codec.Archive.Zip
14+import Text.XML.Light
11−import Data.List (isPrefixOf, isSuffixOf)
15+import Data.List (isPrefixOf, isSuffixOf, sort)
16+import Data.Maybe (mapMaybe)
17+
18+getPptxArchive :: WriterOptions -> Pandoc -> IO Archive
19+getPptxArchive opts pd = do
20+ mbs <- runIO $
21+ do setUserDataDir $ Just "../data"
22+ writePowerpoint opts pd
23+ case mbs of
24+ Left e -> throwIO e
25+ Right bs -> return $ toArchive bs
12
26
13----- Number of Slides -----------
27----- Number of Slides -----------
14
28
15numberOfSlides :: WriterOptions -> Pandoc -> IO Int
29numberOfSlides :: WriterOptions -> Pandoc -> IO Int
16numberOfSlides opts pd = do
30numberOfSlides opts pd = do
17− mbs <- runIO $
31+ archive <- getPptxArchive opts pd
18− do setUserDataDir $ Just "../data"
19− writePowerpoint opts pd
20− case mbs of
21− Left e -> throwIO e
22− Right bs -> do
23− let archive = toArchive bs
24− return $
32+ return $
25− length $
33+ length $
26− filter (isSuffixOf ".xml") $
34+ filter (isSuffixOf ".xml") $
27− filter (isPrefixOf "ppt/slides/slide") $
35+ filter (isPrefixOf "ppt/slides/slide") $
28− filesInArchive archive
36+ filesInArchive archive
29
37
30testNumberOfSlides :: TestName -> Int -> WriterOptions -> Pandoc -> TestTree
38testNumberOfSlides :: TestName -> Int -> WriterOptions -> Pandoc -> TestTree
31testNumberOfSlides name n opts pd =
39testNumberOfSlides name n opts pd =
@@ -52,12 +60,12 @@ numSlideTests = testGroup "Number of slides in output"
52 def
60 def
53 (doc $ header 1 "Header" <> header 2 "subeader" <> para "foo")
61 (doc $ header 1 "Header" <> header 2 "subeader" <> para "foo")
54 , testNumberOfSlides
62 , testNumberOfSlides
55− "With h1 slide (using default slide-level)" 2
63+ "With h1 slide (using slide-level 3)" 2
56− def
64+ def {writerSlideLevel= Just 3}
57 (doc $ header 1 "Header" <> para "foo")
65 (doc $ header 1 "Header" <> para "foo")
58 , testNumberOfSlides
66 , testNumberOfSlides
59− "With h2 slide (using default slide-level)" 2
67+ "With h2 slide (using slide-level 3)" 3
60− def
68+ def {writerSlideLevel= Just 3}
61 (doc $ header 1 "Header" <> header 2 "subeader" <> para "foo")
69 (doc $ header 1 "Header" <> header 2 "subeader" <> para "foo")
62 , testNumberOfSlides
70 , testNumberOfSlides
63 "With image slide, no header" 3
71 "With image slide, no header" 3
@@ -94,8 +102,68 @@ numSlideTests = testGroup "Number of slides in output"
94 def
102 def
95 (doc $
103 (doc $
96 para "first slide" <> horizontalRule <> para "last slide")
104 para "first slide" <> horizontalRule <> para "last slide")
105+ , testNumberOfSlides
106+ "with notes slide" 2
107+ def
108+ (doc $
109+ para $ text "Foo" <> note (para "note text"))
97 ]
110 ]
98
111
112+----- Content Types -----------
113+
114+
115+contentTypesFileExists :: WriterOptions -> Pandoc -> TestTree
116+contentTypesFileExists opts pd =
117+ testCase "Existence of [Content_Types].xml file" $
118+ do archive <- getPptxArchive opts pd
119+ assertBool "Missing [Content_Types].xml file" $
120+ "[Content_Types].xml" `elem` (filesInArchive archive)
121+
122+
123+
124+-- We want an "Override" entry for each xml file under ppt/.
125+prop_ContentOverrides :: Pandoc -> IO Bool
126+prop_ContentOverrides pd = do
127+ -- remove Math to avoid warnings
128+ let go :: Inline -> Inline
129+ go (Math _ _) = Str "Math"
130+ go i = i
131+ pd' = walk go pd
132+ archive <- getPptxArchive def pd'
133+ let xmlFiles = filter ("[Content_Types].xml" /=) $
134+ filter (isSuffixOf ".xml") $
135+ filesInArchive archive
136+ contentTypes <- case findEntryByPath "[Content_Types].xml" archive of
137+ Just ent -> return $ fromEntry ent
138+ Nothing -> throwIO $
139+ PandocSomeError "Missing [Content_Types].xml file"
140+ typesElem <- case parseXMLDoc contentTypes of
141+ Just element -> return $ element
142+ Nothing -> throwIO $
143+ PandocSomeError "[Content_Types].xml cannot be parsed"
144+ let ns = findAttr (QName "xmlns" Nothing Nothing) typesElem
145+ overrides = findChildren (QName "Override" ns Nothing) typesElem
146+ partNames = mapMaybe (findAttr (QName "PartName" Nothing Nothing)) overrides
147+ -- files in content_types are absolute
148+ absXmlFiles = map (\fp -> case fp of
149+ ('/':_) -> fp
150+ _ -> '/': fp
151+ )
152+ xmlFiles
153+ return $ sort absXmlFiles == sort partNames
154+
155+contentOverridesTests :: TestTree
156+contentOverridesTests = localOption (QuickCheckTests 20) $
157+ testProperty "Content Overrides for each XML file" $
158+ \x -> ioProperty $ prop_ContentOverrides (x :: Pandoc)
159+
160+contentTypeTests :: TestTree
161+contentTypeTests = testGroup "[Content_Types].xml file"
162+ [ contentTypesFileExists def (doc $ para "foo")
163+ , contentOverridesTests
164+ ]
99
165
100tests :: [TestTree]
166tests :: [TestTree]
101−tests = [numSlideTests]
167+tests = [ numSlideTests
168+ , contentTypeTests
169+ ]
tools/update-lua-docs.lua+3−1
@@ -25,7 +25,9 @@ end
25
25
26function Header (el)
26function Header (el)
27 if in_module_section then
27 if in_module_section then
28− if el.level == 1 then
28+ if el.level == 1 or
29+ -- special case for Module pandoc
30+ (el.level == 2 and el.identifier == 'helper-functions') then
29 in_module_section = false
31 in_module_section = false
30 return el
32 return el
31 else
33 else