Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lua filters #3514

Merged
merged 10 commits into from
Mar 20, 2017
25 changes: 25 additions & 0 deletions MANUAL.txt
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,31 @@ Reader options

3. `$PATH` (executable only)

`--lua-filter=`*SCRIPT*

: Transform the document in a similar fashion as JSON filters (see
`--filter`), but use pandoc's build-in lua filtering system. The given
lua script is expected to return a list of lua filters which will be
applied in order. Each lua filter must contain element-transforming
functions indexed by the name of the AST element on which the filter
function should be applied.

The `pandoc` lua module provides helper functions for element
creation. It is always loaded into the script's lua environment.

The following is an example lua script for macro-expansion:

function expand_hello_world(inline)
if inline.c == '{{helloworld}}' then
return pandoc.Emph{ pandoc.Str "Hello, World" }
else
return inline
end
end

return {{Str = expand_hello_world}}


`-M` *KEY*[`=`*VAL*], `--metadata=`*KEY*[`:`*VAL*]

: Set the metadata field *KEY* to the value *VAL*. A value specified
Expand Down
144 changes: 144 additions & 0 deletions data/pandoc.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
--[[
pandoc.lua

Copyright (c) 2017 Albert Krewinkel

Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
]]

--- The module
local M = {
_version = "0.1.0"
}

--- Create a new set of attributes (Attr).
function M.Attributes(id, classes, key_values)
return {id, classes, key_values}
end

