[PD] A lua script to print comments from help patches

Martin Peach martin.peach at sympatico.ca
Thu Oct 11 20:19:03 CEST 2012


Here is a script that will print out all the comment lines it finds in 
each file that ends in "-help.pd" in the current directory or the path 
given to it as an argument, like:
./list_pd_help_text.lua /usr/lib/pd-extended > list.txt

The resulting list.txt can then be searched for keywords.

Comments are printed in top-to-bottom order for the main patch and for 
each subpatch, but they still might not make sense out of context; this 
is more useful for finding appropriate objects using keywords.

I've tested it on linux and WinXp (cygwin). I used luarocks to install 
the lfs module. On WinXp I used lfs from
http://sourceware.org/cygwinports/

While it could probably be implemented as a pd_lua object it might hang 
Pd if it were to print everything to the console.

Martin
-------------- next part --------------
#! /usr/bin/env lua
-- A script to print out all the comments from each Pd help file found under
-- the path passed as the first argument, defaults to the directory from which the script is run.
-- Author Martin Peach 20121011

require"lfs"
path = "." -- default search path
totalFiles = 0

function attrdir (path)
  local file ourPdHelpFile = nil

--  print ("Searching " .. path)
  for file in lfs.dir(path) do
    if file ~= "." and file ~= ".." then
      local f
--      if path:find("/", -1) then f = path..file
--      else f = path..'/'..file end
      while path:find("/", -1) do path = path:sub(1,-2) end -- strip any trailing slash(es) from path
      f = path..'/'..file
      --print ("\t "..f)
      local attr = lfs.attributes (f)
      if (type(attr) ~= "table") then break end
      assert (type(attr) == "table", f)
      if attr.mode == "directory" then
        attrdir (f)
      else
        local ss, s, e, x, y
        local objName

        s, e = file:find("-help.pd")
        if e == file:len() then -- a filename ending in "-help.pd"
          totalFiles = totalFiles + 1
          objName = file:sub(1,s-1)
          print (totalFiles .. ": [" .. objName .. "]" .. " at " .. f)
          ourPdHelpFile = io.open(f)
          if ourPdHelpFile == nil then
            print ("Can't open " .. f)
          else
-- an example comment from a pd patcher:
--  #X text 86 306 This example measure the time it takes for two different
--  methods to count from 0 to 999;
            local canvasDepth = 0
            --local maxID = 0
            local canvasName = {}
            local canvasFrom = {}
            local canvasID = 0
            local canvasNow = 0
            local commentLines = {}
            local sortedLines = {}

            repeat
              ourLine = ourPdHelpFile:read()
              if ourLine ~= nil then
                if ourLine:find("^#N canvas") then -- line starts with "#N canvas"
                  canvasDepth = canvasDepth + 1
                  canvasID = canvasID + 1
                  canvasFrom[canvasID] = canvasNow
                  canvasNow = canvasID 
                  s, e = ourLine:find("%a", 10) -- first text after "canvas" will be canvas name
                  if s then
                    ss, e = ourLine:find(" ", e) -- first space after canvas name
                   if not ss then
                      ourLine = ourLine:sub(s) -- name extends to end of line
                    else
                      ourLine = ourLine:sub(s, ss-1) -- name followed by number
                    end
                    canvasName[canvasID] = ourLine
                  else
                    canvasName[canvasID] = " "
                  end
                  --if canvasID > maxID then maxID = canvasID end
--print("a->canvasDepth " .. canvasDepth .. " canvasID " .. canvasID .. " canvasFrom " .. canvasFrom[canvasID] .. " canvasNow " .. canvasNow .. " maxID " .. maxID)
                elseif ourLine:find("^#X restore") then
                  canvasDepth = canvasDepth - 1
                  canvasNow = canvasFrom[canvasNow]
--print("b->canvasDepth " .. canvasDepth .. " canvasID " .. canvasID .. " canvasFrom " .. canvasFrom[canvasID] .. " canvasNow " .. canvasNow .. " maxID " .. maxID)
                else
