MAN-PANDOC(lua)

📅 published 1.3.1, from nixos 26.05, nixpkgs unstable

🌸 MAN-PANDOC(lua) 🌸

NAME

MAN-PANDOC(lua) — the Lua filter that tweaks our manual pages during conversion to HTML by pandoc. That’s the whole first-line vibe, bestie. 💅

SYNOPSIS

It’s a pandoc Lua filter, so the “synopsis” is literally the filter code below. No extra command-line flags are hiding in this page; the original just has the filter functions and the BINARY env check.

Debugging/developing? The original note says: see https://pandoc.org/lua‐filters.html#debugging‐lua‐fil‐ters on how to debug and develop these filters. 🧚

DESCRIPTION

Okay so this filter’s job is tweaking our manual pages during conversion to HTML by pandoc. It does a bunch of little AST glow-ups:

  • overwrites the title,
  • turns subcommand headers into links,
  • rewrites SYNOPSIS entries into links,
  • rewrites SEE ALSO entries into links,
  • turns backtick-quoted text into inline code or links,
  • turns implicit <https://...> links into actual links.

No cap: every transformation is in the exact code block at the end, and the function-by-function tea is below. 🍵

ENVIRONMENT

  • BINARY — you must set this env var to the name of the primary executable, e.g. 'sq' or 'sq-git'. The filter does BINARY = os.getenv("BINARY"); if BINARY == nil or BINARY == "", it errors out with the BINARY environment variable should be set to the bi‐nary’s name, e.g., sq message (line-wrapped in the original code). Periodt. 💀

FUNCTIONS

Meta(m)

Overwrites the title. The original comment is like “it just says 'sq' which looks odd,” so it slaps in:

m.title = "Sequoia PGP Manual Pages"
return m

Header (h)

Turns subcommand headers into links. It checks whether h.c[1].t == 'Str' and h.c[1].text == BINARY; if yes, it does h.c = pandoc.Link(h.c, subcommand_to_link(h.c)) and returns h. The local subcommand_to_link (s) helper walks the sequence, turns 'Space' tokens into '-', concatenates the other tokens’ .text, and appends ".1.html". 🎀

rewrite_synopsis (inlines)

Rewrites the entries in the "SYNOPSIS" section into links. Its synopsis_p (s) matcher is looking for this exact sequence:

--      [1] LineBreak {}
--      [2] Strong {
--        content: Inlines[7] {
--          [1] Str "sq"
--          [2] Space
--          [3] Str "key"
--          [4] Space
--          [5] Str "userid"
--          [6] Space
--          [7] Str "add"
--        }
--      }
--      [3] Space
--      [4] Str "["

The predicate checks s[1].t == 'LineBreak', s[2].t == 'Strong', s[2].c[1].text == BINARY, (not s[2].c[2] or s[2].c[2].t == 'Space'), s[3].t == 'Space', and s[4].t == 'Str' with s[4].text == '['. Then it loops for i = 1, #inlines‐4 do, packs a four-token window with table.pack(table.unpack(inlines, i, i + 3)), and if it matches, sets inlines[i+1] = pandoc.Link(inlines[i+1], subcommand_to_link(inlines[i+1].c)). The same subcommand_to_link helper as above builds the .1.html filename. 🦋

rewrite_see_also (inlines)

Transforms the entries in the "SEE ALSO" section into links. Its see_also_p (a, b) checks a.t == 'Strong', b.t == 'Str', and string.sub(b.text, 1, 3) == '(1)'. The exact match comment is:

--      [43] Strong {
--        content: Inlines[1] {
--          [1] Str "sq‐wkd"
--        }
--      }
--      [44] Str "(1),"

Then it loops for i = 1, #inlines‐1 do; if the predicate hits, it does inlines[i] = pandoc.Link(inlines[i], inlines[i].c[1].text .. ".1.html"). Slay. 🔥

rewrite_inline_code (inlines)

Transforms text enclosed in backticks into inline code or links. This one is the main-character of the filter, lowkey.

  • inline_quoted (s) returns the length of the token stream containing quoted text starting from the first token, or nil if no quoted text is found. It starts when s[1].t == 'Str' and string.sub(s[1].text, 1, 1) == "‘", then finds the end with the first token where string.find(s[i].text, "‘", 2) is truthy.
  • tokens_to_text (s) turns 'Space' tokens into ' ' and concatenates the rest via .text.
  • subcommand_to_link (s) returns string.gsub(s, " ", "‐") .. ".1.html".
  • The main loop is a while loop because it mutates inlines. The original comment says a for loop doesn’t cut it since #inlines is evaluated only once when entering the loop for the first time.

