main.lua 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. local serpent = require 'serpent'
  2. -- Make sure init.lua can be required
  3. if not package.path:match('%./?/init%.lua') then
  4. package.path = package.path .. ';./?/init.lua'
  5. end
  6. -- Helpers
  7. local function copy(t)
  8. if type(t) ~= 'table' then return t end
  9. local result = {}
  10. for k, v in pairs(t) do
  11. result[k] = copy(v)
  12. end
  13. return result
  14. end
  15. local function unindent(code)
  16. local indent = code:match('^(% +)')
  17. if indent then
  18. return code:gsub('\n' .. indent, '\n'):gsub('^' .. indent, ''):gsub('%s*$', '')
  19. else
  20. return code
  21. end
  22. end
  23. local function unwrap(str)
  24. if not str then return str end
  25. str = unindent(str)
  26. return str:gsub('([^\n])\n(%S)', function(a, b)
  27. if b == '-' or b == '>' then
  28. return a .. '\n' .. b
  29. else
  30. return a .. ' ' .. b
  31. end
  32. end)
  33. :gsub('^%s+', '')
  34. :gsub('%s+$', '')
  35. end
  36. local function pluralify(t, key)
  37. t[key .. 's'] = t[key .. 's'] or (t[key] and { t[key] } or nil)
  38. t[key] = nil
  39. return t[key .. 's']
  40. end
  41. -- Processors
  42. local function processExample(example)
  43. if type(example) == 'string' then
  44. return {
  45. code = unindent(example)
  46. }
  47. else
  48. example.description = unwrap(example.description)
  49. example.code = unindent(example.code)
  50. end
  51. return example
  52. end
  53. local function processEnum(path, parent)
  54. local enum = require(path)
  55. enum.name = path:match('[^/]+$')
  56. enum.key = enum.name
  57. enum.module = parent.name
  58. enum.description = unwrap(enum.description)
  59. enum.notes = unwrap(enum.notes)
  60. for _, value in ipairs(enum.values) do
  61. value.description = unwrap(value.description)
  62. end
  63. return enum
  64. end
  65. local function processFunction(path, parent)
  66. local fn = require(path)
  67. fn.name = path:match('[^/]+$')
  68. fn.key = parent.name:match('^[A-Z]') and (parent.key .. ':' .. fn.name) or (path:gsub('/', '.'):gsub('callbacks%.', ''))
  69. fn.description = unwrap(fn.description)
  70. fn.module = parent.module or parent.key
  71. fn.notes = unwrap(fn.notes)
  72. fn.examples = pluralify(fn, 'example')
  73. for k, example in ipairs(fn.examples or {}) do
  74. fn.examples[k] = processExample(example)
  75. end
  76. if not fn.variants then
  77. fn.variants = {
  78. {
  79. arguments = fn.arguments,
  80. returns = fn.returns
  81. }
  82. }
  83. else
  84. for name, arg in pairs(fn.arguments) do
  85. arg.name = name
  86. end
  87. for name, ret in pairs(fn.returns) do
  88. ret.name = name
  89. end
  90. for _, variant in ipairs(fn.variants) do
  91. for i, name in ipairs(variant.arguments) do
  92. variant.arguments[i] = copy(fn.arguments[name])
  93. end
  94. for i, name in ipairs(variant.returns) do
  95. variant.returns[i] = copy(fn.returns[name])
  96. end
  97. end
  98. end
  99. for _, variant in ipairs(fn.variants) do
  100. local function processTable(t)
  101. if not t then return end
  102. for _, field in ipairs(t) do
  103. field.description = unwrap(field.description)
  104. end
  105. t.description = unwrap(t.description)
  106. processTable(t.table)
  107. end
  108. variant.description = unwrap(variant.description)
  109. for _, arg in ipairs(variant.arguments) do
  110. arg.description = unwrap(arg.description)
  111. processTable(arg.table)
  112. end
  113. for _, ret in ipairs(variant.returns) do
  114. ret.description = unwrap(ret.description)
  115. processTable(ret.table)
  116. end
  117. end
  118. fn.arguments = nil
  119. fn.returns = nil
  120. return fn
  121. end
  122. local function processObject(path, parent)
  123. local object = require(path)
  124. object.key = path:match('[^/]+$')
  125. object.name = object.key
  126. object.description = unwrap(object.description)
  127. object.summary = object.summary or object.description
  128. object.module = parent.key
  129. object.methods = {}
  130. object.constructors = pluralify(object, 'constructor')
  131. object.notes = unwrap(object.notes)
  132. object.examples = pluralify(object, 'example')
  133. if object.sections then
  134. for _, section in ipairs(object.sections) do
  135. section.description = unwrap(section.description)
  136. end
  137. end
  138. for k, example in ipairs(object.examples or {}) do
  139. object.examples[k] = processExample(example)
  140. end
  141. for _, file in ipairs(lovr.filesystem.getDirectoryItems(path)) do
  142. if file ~= 'init.lua' then
  143. table.insert(object.methods, processFunction(path .. '/' .. file:gsub('%..+$', ''), object))
  144. end
  145. end
  146. return object
  147. end
  148. local function processModule(path)
  149. local module = require(path)
  150. module.key = module.external and path:match('[^/]+$') or path:gsub('/', '.')
  151. module.name = module.external and module.key or module.key:match('[^%.]+$')
  152. module.description = unwrap(module.description)
  153. module.functions = {}
  154. module.objects = {}
  155. module.enums = {}
  156. module.notes = unwrap(module.notes)
  157. if module.sections then
  158. for _, section in ipairs(module.sections) do
  159. section.description = unwrap(section.description)
  160. end
  161. end
  162. module.examples = pluralify(module, 'example')
  163. for k, example in ipairs(module.examples or {}) do
  164. module.examples[k] = processExample(example)
  165. end
  166. for _, file in ipairs(lovr.filesystem.getDirectoryItems(path)) do
  167. local childPath = path .. '/' .. file
  168. local childModule = childPath:gsub('%..+$', '')
  169. local isFile = lovr.filesystem.isFile(childPath)
  170. local capitalized = file:match('^[A-Z]')
  171. if file ~= 'init.lua' and not capitalized and isFile then
  172. table.insert(module.functions, processFunction(childModule, module))
  173. elseif capitalized and not isFile then
  174. table.insert(module.objects, processObject(childModule, module))
  175. elseif capitalized and isFile then
  176. table.insert(module.enums, processEnum(childModule, module))
  177. end
  178. end
  179. return module
  180. end
  181. function lovr.load()
  182. local api = {
  183. modules = {},
  184. callbacks = {}
  185. }
  186. -- Modules
  187. table.insert(api.modules, processModule('lovr'))
  188. for _, file in ipairs(lovr.filesystem.getDirectoryItems('lovr')) do
  189. local path = 'lovr/' .. file
  190. if file ~= 'callbacks' and file:match('^[a-z]') and lovr.filesystem.isDirectory(path) then
  191. table.insert(api.modules, processModule(path))
  192. end
  193. end
  194. -- Callbacks
  195. local callbacks = 'lovr/callbacks'
  196. for _, file in ipairs(lovr.filesystem.getDirectoryItems(callbacks)) do
  197. table.insert(api.callbacks, processFunction(callbacks .. '/' .. file:gsub('%.lua', ''), api.modules[1]))
  198. end
  199. -- Serialize
  200. local file = io.open(lovr.filesystem.getSource() .. '/init.lua', 'w')
  201. assert(file, 'Could not open init.lua for writing')
  202. local keyPriority = {
  203. name = 1,
  204. tag = 2,
  205. summary = 3,
  206. type = 4,
  207. description = 5,
  208. key = 6,
  209. module = 7,
  210. arguments = 8,
  211. returns = 9
  212. }
  213. local function sort(keys, t)
  214. table.sort(keys, function(a, b) return (keyPriority[a] or 1000) < (keyPriority[b] or 1000) end)
  215. end
  216. local contents = 'return ' .. serpent.block(api, { comment = false, sortkeys = sort })
  217. file:write(contents)
  218. file:close()
  219. -- Bye
  220. lovr.event.quit()
  221. end