main.lua 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  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. table.sort(object.methods, function(a, b) return a.key < b.key end)
  147. return object
  148. end
  149. local function processModule(path)
  150. local module = require(path)
  151. module.key = module.external and path:match('[^/]+$') or path:gsub('/', '.')
  152. module.name = module.external and module.key or module.key:match('[^%.]+$')
  153. module.description = unwrap(module.description)
  154. module.functions = {}
  155. module.objects = {}
  156. module.enums = {}
  157. module.notes = unwrap(module.notes)
  158. if module.sections then
  159. for _, section in ipairs(module.sections) do
  160. section.description = unwrap(section.description)
  161. end
  162. end
  163. module.examples = pluralify(module, 'example')
  164. for k, example in ipairs(module.examples or {}) do
  165. module.examples[k] = processExample(example)
  166. end
  167. for _, file in ipairs(lovr.filesystem.getDirectoryItems(path)) do
  168. local childPath = path .. '/' .. file
  169. local childModule = childPath:gsub('%..+$', '')
  170. local isFile = lovr.filesystem.isFile(childPath)
  171. local capitalized = file:match('^[A-Z]')
  172. if file ~= 'init.lua' and not capitalized and isFile then
  173. table.insert(module.functions, processFunction(childModule, module))
  174. elseif capitalized and not isFile then
  175. table.insert(module.objects, processObject(childModule, module))
  176. elseif capitalized and isFile then
  177. table.insert(module.enums, processEnum(childModule, module))
  178. end
  179. end
  180. table.sort(module.functions, function(a, b) return a.key < b.key end)
  181. table.sort(module.objects, function(a, b) return a.key < b.key end)
  182. table.sort(module.enums, function(a, b) return a.key < b.key end)
  183. return module
  184. end
  185. function lovr.load()
  186. local api = {
  187. modules = {},
  188. callbacks = {}
  189. }
  190. -- Modules
  191. table.insert(api.modules, processModule('lovr'))
  192. for _, file in ipairs(lovr.filesystem.getDirectoryItems('lovr')) do
  193. local path = 'lovr/' .. file
  194. if file ~= 'callbacks' and file:match('^[a-z]') and lovr.filesystem.isDirectory(path) then
  195. table.insert(api.modules, processModule(path))
  196. end
  197. end
  198. -- Callbacks
  199. local callbacks = 'lovr/callbacks'
  200. for _, file in ipairs(lovr.filesystem.getDirectoryItems(callbacks)) do
  201. table.insert(api.callbacks, processFunction(callbacks .. '/' .. file:gsub('%.lua', ''), api.modules[1]))
  202. end
  203. -- Sort
  204. table.sort(api.modules, function(a, b) return a.key < b.key end)
  205. table.sort(api.callbacks, function(a, b) return a.key < b.key end)
  206. -- Serialize
  207. local file = io.open(lovr.filesystem.getSource() .. '/init.lua', 'w')
  208. assert(file, 'Could not open init.lua for writing')
  209. local keyPriority = {
  210. name = 1,
  211. tag = 2,
  212. summary = 3,
  213. type = 4,
  214. description = 5,
  215. key = 6,
  216. module = 7,
  217. arguments = 8,
  218. returns = 9
  219. }
  220. local function sort(keys, t)
  221. table.sort(keys, function(a, b) return (keyPriority[a] or 1000) < (keyPriority[b] or 1000) end)
  222. end
  223. local contents = 'return ' .. serpent.block(api, { comment = false, sortkeys = sort })
  224. file:write(contents)
  225. file:close()
  226. -- Bye
  227. lovr.event.quit()
  228. end