Inside the loop it does local len = inline_quoted(table.pack(table.unpack(inlines, i))); if len is truthy, it flattens the matched tokens with tokens_to_text(table.pack(table.unpack(inlines, i, i+len))), removes the old tokens with for j = math.min(i+len, #inlines), i, ‐1 do inlines:remove(j) end, and builds a replacement.

If string.find(text, "‘" .. BINARY .. "[ˆ‘]*‘") finds a (sub)command reference, it makes a pandoc.Link with sub = string.sub(text, s+1, e‐1) and target subcommand_to_link(sub). Otherwise it finds "‘[ˆ‘]*‘" and makes pandoc.Code(string.sub(text, s+1, e‐1)).

Finally it inserts prefix, replacement, and suffix:

inlines:insert(i+0, pandoc.Str(string.sub(text, 1, s‐1)))
inlines:insert(i+1, replacement)
inlines:insert(i+2, pandoc.Str(string.sub(text, e+1)))

Then i = i + 1. Iconic. ✨

Inlines (inlines)

Runs the trio:

rewrite_synopsis(inlines)
rewrite_see_also(inlines)
rewrite_inline_code(inlines)
return inlines

Str (s)

Turns implicit links into actual links. It sets s = s.text, then a, b = string.find(s, "<https://[ˆ>]+>"). If a and b are truthy, it grabs target = string.sub(s, a+1, b‐1) and returns:

pandoc.Inlines {
   pandoc.Str(string.sub(s, 0, a)),
   pandoc.Link(target, target),
   pandoc.Str(string.sub(s, b)),
}

Note the string.sub(s, 0, a) — that’s exactly what the original says, bestie. No edits. 🫶

FULL FILTER CODE (EXACT, NO EDITS) 💾

‐‐  Tweaks  our manual pages during conversion to html by pandoc.
‐‐ ‐‐ See  https://pandoc.org/lua‐filters.html#debugging‐lua‐fil‐
ters on ‐‐ how to debug and develop these filters.

‐‐ Note: you must set the environment variable BINARY to the name
of  ‐‐  the  primary executable, e.g., ’sq’ or ‘sq‐git‘.  BINARY =
os.getenv("BINARY") if BINARY == nil or BINARY == "" then
  error("BINARY environment variable should be  set  to  the  bi‐
nary’s name, e.g., sq") end

‐‐  Overwrites  the  title.   ‐‐ ‐‐ It just says ’sq‘ which looks
odd.  function Meta(m)
   m.title = "Sequoia PGP Manual Pages"
   return m end

‐‐ Turns subcommand headers into links.  function Header (h)
   ‐‐ Given a sequence of  subcommands,  return  the  appropriate
file
   ‐‐ name.
   local function subcommand_to_link (s)
      t = ""
      for i = 1, #s do
         if s[i].t == ’Space’ then
            t = t .. ’‐’
         else
            t = t ..  s[i].text
         end
      end
      return t .. ".1.html"
   end

   if h.c[1].t == ’Str’ and h.c[1].text == BINARY then
      h.c = pandoc.Link(h.c, subcommand_to_link(h.c))
      return h
   end end

‐‐  Transform  the  entries in the "SYNOPSIS" section into links.
local function rewrite_synopsis (inlines)
   ‐‐ Returns true if we’re looking at a synopsis entry.
   ‐‐
   ‐‐ Matches on the following sequence:
   ‐‐
   ‐‐      [1] LineBreak {}
   ‐‐      [2] Strong {
   ‐‐        content: Inlines[7] {
   ‐‐          [1] Str "sq"
   ‐‐          [2] Space
   ‐‐          [3] Str "key"
   ‐‐          [4] Space
   ‐‐          [5] Str "userid"
   ‐‐          [6] Space
   ‐‐          [7] Str "add"
   ‐‐        }
   ‐‐      }
   ‐‐      [3] Space
   ‐‐      [4] Str "["
   local function synopsis_p (s)
      return s[1] and s[1].t == ’LineBreak’
         and s[2] and s[2].t == ’Strong’
         and s[2].c[1].text  ==  BINARY  and  (not  s[2].c[2]  or
s[2].c[2].t == ’Space’)
         and s[3] and s[3].t == ’Space’
         and s[4] and s[4].t == ’Str’ and s[4].text == ’[’
   end

   ‐‐  Given  a  sequence  of subcommands, return the appropriate
file
   ‐‐ name.
   local function subcommand_to_link (s)
      t = ""
      for i = 1, #s do
         if s[i].t == ’Space’ then
            t = t .. ’‐’
         else
            t = t ..  s[i].text
         end
      end
      return t .. ".1.html"
   end

   for i = 1, #inlines‐4 do
      if synopsis_p(table.pack(table.unpack(inlines, i, i +  3)))
then
         inlines[i+1]    =    pandoc.Link(inlines[i+1],   subcom‐
mand_to_link(inlines[i+1].c))
      end
   end end

‐‐ Transforms the entries in the "SEE ALSO" section  into  links.
local function rewrite_see_also (inlines)
   ‐‐ Returns true if we’re looking at a see‐also entry.
   ‐‐
   ‐‐ Matches on the following sequence:
   ‐‐
   ‐‐      [43] Strong {
   ‐‐        content: Inlines[1] {
   ‐‐          [1] Str "sq‐wkd"
   ‐‐        }
   ‐‐      }
   ‐‐      [44] Str "(1),"
   local function see_also_p (a, b)
      return a and a.t == ’Strong’
         and  b  and b.t == ’Str’ and string.sub(b.text, 1, 3) ==
’(1)’
   end

   for i = 1, #inlines‐1 do
      if see_also_p(inlines[i], inlines[i+1]) then
         inlines[i]      =      pandoc.Link(inlines[i],       in‐
lines[i].c[1].text .. ".1.html")
      end
   end end

‐‐  Transforms  text  enclosed  in  backticks into inline code or
links.  local function rewrite_inline_code (inlines)
   ‐‐ Returns the length of the token  stream  containing  quoted
text
   ‐‐ starting from the first token, or nil if no quoted text is
   ‐‐ found.
   local function inline_quoted (s)
      if s[1] and s[1].t == ’Str’ and string.sub(s[1].text, 1, 1)
== "‘" then
         ‐‐  Find  the  end,  i.e. the first token containing the
closing
         ‐‐ tick (which may be the first token).
         for i = 1, #s do
            if    s[i]    and     s[i].t     ==     ’Str’     and
string.find(s[i].text, "‘", 2) then
               return i
            end
         end
      end
   end

   ‐‐  Given  a  sequence of tokens, returns the text representa‐
tion.
   local function tokens_to_text (s)
      t = ""
      for i = 1, #s do
         if s[i].t == ’Space’ then
            t = t .. ’ ’
         else
            t = t ..  s[i].text
         end
      end
      return t
   end

   ‐‐ Given a string subcommand, return the appropriate file
   ‐‐ name.
   local function subcommand_to_link (s)
      return string.gsub(s, " ", "‐") .. ".1.html"
   end

   ‐‐ Note: as we mutate ‘inlines‘ here, we use a while loop to
   ‐‐ iterate over it.  Using a for loop doesn’t cut it as  ‘#in‐
lines‘
   ‐‐  is then evaluated only once when entering the loop for the
first
   ‐‐ time.
   local i = 1
   while i <= #inlines do
      local len =  inline_quoted(table.pack(table.unpack(inlines,
i)))
      if len then
         ‐‐ First, flatten the matched tokens to a text.
         local  text = tokens_to_text(table.pack(table.unpack(in‐
lines, i, i+len)))

         ‐‐ Then, remove the tokens from the AST.
         for j = math.min(i+len, #inlines), i, ‐1 do
            inlines:remove(j)
         end

         ‐‐ We need to create a replacement node.
         local replacement

         ‐‐ See if the quoted text is a reference to a  (sub)com‐
mand.
         local   s,  e  =  string.find(text,  "‘"  ..  BINARY  ..
"[ˆ‘]*‘")
         if s then
            ‐‐ If so, we turn it into a link.
            local sub = string.sub(text, s+1, e‐1)
            replacement      =      pandoc.Link(sub,      subcom‐
mand_to_link(sub))
         else
            s, e = string.find(text, "‘[ˆ‘]*‘")
            replacement = pandoc.Code(string.sub(text, s+1, e‐1))
         end

         ‐‐  Finally, insert prefix, replacement, and suffix into
the
         ‐‐ AST.
         inlines:insert(i+0,    pandoc.Str(string.sub(text,    1,
s‐1)))
         inlines:insert(i+1, replacement)
         inlines:insert(i+2, pandoc.Str(string.sub(text, e+1)))
      end

      i = i + 1
   end end

function Inlines (inlines)
   rewrite_synopsis(inlines)
   rewrite_see_also(inlines)
   rewrite_inline_code(inlines)
   return inlines end

‐‐ Turns implicit links into actual links.  function Str (s)
   s = s.text
   a, b = string.find(s, "<https://[ˆ>]+>")
   if a and b then
      target = string.sub(s, a+1, b‐1)
      return pandoc.Inlines {
         pandoc.Str(string.sub(s, 0, a)),
         pandoc.Link(target, target),
         pandoc.Str(string.sub(s, b)),
      }
   end end

💖 Cute sign-off: the original snippet didn’t include a footer line with source/version and date, so I’m not inventing one, bestie. If it had one, it would go right here with a little bow. 🎀

📦 where this page comes from