From e0cb0dab1821e93e50e8b8006ef569bdeee9a248 Mon Sep 17 00:00:00 2001
From: Albert Krewinkel <albert@zeitkraut.de>
Date: Sat, 13 Jan 2018 22:29:16 +0100
Subject: data/pandoc.lua: accept single block as singleton list

Every constructor which accepts a list of blocks now also accepts a
single block element for convenience.  Furthermore, strings are accepted as
shorthand for `{pandoc.Str "text"}` in constructors.
---
 data/pandoc.lua   | 47 ++++++++++++++++++++++++++++-------------------
 test/Tests/Lua.hs | 18 ++++++++++++++----
 2 files changed, 42 insertions(+), 23 deletions(-)

diff --git a/data/pandoc.lua b/data/pandoc.lua
index 40c5f9d91..a041c133d 100644
--- a/data/pandoc.lua
+++ b/data/pandoc.lua
@@ -186,15 +186,24 @@ function AstElement:create_constructor(tag, fn, accessors)
   return constr
 end
 
+--- Convert AstElement input into a list if necessary.
+-- @local
+local function ensureList (x)
+  if x.tag then
+    -- Lists are not tagged, but all elements are
+    return List:new{x}
+  else
+    return List:new(x)
+  end
+end
+
 --- Ensure a given object is an Inline element, or convert it into one.
+-- @local
 local function ensureInlineList (x)
-  if type(x) == string then
+  if type(x) == 'string' then
     return List:new{M.Str(x)}
-  elseif x.tag then
-    -- Lists are not tagged, but all elements are
-    return List:new{x}
   else
-    return x
+    return ensureList(x)
   end
 end
 
@@ -209,7 +218,7 @@ end
 M.Pandoc = AstElement:make_subtype'Pandoc'
 function M.Pandoc:new (blocks, meta)
   return {
-    blocks = List:new(blocks),
+    blocks = ensureList(blocks),
     meta = meta or {},
   }
 end
@@ -239,7 +248,7 @@ M.MetaValue = AstElement:make_subtype('MetaValue')
 -- @tparam {Block,...} blocks blocks
 M.MetaBlocks = M.MetaValue:create_constructor(
   'MetaBlocks',
-  function (content) return List:new(content) end
+  function (content) return ensureList(content) end
 )
 
 --- Meta inlines
