typescript.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. -- done:
  2. --
  3. -- - callbacks
  4. -- - module functions
  5. -- - object methods
  6. -- - rest parameters
  7. -- - uses of the name 'default'
  8. -- - uses of the name '*'
  9. -- - uses of the type '*'
  10. -- - rest returns
  11. -- - interface inheritance
  12. -- - arguments with defaults are optional
  13. -- - vector operators
  14. -- - vec3.up etc
  15. -- - table arguments with specified fields
  16. -- - fn variant documentation
  17. -- - type and interface documentation
  18. --
  19. -- todo:
  20. --
  21. -- - show call example in documentation (e.g. `const [x, y, z] = collider.getPosition()`)
  22. -- - lovr.graphics.getDevice() more specific return value and documentation
  23. -- (available in .table field of return value)
  24. -- - global vector constructors documentation
  25. -- - LuaTable specializations (e.g. arrays)
  26. -- - enet
  27. -- - http
  28. --
  29. -- some recommended changes to lovr-docs i found while working on this:
  30. --
  31. -- - mark draw return value as optional somehow?
  32. -- - make pass.setColor() explicitly accept vec3/vec4
  33. -- todo: the global constructors should be automated in order to include documentation
  34. local vector_ops = [[
  35. /** @noSelf **/ declare function vec2(x?: number, y?: number): Vec2
  36. /** @noSelf **/ declare function vec2(u: Vec2): Vec2
  37. /** @noSelf **/ declare function Vec2(x?: number, y?: number): Vec2
  38. /** @noSelf **/ declare function Vec2(u: Vec2): Vec2
  39. declare namespace vec2 {
  40. const zero: Vec2
  41. const one: Vec2
  42. }
  43. /** @noSelf **/ declare function vec3(x?: number, y?: number, z?: number): Vec3
  44. /** @noSelf **/ declare function vec3(u: Vec3): Vec3
  45. /** @noSelf **/ declare function vec3(m: Mat4): Vec3
  46. /** @noSelf **/ declare function vec3(q: Quat): Vec3
  47. /** @noSelf **/ declare function Vec3(x?: number, y?: number, z?: number): Vec3
  48. /** @noSelf **/ declare function Vec3(u: Vec3): Vec3
  49. /** @noSelf **/ declare function Vec3(m: Mat4): Vec3
  50. /** @noSelf **/ declare function Vec3(q: Quat): Vec3
  51. declare namespace vec3 {
  52. const zero: Vec3
  53. const one: Vec3
  54. const left: Vec3
  55. const right: Vec3
  56. const up: Vec3
  57. const down: Vec3
  58. const back: Vec3
  59. const forward: Vec3
  60. }
  61. /** @noSelf **/ declare function vec4(x?: number, y?: number, z?: number, w?: number): Vec4
  62. /** @noSelf **/ declare function vec4(u: Vec4): Vec4
  63. /** @noSelf **/ declare function Vec4(x?: number, y?: number, z?: number, w?: number): Vec4
  64. /** @noSelf **/ declare function Vec4(u: Vec4): Vec4
  65. declare namespace vec4 {
  66. const zero: Vec4
  67. const one: Vec4
  68. }
  69. /** @noSelf **/ declare function quat(angle?: number, ax?: number, ay?: number, az?: number, raw?: boolean): Quat
  70. /** @noSelf **/ declare function quat(r: Quat): Quat
  71. /** @noSelf **/ declare function quat(v: Vec3): Quat
  72. /** @noSelf **/ declare function quat(v: Vec3, u: Vec3): Quat
  73. /** @noSelf **/ declare function quat(m: Mat4): Quat
  74. /** @noSelf **/ declare function quat(): Quat
  75. /** @noSelf **/ declare function Quat(angle?: number, ax?: number, ay?: number, az?: number, raw?: boolean): Quat
  76. /** @noSelf **/ declare function Quat(r: Quat): Quat
  77. /** @noSelf **/ declare function Quat(v: Vec3): Quat
  78. /** @noSelf **/ declare function Quat(v: Vec3, u: Vec3): Quat
  79. /** @noSelf **/ declare function Quat(m: Mat4): Quat
  80. /** @noSelf **/ declare function Quat(): Quat
  81. declare namespace quat {
  82. const identity: Quat
  83. }
  84. /** @noSelf **/ declare function mat4(): Mat4
  85. /** @noSelf **/ declare function mat4(n: Mat4): Mat4
  86. /** @noSelf **/ declare function mat4(position?: Vec3, scale?: Vec3, rotation?: Quat): Mat4
  87. /** @noSelf **/ declare function mat4(position?: Vec3, rotation?: Quat): Mat4
  88. /** @noSelf **/ declare function mat4(...rest: number[]): Mat4
  89. /** @noSelf **/ declare function mat4(d: number): Mat4
  90. /** @noSelf **/ declare function Mat4(): Mat4
  91. /** @noSelf **/ declare function Mat4(n: Mat4): Mat4
  92. /** @noSelf **/ declare function Mat4(position?: Vec3, scale?: Vec3, rotation?: Quat): Mat4
  93. /** @noSelf **/ declare function Mat4(position?: Vec3, rotation?: Quat): Mat4
  94. /** @noSelf **/ declare function Mat4(...rest: number[]): Mat4
  95. /** @noSelf **/ declare function Mat4(d: number): Mat4
  96. declare interface Vec2 {
  97. add_temp: LuaAdditionMethod<Vec2, Vec2>
  98. sub_temp: LuaSubtractionMethod<Vec2, Vec2>
  99. mul_temp: LuaMultiplicationMethod<Vec2 | number, Vec2>
  100. div_temp: LuaDivisionMethod<Vec2 | number, Vec2>
  101. 1: number
  102. 2: number
  103. x: number
  104. y: number
  105. r: number
  106. g: number
  107. s: number
  108. t: number
  109. }
  110. declare interface Vec3 {
  111. add_temp: LuaAdditionMethod<Vec3, Vec3>
  112. sub_temp: LuaSubtractionMethod<Vec3, Vec3>
  113. mul_temp: LuaMultiplicationMethod<Vec3 | number, Vec3>
  114. div_temp: LuaDivisionMethod<Vec3 | number, Vec3>
  115. 1: number
  116. 2: number
  117. 3: number
  118. x: number
  119. y: number
  120. z: number
  121. r: number
  122. g: number
  123. b: number
  124. s: number
  125. t: number
  126. p: number
  127. }
  128. declare interface Vec4 {
  129. add_temp: LuaAdditionMethod<Vec4, Vec4>
  130. sub_temp: LuaSubtractionMethod<Vec4, Vec4>
  131. mul_temp: LuaMultiplicationMethod<Vec4 | number, Vec4>
  132. div_temp: LuaDivisionMethod<Vec4 | number, Vec4>
  133. 1: number
  134. 2: number
  135. 3: number
  136. 4: number
  137. x: number
  138. y: number
  139. z: number
  140. w: number
  141. r: number
  142. g: number
  143. b: number
  144. a: number
  145. s: number
  146. t: number
  147. p: number
  148. q: number
  149. }
  150. declare interface Quat {
  151. add_temp: LuaAdditionMethod<Quat, Quat>
  152. sub_temp: LuaSubtractionMethod<Quat, Quat>
  153. mul_temp: LuaMultiplicationMethod<Quat, Quat> & LuaMultiplicationMethod<Vec3, Vec3>
  154. div_temp: LuaDivisionMethod<Quat, Quat>
  155. 1: number
  156. 2: number
  157. 3: number
  158. 4: number
  159. x: number
  160. y: number
  161. z: number
  162. w: number
  163. }
  164. declare interface Mat4 {
  165. add_temp: LuaAdditionMethod<Mat4, Mat4>
  166. sub_temp: LuaSubtractionMethod<Mat4, Mat4>
  167. mul_temp: LuaMultiplicationMethod<Mat4 | number, Mat4> & LuaMultiplicationMethod<Vec3, Vec3> & LuaMultiplicationMethod<Vec4, Vec4>
  168. div_temp: LuaDivisionMethod<Mat4 | number, Mat4>
  169. 1: number
  170. 2: number
  171. 3: number
  172. 4: number
  173. 5: number
  174. 6: number
  175. 7: number
  176. 8: number
  177. 9: number
  178. 10: number
  179. 11: number
  180. 12: number
  181. 13: number
  182. 14: number
  183. 15: number
  184. 16: number
  185. }
  186. ]]
  187. return function (api)
  188. local path = lovr.filesystem.getSource() .. '/typescript'
  189. if lovr.system.getOS() == 'Windows' then
  190. os.execute('mkdir ' .. path:gsub('/', '\\'))
  191. else
  192. os.execute('mkdir -p ' .. path)
  193. end
  194. local out = io.open(path .. '/lovr-api.d.ts', 'w')
  195. assert(out)
  196. local function put (...)
  197. out:write(...)
  198. end
  199. local indentation = ''
  200. local function indent()
  201. indentation = (' '):rep(#indentation + 2)
  202. end
  203. local function unindent()
  204. indentation = (' '):rep(#indentation - 2)
  205. end
  206. local function quote_string (str)
  207. return "'" .. str:gsub('\\', '\\\\'):gsub("'", "\\'") .. "'"
  208. end
  209. local function put_doc(doc)
  210. if doc:match('\n') then
  211. put(indentation, '/**\n',
  212. indentation, ' * ', doc:gsub('\n', '\n' .. indentation .. ' * '), '\n',
  213. indentation, ' */\n')
  214. else
  215. put(indentation, '/** ', doc, ' */\n')
  216. end
  217. end
  218. -- todo: more specific types
  219. local type_map = {
  220. table = 'LuaTable',
  221. userdata = 'any',
  222. lightuserdata = 'any',
  223. ['function'] = '(...args: any[]) => any',
  224. ['*'] = 'any',
  225. ['Object'] = 'LovrObject'
  226. }
  227. local convert_type
  228. local function convert_table (table_fields)
  229. local result = '{ '
  230. for _, field in ipairs (table_fields) do
  231. local optional = field.default and '?' or ''
  232. result = result .. field.name .. optional .. ': ' .. convert_type(field.type, field.table) .. ', '
  233. end
  234. return result .. '}'
  235. end
  236. function convert_type (t, table_fields)
  237. if t == 'table' and table_fields then
  238. return convert_table (table_fields)
  239. else
  240. return type_map[t] or t
  241. end
  242. end
  243. local name_map = {
  244. ['*'] = 'rest',
  245. }
  246. local function convert_name (n)
  247. return name_map[n] or n
  248. end
  249. local function convert_param (arg, optional)
  250. local name = convert_name(arg.name)
  251. local t = convert_type(arg.type, arg.table)
  252. local pattern = '%.%.%.'
  253. if name:match(pattern) then
  254. name = '...' .. name:gsub(pattern, '')
  255. if name == '...' then
  256. name = name .. 'rest'
  257. end
  258. t = t .. '[]'
  259. end
  260. return name .. (optional and '?' or '') .. ': ' .. t
  261. end
  262. local function convert_return_values (returns)
  263. local ret = 'void'
  264. if #returns == 1 and (not returns[1].name:match('%.%.%.')) then
  265. ret = convert_type(returns[1].type)
  266. elseif #returns >= 1 then
  267. ret = 'LuaMultiReturn<['
  268. for n, ret_n in ipairs (returns) do
  269. local name = convert_name(ret_n.name)
  270. local t = convert_type(ret_n.type)
  271. if name:match('%.%.%.') then
  272. if name == '...' then
  273. name = '...rest'
  274. end
  275. t = t .. '[]'
  276. end
  277. ret = ret .. name .. ': ' .. t
  278. if n < #returns then
  279. ret = ret .. ', '
  280. end
  281. end
  282. ret = ret .. ']>'
  283. end
  284. return ret
  285. end
  286. local function put_fn_variant (fn, variant, is_interface)
  287. -- docs
  288. local doc = fn.description
  289. if variant.description then
  290. doc = doc .. '\n\n' .. variant.description
  291. end
  292. -- todo: show call example
  293. if #variant.arguments > 0 or #variant.returns > 0 then
  294. doc = doc .. '\n'
  295. if #variant.arguments > 0 then
  296. for _, arg in ipairs(variant.arguments) do
  297. doc = doc .. '\n@param ' .. arg.name .. ' - ' .. arg.description
  298. end
  299. end
  300. if #variant.returns > 0 then
  301. doc = doc .. '\n@returns '
  302. -- todo handle .table field in return value (for lovr.graphics.getDevice() it's there instead of description)
  303. if #variant.returns == 1 then
  304. doc = doc .. (variant.returns[1].description or '')
  305. else
  306. for _, ret in ipairs(variant.returns) do
  307. doc = doc .. '\n' .. ret.name .. ' - ' .. (ret.description or '')
  308. end
  309. end
  310. end
  311. end
  312. if fn.notes then
  313. doc = doc .. '\n\n' .. fn.notes
  314. end
  315. if fn.related then
  316. doc = doc .. '\n'
  317. for _, rel in ipairs(fn.related) do
  318. doc = doc .. '\n@see {@link ' .. rel:gsub(':', '.') .. '}'
  319. end
  320. end
  321. put_doc(doc)
  322. -- type
  323. put(indentation)
  324. if not is_interface then
  325. put('function ')
  326. end
  327. put(convert_name(fn.name))
  328. put('(')
  329. local last_required = 0
  330. for arg_index, arg in ipairs (variant.arguments) do
  331. if not arg.default then
  332. last_required = arg_index
  333. end
  334. end
  335. for arg_index, arg in ipairs (variant.arguments) do
  336. put(convert_param(arg, arg_index > last_required))
  337. if arg_index < #variant.arguments then
  338. put(', ')
  339. end
  340. end
  341. put('): ')
  342. put(convert_return_values(variant.returns))
  343. put('\n\n')
  344. end
  345. put ('/** @noSelf **/\n')
  346. put ('declare namespace lovr {\n')
  347. indent()
  348. -- callbacks
  349. for _, callback in ipairs (api.callbacks) do
  350. for _, variant in ipairs(callback.variants) do
  351. put_fn_variant (callback, variant)
  352. end
  353. end
  354. put('\n')
  355. -- module functions
  356. for module_index, module in ipairs(api.modules) do
  357. put (indentation, 'namespace ', module.name, ' {\n')
  358. indent()
  359. for _, fn in ipairs(module.functions) do
  360. for _, variant in ipairs (fn.variants) do
  361. put_fn_variant (fn, variant)
  362. end
  363. end
  364. unindent()
  365. put (indentation, '}\n')
  366. if module_index < #api.modules then
  367. put('\n')
  368. end
  369. end
  370. unindent()
  371. put ('}\n\n')
  372. -- module types
  373. for _, module in ipairs(api.modules) do
  374. for _, enum in ipairs(module.enums) do
  375. -- doc
  376. local doc = enum.description
  377. if enum.notes then
  378. doc = doc .. '\n\n' .. enum.notes
  379. end
  380. if enum.related then
  381. doc = doc .. '\n'
  382. for _, rel in ipairs(enum.related) do
  383. doc = doc .. '\n@see {@link ' .. rel:gsub(':', '.') .. '}'
  384. end
  385. end
  386. put_doc(doc)
  387. -- type
  388. put('declare type ', enum.name, ' = ')
  389. for value_index, value in ipairs(enum.values) do
  390. put(quote_string(value.name))
  391. if value_index < #enum.values then
  392. put(' | ')
  393. end
  394. end
  395. put('\n\n')
  396. end
  397. for _, object in ipairs(module.objects) do
  398. -- doc
  399. local doc = object.description
  400. if object.notes then
  401. doc = doc .. '\n\n' .. object.notes
  402. end
  403. if object.related then
  404. doc = doc .. '\n'
  405. for _, rel in ipairs(object.related) do
  406. doc = doc .. '\n@see {@link ' .. rel:gsub(':', '.') .. '}'
  407. end
  408. end
  409. put_doc(doc)
  410. -- interface
  411. local extends = object.extends and (' extends ' .. convert_type(object.extends)) or ''
  412. put ('declare interface ', convert_type(object.name), extends, ' {\n')
  413. indent()
  414. for _, method in ipairs (object.methods) do
  415. for _, variant in ipairs (method.variants) do
  416. put_fn_variant (method, variant, true)
  417. end
  418. end
  419. unindent()
  420. put ('}\n\n')
  421. end
  422. end
  423. put(vector_ops)
  424. out:close()
  425. end