git.iko.soy/typeable/pandoc
on master help sign in
Merge branch 'master' of github.com:jgm/pandoc
commit4962220315d5e2c429ab63f558381528e808eafd authorJohn MacFarlane <jgm@berkeley.edu> dateFri 29 Dec 2017, 08:37 (9 years ago) parent377780776442a7
8 files changed+168−100
data/pandoc.lua+2−37 doc/lua-filters.md+67−24 pandoc.cabal+2−1 src/Text/Pandoc/Lua.hs+7−17 src/Text/Pandoc/Lua/Filter.hs−2 src/Text/Pandoc/Lua/Util.hs+1 test/Tests/Writers/Powerpoint.hs+86−18 tools/update-lua-docs.lua+3−1
data/pandoc.lua+2−37
@@ -23,7 +23,7 @@ THIS SOFTWARE.
2323-- @copyright © 2017 Albert Krewinkel
2424-- @license MIT
2525local M = {
26− _VERSION = "0.3.0"
26+ _VERSION = "0.4.0"
2727}
2828
2929local List = require 'pandoc.List'
@@ -657,7 +657,6 @@ M.Superscript = M.Inline:create_constructor(
657657
658658------------------------------------------------------------------------
659659-- Helpers
660−-- @section helpers
661660
662661local function assoc_key_equals (x)
663662 return function (y) return y[1] == x end
@@ -671,7 +670,7 @@ local function lookup(alist, key)
671670 return (List.find_if(alist, assoc_key_equals(key)) or {})[2]
672671end
673672
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.
675674-- @function apairs
676675-- @tparam {{key, value},...} alist associative list
677676local apairs = function (alist)
@@ -869,40 +868,6 @@ M.LowerAlpha = "LowerAlpha"
869868-- @see OrderedList
870869M.UpperAlpha = "UpperAlpha"
871870
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−
906871------------------------------------------------------------------------
907872-- Functions which have moved to different modules
908873local utils = require 'pandoc.utils'
doc/lua-filters.md+67−24
@@ -1172,18 +1172,6 @@ Lua functions for pandoc scripts.
11721172
11731173 Returns: strong element
11741174
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−
11871175[`Attr ([identifier[, classes[, attributes]]])`]{#Attr}
11881176
11891177: Create a new set of attributes (Attr).
@@ -1335,25 +1323,80 @@ Lua functions for pandoc scripts.
13351323
13361324 See also: [OrderedList](#OrderedList)
13371325
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:
13391334
1340−[`global_filter ()`]{#global_filter}
1335+ - Output of command.
13411336
1342−: Use functions defined in the global namespace to create a
1343− pandoc filter.
1337+ Raises:
13441338
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.
13461342
13471343 Usage:
13481344
1349− -- within a file defining a pandoc filter:
1350− function Str(text)
1351− return pandoc.Str(utf8.upper(text))
1352− end
1345+ local output = pandoc.pipe("sed", {"-e","s/a/b/"}, "abc")
1346+
1347+[`walk_block (element, filter)`]{#walk_block}
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:
13531393
1354− return {pandoc.global_filter()}
1355− -- the above is equivallent to
1356− -- return {{Str = Str}}
1394+ local org_markup = "/emphasis/" -- Input to be read
1395+ local document = pandoc.read(org_markup, "org")
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")
13571400
13581401# Module pandoc.utils
13591402
pandoc.cabal+2−1
@@ -622,7 +622,8 @@ test-suite test-pandoc
622622 QuickCheck >= 2.4 && < 2.11,
623623 containers >= 0.4.2.1 && < 0.6,
624624 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
626627 if flag(old-locale)
627628 build-depends: old-locale >= 1 && < 1.1,
628629 time >= 1.2 && < 1.5
src/Text/Pandoc/Lua.hs+7−17
@@ -29,17 +29,16 @@ module Text.Pandoc.Lua
2929 ( LuaException (..)
3030 , runLuaFilter
3131 , runPandocLua
32− , pushPandocModule
3332 ) where
3433
35−import Control.Monad (when, (>=>))
34+import Control.Monad ((>=>))
3635import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..),
3736 Status (OK), ToLuaStack (push))
3837import Text.Pandoc.Class (PandocIO)
3938import Text.Pandoc.Definition (Pandoc)
4039import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter)
4140import Text.Pandoc.Lua.Init (runPandocLua)
42−import Text.Pandoc.Lua.Module.Pandoc (pushModule) -- TODO: remove
41+import Text.Pandoc.Lua.Util (popValue)
4342import qualified Foreign.Lua as Lua
4443
4544-- | Run the Lua filter in @filterPath@ for a transformation to target
@@ -63,25 +62,16 @@ runLuaFilter' filterPath format pd = do
6362 Lua.throwLuaError luaErrMsg
6463 else do
6564 newtop <- Lua.gettop
66− -- Use the implicitly defined global filter if nothing was returned
67− when (newtop - top < 1) pushGlobalFilter
68− luaFilters <- peek (-1)
65+ -- Use the returned filters, or the implicitly defined global filter if
66+ -- nothing was returned.
67+ luaFilters <- if (newtop - top >= 1)
68+ then peek (-1)
69+ else Lua.getglobal "_G" *> fmap (:[]) popValue
6970 runAll luaFilters pd
7071 where
7172 registerFormat = do
7273 push format
7374 Lua.setglobal "FORMAT"
7475
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−
8276runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc
8377runAll = 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
164164 Lua.throwLuaError $
165165 "Error while trying to get a filter's return " ++
166166 "value from lua stack.\n" ++ err
167−
168−
src/Text/Pandoc/Lua/Util.hs+1
@@ -37,6 +37,7 @@ module Text.Pandoc.Lua.Util
3737 , setRawInt
3838 , addRawInt
3939 , raiseError
40+ , popValue
4041 , OrNil (..)
4142 , PushViaCall
4243 , pushViaCall
test/Tests/Writers/Powerpoint.hs+86−18
@@ -5,27 +5,35 @@ module Tests.Writers.Powerpoint (tests) where
55import Control.Exception (throwIO)
66import Text.Pandoc
77import Text.Pandoc.Builder
8+import Text.Pandoc.Arbitrary ()
9+import Text.Pandoc.Walk
810import Test.Tasty
911import Test.Tasty.HUnit
12+import Test.Tasty.QuickCheck
1013import Codec.Archive.Zip
11−import Data.List (isPrefixOf, isSuffixOf)
14+import Text.XML.Light
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
1226
1327----- Number of Slides -----------
1428
1529numberOfSlides :: WriterOptions -> Pandoc -> IO Int
1630numberOfSlides opts pd = do
17− mbs <- runIO $
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 $
25− length $
26− filter (isSuffixOf ".xml") $
27− filter (isPrefixOf "ppt/slides/slide") $
28− filesInArchive archive
31+ archive <- getPptxArchive opts pd
32+ return $
33+ length $
34+ filter (isSuffixOf ".xml") $
35+ filter (isPrefixOf "ppt/slides/slide") $
36+ filesInArchive archive
2937
3038testNumberOfSlides :: TestName -> Int -> WriterOptions -> Pandoc -> TestTree
3139testNumberOfSlides name n opts pd =
@@ -52,12 +60,12 @@ numSlideTests = testGroup "Number of slides in output"
5260 def
5361 (doc $ header 1 "Header" <> header 2 "subeader" <> para "foo")
5462 , testNumberOfSlides
55− "With h1 slide (using default slide-level)" 2
56− def
63+ "With h1 slide (using slide-level 3)" 2
64+ def {writerSlideLevel= Just 3}
5765 (doc $ header 1 "Header" <> para "foo")
5866 , testNumberOfSlides
59− "With h2 slide (using default slide-level)" 2
60− def
67+ "With h2 slide (using slide-level 3)" 3
68+ def {writerSlideLevel= Just 3}
6169 (doc $ header 1 "Header" <> header 2 "subeader" <> para "foo")
6270 , testNumberOfSlides
6371 "With image slide, no header" 3
@@ -94,8 +102,68 @@ numSlideTests = testGroup "Number of slides in output"
94102 def
95103 (doc $
96104 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"))
97110 ]
98111
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+ ]
99165
100166tests :: [TestTree]
101−tests = [numSlideTests]
167+tests = [ numSlideTests
168+ , contentTypeTests
169+ ]
tools/update-lua-docs.lua+3−1
@@ -25,7 +25,9 @@ end
2525
2626function Header (el)
2727 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
2931 in_module_section = false
3032 return el
3133 else