@@ -255,7 +264,7 @@ M.MetaInlines = M.MetaValue:create_constructor(
 -- @tparam {MetaValue,...} meta_values list of meta values
 M.MetaList = M.MetaValue:create_constructor(
   'MetaList',
-  function (content) return List:new(content) end
+  function (content) return ensureList(content) end
 )
 
 --- Meta map
@@ -295,7 +304,7 @@ M.Block = AstElement:make_subtype'Block'
 -- @treturn     Block                   block quote element
 M.BlockQuote = M.Block:create_constructor(
   "BlockQuote",
-  function(content) return {c = content} end,
+  function(content) return {c = ensureList(content)} end,
   "content"
 )
 
@@ -305,7 +314,7 @@ M.BlockQuote = M.Block:create_constructor(
 -- @treturn     Block                         bullet list element
 M.BulletList = M.Block:create_constructor(
   "BulletList",
-  function(content) return {c = content} end,
+  function(content) return {c = ensureList(content)} end,
   "content"
 )
 
@@ -326,7 +335,7 @@ M.CodeBlock = M.Block:create_constructor(
 -- @treturn     Block                  definition list element
 M.DefinitionList = M.Block:create_constructor(
   "DefinitionList",
-  function(content) return {c = List:new(content)} end,
+  function(content) return {c = ensureList(content)} end,
   "content"
 )
 
@@ -338,7 +347,7 @@ M.DefinitionList = M.Block:create_constructor(
 M.Div = M.Block:create_constructor(
   "Div",
   function(content, attr)
-    return {c = {attr or M.Attr(), List:new(content)}}
+    return {c = {attr or M.Attr(), ensureList(content)}}
   end,
   {{"identifier", "classes", "attributes"}, "content"}
 )
@@ -371,7 +380,7 @@ M.HorizontalRule = M.Block:create_constructor(
 -- @treturn     Block                   line block element
 M.LineBlock = M.Block:create_constructor(
   "LineBlock",
-  function(content) return {c = List:new(content)} end,
+  function(content) return {c = ensureList(content)} end,
   "content"
 )
 
@@ -392,7 +401,7 @@ M.OrderedList = M.Block:create_constructor(
   "OrderedList",
   function(items, listAttributes)
     listAttributes = listAttributes or {1, M.DefaultStyle, M.DefaultDelim}
-    return {c = {listAttributes, List:new(items)}}
+    return {c = {listAttributes, ensureList(items)}}
   end,
   {{"start", "style", "delimiter"}, "content"}
 )
@@ -468,7 +477,7 @@ M.Inline = AstElement:make_subtype'Inline'
 M.Cite = M.Inline:create_constructor(
   "Cite",
   function(content, citations)
-    return {c = {List:new(citations), ensureInlineList(content)}}
+    return {c = {ensureList(citations), ensureInlineList(content)}}
   end,
   {"citations", "content"}
 )
@@ -572,7 +581,7 @@ M.InlineMath = M.Inline:create_constructor(
 -- @tparam      {Block,...} content     footnote block content
 M.Note = M.Inline:create_constructor(
   "Note",
-  function(content) return {c = List:new(content)} end,
+  function(content) return {c = ensureList(content)} end,
   "content"
 )
 
@@ -806,7 +815,7 @@ end
 M.Attr = AstElement:make_subtype'Attr'
 function M.Attr:new (identifier, classes, attributes)
   identifier = identifier or ''
-  classes = List:new(classes or {})
+  classes = ensureList(classes or {})
   attributes = setmetatable(to_alist(attributes or {}), AttributeList)
   return {identifier, classes, attributes}
 end
@@ -838,8 +847,8 @@ function M.Citation:new (id, mode, prefix, suffix, note_num, hash)
   return {
     id = id,
     mode = mode,
-    prefix = prefix or {},
-    suffix = suffix or {},
+    prefix = ensureList(prefix or {}),
+    suffix = ensureList(suffix or {}),
     note_num = note_num or 0,
     hash = hash or 0,
   }
diff --git a/test/Tests/Lua.hs b/test/Tests/Lua.hs
index c7652a200..d728cb9d0 100644
--- a/test/Tests/Lua.hs
+++ b/test/Tests/Lua.hs
@@ -12,8 +12,8 @@ import Text.Pandoc.Builder (bulletList, divWith, doc, doubleQuoted, emph,
                             header, linebreak, para, plain, rawBlock,
                             singleQuoted, space, str, strong, (<>))
 import Text.Pandoc.Class (runIOorExplode, setUserDataDir)
-import Text.Pandoc.Definition (Block, Inline (Emph, Str), Meta, Pandoc,
-                               pandocTypesVersion)
+import Text.Pandoc.Definition (Block (BlockQuote, Para), Inline (Emph, Str),
+                               Meta, Pandoc, pandocTypesVersion)
 import Text.Pandoc.Lua (runLuaFilter, runPandocLua)
 import Text.Pandoc.Options (def)
 import Text.Pandoc.Shared (pandocVersion)
@@ -126,8 +126,18 @@ tests = map (localOption (QuickCheckTests 20))
         =<< Lua.peek Lua.stackTop
 
   , testCase "Allow singleton inline in constructors" . runPandocLua' $ do
-      res <- Lua.callFunc "pandoc.Emph" (Str "test")
-      Lua.liftIO $ assertEqual "Not the exptected Emph" (Emph [Str "test"]) res
+      Lua.liftIO . assertEqual "Not the exptected Emph" (Emph [Str "test"])
+        =<< Lua.callFunc "pandoc.Emph" (Str "test")
+      Lua.liftIO . assertEqual "Unexpected element" (Para [Str "test"])
+        =<< Lua.callFunc "pandoc.Para" ("test" :: String)
+      Lua.liftIO . assertEqual "Unexptected element"
+        (BlockQuote [Para [Str "foo"]]) =<< (
+        do
+          Lua.getglobal' "pandoc.BlockQuote"
+          Lua.push (Para [Str "foo"])
+          _ <- Lua.call 1 1
+          Lua.peek Lua.stackTop
+        )
 
   , testCase "informative error messages" . runPandocLua' $ do
       Lua.pushboolean True
-- 
cgit v1.2.3