local Element = {}
--- Create a new element subtype
function Element:make_subtype(o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
--- Create a new element given its tag and arguments
function Element:new(tag, ...)
local element = { t = tag }
local content = {...}
-- special case for unary constructors
if #content == 1 then
element.c = content[1]
-- Don't set 'c' field if no further arguments were given. This is important
-- for nullary constructors like `Space` and `HorizontalRule`.
elseif #content > 0 then
element.c = content
end
setmetatable(element, self)
self.__index = self
return element
end

local function Doc(blocks, meta)
return {
["blocks"] = blocks,
["meta"] = meta,
["pandoc-api-version"] = {1,17,0,5},
}
end

local Inline = Element:make_subtype{}
local Block = Element:make_subtype{}

M.block_types = {
"BlockQuote",
"BulletList",
"CodeBlock",
"DefinitionList",
"Div",
"Header",
"HorizontalRule",
"HorizontalRule",
"LineBlock",
"Null",
"OrderedList",
"Para",
"Plain",
"RawBlock",
"Table",
}

M.inline_types = {
"Cite",
"Code",
"DisplayMath",
"DoubleQuoted",
"Emph",
"Image",
"InlineMath",
"LineBreak",
"Link",
"Math",
"Note",
"Quoted",
"RawInline",
"SingleQuoted",
"SmallCaps",
"SoftBreak",
"Space",
"Span",
"Str",
"Strikeout",
"Strong",
"Subscript",
"Superscript"
}

for _, block_type in pairs(M.block_types) do
M[block_type] = function(...)
return Block:new(block_type, ...)
end
end

for _, inline_type in pairs(M.inline_types) do
M[inline_type] = function(...)
return Inline:new(inline_type, ...)
end
end

--- Arrays to provide fast lookup of element types
local set_of_inline_types = {}
local set_of_block_types = {}

for i = 1, #M.inline_types do
set_of_inline_types[M.inline_types[i]] = true
end
for i = 1, #M.block_types do
set_of_block_types[M.block_types[i]] = true
end

function M.global_filter()
local res = {}
for k, v in pairs(_G) do
if set_of_inline_types[k] or set_of_block_types[k] or k == "Doc" then
res[k] = v
end
end
return res
end

M["Doc"] = Doc

return M
7 changes: 7 additions & 0 deletions pandoc.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ Data-Files:
data/abbreviations
-- sample lua custom writer
data/sample.lua
-- pandoc lua module
data/pandoc.lua
-- bash completion template
data/bash_completion.tpl
-- documentation
Expand Down Expand Up @@ -232,6 +234,7 @@ Extra-Source-Files:
test/odt/odt/*.odt
test/odt/markdown/*.md
test/odt/native/*.native
test/lua/strmacro.lua
Source-repository head
type: git
location: git://github.com/jgm/pandoc.git
Expand Down Expand Up @@ -282,6 +285,7 @@ Library
pandoc-types >= 1.17 && < 1.18,
aeson >= 0.7 && < 1.2,
aeson-pretty >= 0.8 && < 0.9,
hslua-aeson >= 0.1.0.2 && < 1,
tagsoup >= 0.13.7 && < 0.15,
base64-bytestring >= 0.1 && < 1.1,
zlib >= 0.5 && < 0.7,
Expand Down Expand Up @@ -396,6 +400,7 @@ Library
Text.Pandoc.Writers.Muse,
Text.Pandoc.Writers.Math,
Text.Pandoc.Writers.Shared,
Text.Pandoc.Lua,
Text.Pandoc.PDF,
Text.Pandoc.UTF8,
Text.Pandoc.Templates,
Expand Down Expand Up @@ -434,6 +439,7 @@ Library
Text.Pandoc.Readers.Org.ParserState,
Text.Pandoc.Readers.Org.Parsing,
Text.Pandoc.Readers.Org.Shared,
Text.Pandoc.Lua.PandocModule,
Text.Pandoc.CSS,
Text.Pandoc.UUID,
Text.Pandoc.Slides,
Expand Down Expand Up @@ -522,6 +528,7 @@ Test-Suite test-pandoc
Other-Modules: Tests.Old
Tests.Command
Tests.Helpers
Tests.Lua
Tests.Shared
Tests.Readers.LaTeX
Tests.Readers.HTML
Expand Down
1 change: 0 additions & 1 deletion pandoc.hs
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,3 @@ import Text.Pandoc.App (convertWithOpts, defaultOpts, options, parseOptions)

main :: IO ()
main = parseOptions options defaultOpts >>= convertWithOpts

16 changes: 16 additions & 0 deletions src/Text/Pandoc/App.hs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import Text.Pandoc
import Text.Pandoc.Builder (setMeta)
import Text.Pandoc.Class (PandocIO, getLog, withMediaBag)
import Text.Pandoc.Highlighting (highlightingStyles)
import Text.Pandoc.Lua ( runLuaFilter )
import Text.Pandoc.MediaBag (MediaBag, extractMediaBag, mediaDirectory)
import Text.Pandoc.PDF (makePDF)
import Text.Pandoc.Process (pipeProcess)
Expand Down Expand Up @@ -389,6 +390,7 @@ convertWithOpts opts = do
doc' <- (maybe return (extractMedia media) (optExtractMedia opts) >=>
return . flip (foldr addMetadata) (optMetadata opts) >=>
applyTransforms transforms >=>
applyLuaFilters datadir (optLuaFilters opts) [format] >=>
applyFilters datadir filters' [format]) doc

case writer of
Expand Down Expand Up @@ -514,6 +516,7 @@ data Opt = Opt
, optWrapText :: WrapOption -- ^ Options for wrapping text
, optColumns :: Int -- ^ Line length in characters
, optFilters :: [FilePath] -- ^ Filters to apply
, optLuaFilters :: [FilePath] -- ^ Lua filters to apply
, optEmailObfuscation :: ObfuscationMethod
, optIdentifierPrefix :: String
, optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks
Expand Down Expand Up @@ -580,6 +583,7 @@ defaultOpts = Opt
, optWrapText = WrapAuto
, optColumns = 72
, optFilters = []
, optLuaFilters = []
, optEmailObfuscation = NoObfuscation
, optIdentifierPrefix = ""
, optIndentedCodeClasses = []
Expand Down Expand Up @@ -725,6 +729,12 @@ expandFilterPath mbDatadir fp = liftIO $ do
else return fp
_ -> return fp

applyLuaFilters :: MonadIO m
=> Maybe FilePath -> [FilePath] -> [String] -> Pandoc -> m Pandoc
applyLuaFilters mbDatadir filters args d = do
expandedFilters <- mapM (expandFilterPath mbDatadir) filters
foldrM ($) d $ map (flip runLuaFilter args) expandedFilters

applyFilters :: MonadIO m
=> Maybe FilePath -> [FilePath] -> [String] -> Pandoc -> m Pandoc
applyFilters mbDatadir filters args d = do
Expand Down Expand Up @@ -814,6 +824,12 @@ options =
"PROGRAM")
"" -- "External JSON filter"

, Option "" ["lua-filter"]
(ReqArg
(\arg opt -> return opt { optLuaFilters = arg : optLuaFilters opt })
"SCRIPTPATH")
"" -- "Lua filter"

, Option "p" ["preserve-tabs"]
(NoArg
(\opt -> return opt { optPreserveTabs = True }))
Expand Down
Loading