--print("c->canvasDepth " .. canvasDepth .. " canvasID " .. canvasID .. " canvasFrom " .. canvasFrom[canvasID] .. " canvasNow " .. canvasNow .. " maxID " .. maxID)
                  s, e = ourLine:find("^#X text")
-- Comment begins with #X text:
                  if s == 1 then
                    ourLine = ourLine:sub(e+1)
-- First number is horizontal position of top left corner of comment box:
                    s, e = ourLine:find("%d+") 
                    x = ourLine:sub(s,e)
                    ourLine = ourLine:sub(e+1)
-- First number is vertical position of top left corner of comment box:
                    s, e = ourLine:find("%d+") 
                    y = ourLine:sub(s,e)
-- Remaining line is comment text:
                    ourLine = ourLine:sub(e+1)
                    ourLine = canvasNow .. ourLine
-- End of comment must be semicolon:
                    if not ourLine:find(";") then
                      repeat
                        moreLine = ourPdHelpFile:read()
                        if moreLine then ourLine = ourLine .. "\n    " .. moreLine end
                      until ((moreLine == nil) or (moreLine:find(";")))   
                    end -- if not ourLine:find(";$") then
-- prepare to sort the lines in order of y (They are usually not in order in the patcher file)
                    ourLine = ourLine:gsub(" \\,", ",") --  de-escape commas (look for space:backslash:comma)
                    ourLine = ourLine:gsub(" \\;", ";") --  de-escape semicolons
                    ourLine = ourLine:gsub("\\", "") --  remove any remaining backslashes
                    ourLine = ourLine:sub(1, -2) -- strip trailing semicolon 
                    commentLines[y] = ourLine -- store comments using their y position as key
                    --print ("<" .. x .. "> <" .. y .. "> ".. ourLine)
                  end -- if s == 1 then
                end -- if ourLine:find("^#N canvas") then
              end -- if ourLine ~= nil then

            until ourLine == nil
            --for x,line in pairs(commentLines) do
            --  print ("> " .. x .. " " ..line)
            --end
            --print("maxID " .. maxID)

-- now we sort the lines in order of y
            local i = 1
            for y, line in pairsByKeys(commentLines, function(a,b) return tonumber(a)<tonumber(b) end) do
              --print("*" .. y .. " " .. line)
              sortedLines[i] = line
              i = i+1
            end
-- now we print them out in order of canvasID
            --for patchID = 1, maxID do
            for patchID = 1, canvasID do
              print ("  {" .. patchID .. ": " .. canvasName[patchID] .. "}")
              for i = 1, #sortedLines do
                line = sortedLines[i]
                --print("*" .. line)
                s, e = line:find("^%d+") -- retrieve patchID from the start of the comment string
                if s == 1 then -- line begins with a number string
                  local n = tonumber(line:sub(s,e)) -- , which should be the patchID
                  if n == patchID then print("   " .. line:sub(e+1)) end -- if it's this patchID strip ID and print it
                end
              end
            end
            print (" ")
--            print ("  " .. canvasID-1 .. " subpatches in [" .. objName .. "]\n")
            commentLines = {}
          end -- if ourPdHelpFile == nil then
        end -- if e == file:len() then
      end -- if attr.mode == "directory" then
    end -- if file ~= "." and file ~= ".." then
  end --   for file in lfs.dir(path) do
  --print (" ")
end

function pairsByKeys (t, f)
  local a = {}
  for n in pairs(t) do
    table.insert(a, n) -- put the keys in table a
  end
  table.sort(a, f) -- sort the keys using f()
  local i = 0 -- iterator variable
  local iter = function () -- iterator function
    i = i + 1 -- step through a in sorted order
    if a[i] == nil then
      return nil
    else
      return a[i], t[a[i]] -- return the key and its value from t
    end
  end
  return iter
end

if arg[1] then path = arg[1] end
print ("Comments from all Pure Data help patchers found under " .. path .. ":")
attrdir(path)
print ("End of listing for " .. path .. ".")
print ("Total help files found: " .. totalFiles .. ".")



More information about the Pd-list mailing list