Search code examples
luapandoc

How to refactor repeating code in Pandoc lua filter


I am in the process of writing a Lua filter for Pandoc specifically targeting Pandoc generated LaTeX documents.

Here is a small code snippet from my filter:

LATEX_CODES_FOR_TAGS = {
  Span = {
    sans = '\\sffamily ',
    serif = '\\rmfamily ',
  },
  Div = {
    -- Font Styles
    sans = '\\begin{sffamily}',
    serif = '\\begin{rmfamily}',
  }
}

function Span(span)
  for tag, code_for_class in pairs(LATEX_CODES_FOR_TAGS) do
    for class, code in pairs(code_for_class) do
      if tag == "Span" then
        if span.classes:includes(class) then
          table.insert(span.content, 1, pandoc.RawInline('latex', code))
        end
      end
    end
  end
  return span
end

function Div(div)
  for tag, code_for_class in pairs(LATEX_CODES_FOR_TAGS) do
    for class, code in pairs(code_for_class) do
      if tag == "Div" then
        if div.classes:includes(class) then
          local code_end = code:gsub('begin', 'end')
          table.insert(div.content, 1, pandoc.RawBlock('latex', code))
          table.insert(div.content, pandoc.RawBlock('latex', code_end))
        end
      end
    end
  end
  return div
end

As you can see the code for the Div() and Span() functions are almost exactly the same, and therefore want to refactor the code.

I have hitherto been unable to come up with a solution that makes the filter without the repeating code.

I am still very new to Lua and wrapping my head around the concepts.

Thanks in advance.


Solution

  • Define the body of the loop as a separate function:

    local function create_handler(tag_str, handler_func)
       return
          function (obj)
             for tag, code_for_class in pairs(LATEX_CODES_FOR_TAGS) do
                for class, code in pairs(code_for_class) do
                   if tag == tag_str then
                      if obj.classes:includes(class) then
                         handler_func(obj, code)
                      end
                   end
                end
             end
             return obj
          end
    end
    
    Span = create_handler("Span",
       function(span, code)
          table.insert(span.content, 1, pandoc.RawInline('latex', code))
       end)
    
    Div = create_handler("Div",
       function(div, code)
          local code_end = code:gsub('begin', 'end')
          table.insert(div.content, 1, pandoc.RawBlock('latex', code))
          table.insert(div.content, pandoc.RawBlock('latex', code_end))
       end)