main.lua 7.0 KB

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