main.lua 7.0 KB

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