typescript.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  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. os.execute("mkdir -p " .. path)
  190. local out = io.open(path .. '/lovr-api.d.ts', 'w')
  191. assert(out)
  192. local function put (...)
  193. out:write(...)
  194. end
  195. local indentation = ''
  196. local function indent()
  197. indentation = (' '):rep(#indentation + 2)
  198. end
  199. local function unindent()
  200. indentation = (' '):rep(#indentation - 2)
  201. end
  202. local function quote_string (str)
  203. return "'" .. str:gsub('\\', '\\\\'):gsub("'", "\\'") .. "'"
  204. end
  205. local function put_doc(doc)
  206. if doc:match('\n') then
  207. put(indentation, '/**\n',
  208. indentation, ' * ', doc:gsub('\n', '\n' .. indentation .. ' * '), '\n',
  209. indentation, ' */\n')
  210. else
  211. put(indentation, '/** ', doc, ' */\n')
  212. end
  213. end
  214. -- todo: more specific types
  215. local type_map = {
  216. table = 'LuaTable',
  217. userdata = 'any',
  218. lightuserdata = 'any',
  219. ['function'] = '(...args: any[]) => any',
  220. ['*'] = 'any',
  221. ['Object'] = 'LovrObject'
  222. }
  223. local convert_type
  224. local function convert_table (table_fields)
  225. local result = '{ '
  226. for _, field in ipairs (table_fields) do
  227. local optional = field.default and '?' or ''
  228. result = result .. field.name .. optional .. ': ' .. convert_type(field.type, field.table) .. ', '
  229. end
  230. return result .. '}'
  231. end
  232. function convert_type (t, table_fields)
  233. if t == 'table' and table_fields then
  234. return convert_table (table_fields)
  235. else
  236. return type_map[t] or t
  237. end
  238. end
  239. local name_map = {
  240. ['*'] = 'rest',
  241. }
  242. local function convert_name (n)
  243. return name_map[n] or n
  244. end
  245. local function convert_param (arg, optional)
  246. local name = convert_name(arg.name)
  247. local t = convert_type(arg.type, arg.table)
  248. local pattern = '%.%.%.'
  249. if name:match(pattern) then
  250. name = '...' .. name:gsub(pattern, '')
  251. if name == '...' then
  252. name = name .. 'rest'
  253. end
  254. t = t .. '[]'
  255. end
  256. return name .. (optional and '?' or '') .. ': ' .. t
  257. end
  258. local function convert_return_values (returns)
  259. local ret = 'void'
  260. if #returns == 1 and (not returns[1].name:match('%.%.%.')) then
  261. ret = convert_type(returns[1].type)
  262. elseif #returns >= 1 then
  263. ret = 'LuaMultiReturn<['
  264. for n, ret_n in ipairs (returns) do
  265. local name = convert_name(ret_n.name)
  266. local t = convert_type(ret_n.type)
  267. if name:match('%.%.%.') then
  268. if name == '...' then
  269. name = '...rest'
  270. end
  271. t = t .. '[]'
  272. end
  273. ret = ret .. name .. ': ' .. t
  274. if n < #returns then
  275. ret = ret .. ', '
  276. end
  277. end
  278. ret = ret .. ']>'
  279. end
  280. return ret
  281. end
  282. local function put_fn_variant (fn, variant, is_interface)
  283. -- docs
  284. local doc = fn.description
  285. if variant.description then
  286. doc = doc .. '\n\n' .. variant.description
  287. end
  288. -- todo: show call example
  289. if #variant.arguments > 0 or #variant.returns > 0 then
  290. doc = doc .. '\n'
  291. if #variant.arguments > 0 then
  292. for _, arg in ipairs(variant.arguments) do
  293. doc = doc .. '\n@param ' .. arg.name .. ' - ' .. arg.description
  294. end
  295. end
  296. if #variant.returns > 0 then
  297. doc = doc .. '\n@returns '
  298. -- todo handle .table field in return value (for lovr.graphics.getDevice() it's there instead of description)
  299. if #variant.returns == 1 then
  300. doc = doc .. (variant.returns[1].description or '')
  301. else
  302. for _, ret in ipairs(variant.returns) do
  303. doc = doc .. '\n' .. ret.name .. ' - ' .. (ret.description or '')
  304. end
  305. end
  306. end
  307. end
  308. if fn.notes then
  309. doc = doc .. '\n\n' .. fn.notes
  310. end
  311. if fn.related then
  312. doc = doc .. '\n'
  313. for _, rel in ipairs(fn.related) do
  314. doc = doc .. '\n@see {@link ' .. rel:gsub(':', '.') .. '}'
  315. end
  316. end
  317. put_doc(doc)
  318. -- type
  319. put(indentation)
  320. if not is_interface then
  321. put('function ')
  322. end
  323. put(convert_name(fn.name))
  324. put('(')
  325. local last_required = 0
  326. for arg_index, arg in ipairs (variant.arguments) do
  327. if not arg.default then
  328. last_required = arg_index
  329. end
  330. end
  331. for arg_index, arg in ipairs (variant.arguments) do
  332. put(convert_param(arg, arg_index > last_required))
  333. if arg_index < #variant.arguments then
  334. put(', ')
  335. end
  336. end
  337. put('): ')
  338. put(convert_return_values(variant.returns))
  339. put('\n\n')
  340. end
  341. put ('/** @noSelf **/\n')
  342. put ('declare namespace lovr {\n')
  343. indent()
  344. -- callbacks
  345. for _, callback in ipairs (api.callbacks) do
  346. for _, variant in ipairs(callback.variants) do
  347. put_fn_variant (callback, variant)
  348. end
  349. end
  350. put('\n')
  351. -- module functions
  352. for module_index, module in ipairs(api.modules) do
  353. put (indentation, 'namespace ', module.name, ' {\n')
  354. indent()
  355. for _, fn in ipairs(module.functions) do
  356. for _, variant in ipairs (fn.variants) do
  357. put_fn_variant (fn, variant)
  358. end
  359. end
  360. unindent()
  361. put (indentation, '}\n')
  362. if module_index < #api.modules then
  363. put('\n')
  364. end
  365. end
  366. unindent()
  367. put ('}\n\n')
  368. -- module types
  369. for _, module in ipairs(api.modules) do
  370. for _, enum in ipairs(module.enums) do
  371. -- doc
  372. local doc = enum.description
  373. if enum.notes then
  374. doc = doc .. '\n\n' .. enum.notes
  375. end
  376. if enum.related then
  377. doc = doc .. '\n'
  378. for _, rel in ipairs(enum.related) do
  379. doc = doc .. '\n@see {@link ' .. rel:gsub(':', '.') .. '}'
  380. end
  381. end
  382. put_doc(doc)
  383. -- type
  384. put('declare type ', enum.name, ' = ')
  385. for value_index, value in ipairs(enum.values) do
  386. put(quote_string(value.name))
  387. if value_index < #enum.values then
  388. put(' | ')
  389. end
  390. end
  391. put('\n\n')
  392. end
  393. for _, object in ipairs(module.objects) do
  394. -- doc
  395. local doc = object.description
  396. if object.notes then
  397. doc = doc .. '\n\n' .. object.notes
  398. end
  399. if object.related then
  400. doc = doc .. '\n'
  401. for _, rel in ipairs(object.related) do
  402. doc = doc .. '\n@see {@link ' .. rel:gsub(':', '.') .. '}'
  403. end
  404. end
  405. put_doc(doc)
  406. -- interface
  407. local extends = object.extends and (' extends ' .. convert_type(object.extends)) or ''
  408. put ('declare interface ', convert_type(object.name), extends, ' {\n')
  409. indent()
  410. for _, method in ipairs (object.methods) do
  411. for _, variant in ipairs (method.variants) do
  412. put_fn_variant (method, variant, true)
  413. end
  414. end
  415. unindent()
  416. put ('}\n\n')
  417. end
  418. end
  419. put(vector_ops)
  420. out:close()
  421. end