cluaupp 1.4.1 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +10 -5
  3. package/api/CHANGELOG.md +3 -0
  4. package/api/chat.js +74 -0
  5. package/api/fixtures/roblox/README.md +6 -0
  6. package/api/fixtures/roblox/aux/sample.d.luau +6 -0
  7. package/api/fixtures/roblox/mini/Mini-API-Dump.json +72 -0
  8. package/api/generated/bench-latest.json +39 -0
  9. package/api/generated/capability-profile.json +76 -0
  10. package/api/generated/lsp-index.json +1 -0
  11. package/api/generated/luau/classes.d.luau +11221 -0
  12. package/api/generated/luau/datatypes.d.luau +327 -0
  13. package/api/generated/luau/enums.d.luau +4918 -0
  14. package/api/generated/luau/globals.d.luau +12 -0
  15. package/api/manifests/roblox-target.manifest.json +22 -0
  16. package/api/normalized/roblox-target.profile.json +237461 -0
  17. package/api/overrides/classes.json +8 -0
  18. package/api/overrides/compatibility.json +9 -0
  19. package/api/overrides/datatypes.json +1579 -0
  20. package/api/overrides/enums.json +8 -0
  21. package/api/overrides/globals.json +8 -0
  22. package/api/overrides/type-mappings.json +9 -0
  23. package/api/roblox-api.lock.json +9 -0
  24. package/data/Mini-API-Dump.json +1 -0
  25. package/editors/vscode/README.md +19 -0
  26. package/editors/vscode/extension.js +222 -21
  27. package/editors/vscode/icons/axiom.svg +4 -0
  28. package/editors/vscode/icons/bloom.svg +5 -0
  29. package/editors/vscode/icons/flare.svg +5 -0
  30. package/editors/vscode/icons/helm.svg +4 -0
  31. package/editors/vscode/icons/hive.svg +5 -0
  32. package/editors/vscode/icons/mint.svg +4 -0
  33. package/editors/vscode/icons/shift.svg +6 -0
  34. package/editors/vscode/language-configuration-schema.json +24 -0
  35. package/editors/vscode/language-configuration.json +21 -0
  36. package/editors/vscode/package.json +220 -6
  37. package/editors/vscode/snippets/axiom.json +12 -0
  38. package/editors/vscode/snippets/bloom.json +12 -0
  39. package/editors/vscode/snippets/flare.json +27 -0
  40. package/editors/vscode/snippets/helm.json +12 -0
  41. package/editors/vscode/snippets/hive.json +12 -0
  42. package/editors/vscode/snippets/mint.json +12 -0
  43. package/editors/vscode/snippets/shift.json +17 -0
  44. package/editors/vscode/syntaxes/axiom.tmLanguage.json +5 -0
  45. package/editors/vscode/syntaxes/bloom.tmLanguage.json +5 -0
  46. package/editors/vscode/syntaxes/flare.tmLanguage.json +75 -0
  47. package/editors/vscode/syntaxes/helm.tmLanguage.json +5 -0
  48. package/editors/vscode/syntaxes/hive.tmLanguage.json +5 -0
  49. package/editors/vscode/syntaxes/mint.tmLanguage.json +5 -0
  50. package/editors/vscode/syntaxes/schema.tmLanguage.json +89 -0
  51. package/editors/vscode/syntaxes/shift.tmLanguage.json +5 -0
  52. package/examples/game/.cluaupp/compile-cache/ReplicatedStorage__Shared__Constants__Datas__TemplateData.clh.json +1 -0
  53. package/examples/game/.cluaupp/compile-cache/ServerScriptService__Boot__DataBoot.server.clpp.json +1 -0
  54. package/examples/game/.cluaupp/compile-cache/ServerScriptService__Configuration__ServerConfig.clp.json +1 -0
  55. package/examples/game/.cluaupp/compile-cache/ServerScriptService__Handlers__Guard.server.clpp.json +1 -0
  56. package/examples/game/.cluaupp/compile-cache/ServerScriptService__Handlers__PlayerHandler.server.clpp.json +1 -0
  57. package/examples/game/.cluaupp/compile-cache/StarterPlayer__StarterCharacterScripts__Character__CharacterReady.client.clpp.json +1 -0
  58. package/examples/game/.cluaupp/compile-cache/StarterPlayer__StarterPlayerScripts__Controllers__DataController.client.clpp.json +1 -0
  59. package/examples/game/.cluaupp/compile-cache/index.json +12 -0
  60. package/examples/game/.cluaupp/datamodel-profile.json +94 -0
  61. package/examples/game/.vscode/extensions.json +3 -1
  62. package/examples/game/.vscode/settings.json +13 -1
  63. package/examples/game/include/clpp/datatypes.clh +337 -373
  64. package/examples/game/include/clpp/generated/datamodel.clh +25 -0
  65. package/examples/game/include/clpp/generated/enums.clh +1 -1
  66. package/examples/game/include/clpp/generated/instances.clh +41135 -41064
  67. package/examples/game/include/clpp/libs/axiom.clh +1 -1
  68. package/examples/game/include/clpp/libs/math.clh +14 -0
  69. package/examples/game/include/clpp/libs/roster.clh +6 -0
  70. package/examples/game/include/clpp/libs/spring.clh +17 -0
  71. package/examples/game/include/clpp/libs/ward.clh +1 -0
  72. package/examples/game/include/clpp/roblox.clh +3 -4
  73. package/examples/game/libs/ArrayIndexer/init.luau +209 -0
  74. package/examples/game/libs/Axiom/init.luau +608 -0
  75. package/examples/game/libs/Bloom/init.luau +433 -0
  76. package/examples/game/libs/Coil/init.luau +215 -0
  77. package/examples/game/libs/Crest/init.luau +426 -0
  78. package/examples/game/libs/Echo/init.luau +417 -0
  79. package/examples/game/libs/Ember/init.luau +365 -0
  80. package/examples/game/libs/Flare/init.luau +717 -0
  81. package/examples/game/libs/Gleam/init.luau +729 -0
  82. package/examples/game/libs/Guide/init.luau +215 -0
  83. package/examples/game/libs/Helm/init.luau +377 -0
  84. package/examples/game/libs/Hive/init.luau +241 -0
  85. package/examples/game/libs/Keep/Client.luau +114 -0
  86. package/examples/game/libs/Keep/Data.luau +883 -0
  87. package/examples/game/libs/Keep/Engine.luau +211 -0
  88. package/examples/game/libs/Keep/Enum.luau +6 -0
  89. package/examples/game/libs/Keep/Enums.luau +10 -0
  90. package/examples/game/libs/Keep/Path.luau +236 -0
  91. package/examples/game/libs/Keep/Replicator.luau +298 -0
  92. package/examples/game/libs/Keep/Server.luau +251 -0
  93. package/examples/game/libs/Keep/Store.luau +652 -0
  94. package/examples/game/libs/Keep/Trade.luau +455 -0
  95. package/examples/game/libs/Keep/init.luau +205 -0
  96. package/examples/game/libs/Lens/init.luau +609 -0
  97. package/examples/game/libs/Mint/init.luau +279 -0
  98. package/examples/game/libs/Net/init.luau +156 -0
  99. package/examples/game/libs/Net/src/codec.luau +224 -0
  100. package/examples/game/libs/Occlude/init.luau +117 -0
  101. package/examples/game/libs/Pin/init.luau +309 -0
  102. package/examples/game/libs/Promise/LICENSE +21 -0
  103. package/examples/game/libs/Promise/_impl/LICENSE +21 -0
  104. package/examples/game/libs/Promise/_impl/init.lua +1991 -0
  105. package/examples/game/libs/Promise/init.lua +1990 -0
  106. package/examples/game/libs/Promise/init.luau +13 -0
  107. package/examples/game/libs/Promise/types.luau +46 -0
  108. package/examples/game/libs/Roster/init.luau +686 -0
  109. package/examples/game/libs/SOURCES.md +31 -0
  110. package/examples/game/libs/Shift/init.luau +238 -0
  111. package/examples/game/libs/Spark/init.luau +487 -0
  112. package/examples/game/libs/Stage/init.luau +178 -0
  113. package/examples/game/libs/Sweep/init.luau +259 -0
  114. package/examples/game/libs/Trace/init.luau +213 -0
  115. package/examples/game/libs/Ward/init.luau +211 -0
  116. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau +29 -0
  117. package/examples/game/out/ReplicatedStorage/Shared/Constants/Datas/TemplateData.luau.map.json +13 -0
  118. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau +83 -0
  119. package/examples/game/out/ReplicatedStorage/Shared/Net/Net.luau.map.json +13 -0
  120. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau +18 -0
  121. package/examples/game/out/ServerScriptService/Boot/DataBoot.server.luau.map.json +13 -0
  122. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau +14 -0
  123. package/examples/game/out/ServerScriptService/Configuration/ServerConfig.luau.map.json +13 -0
  124. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau +15 -0
  125. package/examples/game/out/ServerScriptService/Handlers/Guard.server.luau.map.json +13 -0
  126. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau +50 -0
  127. package/examples/game/out/ServerScriptService/Handlers/PlayerHandler.server.luau.map.json +13 -0
  128. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau +12 -0
  129. package/examples/game/out/StarterPlayer/StarterCharacterScripts/Character/CharacterReady.client.luau.map.json +13 -0
  130. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau +16 -0
  131. package/examples/game/out/StarterPlayer/StarterPlayerScripts/Controllers/DataController.client.luau.map.json +13 -0
  132. package/examples/game/src/ReplicatedStorage/Shared/Net/Net.clh +26 -0
  133. package/examples/game/src/ReplicatedStorage/Shared/Net/Net.flare +5 -0
  134. package/examples/game/src/ServerScriptService/Boot/DataBoot.server.clpp +2 -2
  135. package/examples/game/src/ServerScriptService/Handlers/PlayerHandler.server.clpp +1 -1
  136. package/examples/game/stylua.toml +9 -0
  137. package/generated/api/build-profile.js +115 -0
  138. package/generated/api/cache.js +44 -0
  139. package/generated/api/coverage.js +32 -0
  140. package/generated/api/diff.js +125 -0
  141. package/generated/api/generators/clpp-headers.js +353 -0
  142. package/generated/api/generators/docs-reference.js +7 -0
  143. package/generated/api/generators/lsp-index.js +81 -0
  144. package/generated/api/generators/luau-defs.js +94 -0
  145. package/generated/api/ide.js +76 -0
  146. package/generated/api/identifier.js +47 -0
  147. package/generated/api/index.js +324 -0
  148. package/generated/api/loader.js +57 -0
  149. package/generated/api/merge.js +88 -0
  150. package/generated/api/model.js +5 -0
  151. package/generated/api/normalize.js +231 -0
  152. package/generated/api/overrides-validate.js +58 -0
  153. package/generated/api/perf.js +12 -0
  154. package/generated/api/registry.js +136 -0
  155. package/generated/api/release.js +44 -0
  156. package/generated/api/resolver.js +250 -0
  157. package/generated/api/sources/aux-types-diff.js +75 -0
  158. package/generated/api/sources/client-tracker.js +11 -0
  159. package/generated/api/sources/creator-docs.js +42 -0
  160. package/generated/api/sources/luau-types.js +34 -0
  161. package/generated/api/sources/mini-dump.js +17 -0
  162. package/generated/api/sources/studio-api.js +8 -0
  163. package/generated/api/sources/studio-apiv2.js +11 -0
  164. package/generated/api/sources/studio-full-api.js +8 -0
  165. package/generated/api/studio-smoke.js +80 -0
  166. package/generated/api/type-parse.js +49 -0
  167. package/generated/api/understander.js +51 -0
  168. package/generated/api/validate.js +135 -0
  169. package/generated/api-cli.js +421 -0
  170. package/generated/cli.js +17 -6
  171. package/generated/clpp/contract.js +3 -3
  172. package/generated/clpp/format-luau.js +236 -0
  173. package/generated/clpp/index.js +13 -1
  174. package/generated/clpp/modules.js +227 -0
  175. package/generated/clpp/postprocess.js +86 -5
  176. package/generated/clpp/rewrite-janitor.js +145 -0
  177. package/generated/clpp/runner.js +14 -2
  178. package/generated/compile.js +82 -0
  179. package/generated/flare/index.js +15 -3
  180. package/generated/flare/language.js +561 -0
  181. package/generated/flare/parse.js +19 -7
  182. package/generated/intellisense.js +76 -2
  183. package/generated/lsp.js +118 -7
  184. package/generated/native/index.js +217 -16
  185. package/generated/target/authority-check.js +136 -0
  186. package/generated/target/authority-graph.js +74 -0
  187. package/generated/target/build-cache.js +112 -0
  188. package/generated/target/capabilities.js +89 -0
  189. package/generated/target/capability-profile.js +30 -0
  190. package/generated/target/component-contracts.js +60 -0
  191. package/generated/target/context-check.js +160 -0
  192. package/generated/target/datamodel-check.js +170 -0
  193. package/generated/target/datamodel-generate.js +74 -0
  194. package/generated/target/datamodel.js +233 -0
  195. package/generated/target/diagnostics.js +63 -0
  196. package/generated/target/doctor.js +152 -0
  197. package/generated/target/flare-version.js +58 -0
  198. package/generated/target/get-service.js +69 -0
  199. package/generated/target/lifetime-check.js +47 -0
  200. package/generated/target/luau-stamp.js +39 -0
  201. package/generated/target/optimizer-advise.js +88 -0
  202. package/generated/target/optimizer-apply.js +44 -0
  203. package/generated/target/optimizer-soa.js +254 -0
  204. package/generated/target/parallel-check.js +136 -0
  205. package/generated/target/pgo.js +148 -0
  206. package/generated/target/project-docs.js +69 -0
  207. package/generated/target/project-graph.js +71 -0
  208. package/generated/target/roblox.js +63 -0
  209. package/generated/target/security-check.js +41 -0
  210. package/generated/target/source-map.js +25 -0
  211. package/generated/target/studio-bridge.js +112 -0
  212. package/generated/utils/project.js +178 -8
  213. package/generated/utils/rojo-mapper.js +11 -3
  214. package/include/clpp/datatypes.clh +337 -373
  215. package/include/clpp/generated/enums.clh +1 -1
  216. package/include/clpp/generated/instances.clh +41135 -41064
  217. package/include/clpp/libs/axiom.clh +1 -1
  218. package/include/clpp/libs/hive.clh +1 -0
  219. package/include/clpp/libs/keep.clh +2 -0
  220. package/include/clpp/libs/math.clh +14 -0
  221. package/include/clpp/libs/roster.clh +6 -0
  222. package/include/clpp/libs/spring.clh +17 -0
  223. package/include/clpp/libs/ward.clh +1 -0
  224. package/include/clpp/roblox.clh +3 -4
  225. package/package.json +10 -3
  226. package/plugins/CluauppNav/Plugin.luau +207 -0
  227. package/plugins/CluauppNav/ProfileLogger.luau +27 -0
  228. package/plugins/CluauppNav/README.md +24 -0
  229. package/runtime/Axiom/init.luau +10 -7
  230. package/runtime/Coil/init.luau +8 -2
  231. package/runtime/Crest/init.luau +1 -0
  232. package/runtime/Flare/init.luau +2 -1
  233. package/runtime/Gleam/init.luau +15 -4
  234. package/runtime/Hive/init.luau +36 -14
  235. package/runtime/Pin/init.luau +5 -2
  236. package/runtime/Roster/init.luau +8 -0
  237. package/runtime/Spark/init.luau +3 -0
  238. package/runtime/Sweep/init.luau +2 -0
  239. package/runtime/Trace/init.luau +3 -0
  240. package/runtime/Ward/init.luau +38 -5
  241. package/scripts/postinstall-editor.js +17 -0
  242. package/skills/README.md +13 -0
  243. package/skills/clpp-hive-flare/SKILL.md +79 -0
  244. package/skills/clpp-language/SKILL.md +123 -0
  245. package/skills/clpp-language/compiler.md +89 -0
  246. package/skills/clpp-style/SKILL.md +95 -0
  247. package/src/api/build-profile.ts +138 -0
  248. package/src/api/cache.ts +45 -0
  249. package/src/api/coverage.ts +46 -0
  250. package/src/api/diff.ts +143 -0
  251. package/src/api/generators/clpp-headers.ts +377 -0
  252. package/src/api/generators/docs-reference.ts +11 -0
  253. package/src/api/generators/lsp-index.ts +106 -0
  254. package/src/api/generators/luau-defs.ts +98 -0
  255. package/src/api/ide.ts +87 -0
  256. package/src/api/identifier.ts +47 -0
  257. package/src/api/index.ts +269 -0
  258. package/src/api/loader.ts +60 -0
  259. package/src/api/merge.ts +126 -0
  260. package/src/api/model.ts +283 -0
  261. package/src/api/normalize.ts +257 -0
  262. package/src/api/overrides-validate.ts +58 -0
  263. package/src/api/perf.ts +15 -0
  264. package/src/api/registry.ts +162 -0
  265. package/src/api/release.ts +47 -0
  266. package/src/api/resolver.ts +255 -0
  267. package/src/api/sources/aux-types-diff.ts +81 -0
  268. package/src/api/sources/client-tracker.ts +10 -0
  269. package/src/api/sources/creator-docs.ts +37 -0
  270. package/src/api/sources/luau-types.ts +30 -0
  271. package/src/api/sources/mini-dump.ts +50 -0
  272. package/src/api/sources/studio-api.ts +7 -0
  273. package/src/api/sources/studio-apiv2.ts +10 -0
  274. package/src/api/sources/studio-full-api.ts +7 -0
  275. package/src/api/studio-smoke.ts +82 -0
  276. package/src/api/type-parse.ts +48 -0
  277. package/src/api/understander.ts +72 -0
  278. package/src/api/validate.ts +160 -0
  279. package/src/api-cli.ts +493 -0
  280. package/src/api.generated.js +37 -22
  281. package/src/cli.ts +22 -7
  282. package/src/clpp/contract.ts +34 -3
  283. package/src/clpp/format-luau.ts +262 -0
  284. package/src/clpp/index.ts +13 -2
  285. package/src/clpp/modules.ts +252 -0
  286. package/src/clpp/postprocess.ts +100 -5
  287. package/src/clpp/rewrite-janitor.ts +156 -0
  288. package/src/clpp/runner.ts +14 -2
  289. package/src/compile.ts +87 -0
  290. package/src/flare/index.ts +20 -1
  291. package/src/flare/language.ts +708 -0
  292. package/src/flare/parse.ts +20 -7
  293. package/src/flare/types.ts +2 -0
  294. package/src/intellisense.ts +76 -2
  295. package/src/lsp.ts +151 -8
  296. package/src/native/index.ts +225 -16
  297. package/src/target/authority-check.ts +162 -0
  298. package/src/target/authority-graph.ts +80 -0
  299. package/src/target/build-cache.ts +129 -0
  300. package/src/target/capabilities.ts +115 -0
  301. package/src/target/capability-profile.ts +25 -0
  302. package/src/target/component-contracts.ts +64 -0
  303. package/src/target/context-check.ts +177 -0
  304. package/src/target/datamodel-check.ts +188 -0
  305. package/src/target/datamodel-generate.ts +78 -0
  306. package/src/target/datamodel.ts +261 -0
  307. package/src/target/diagnostics.ts +67 -0
  308. package/src/target/doctor.ts +171 -0
  309. package/src/target/flare-version.ts +61 -0
  310. package/src/target/get-service.ts +85 -0
  311. package/src/target/lifetime-check.ts +45 -0
  312. package/src/target/luau-stamp.ts +33 -0
  313. package/src/target/optimizer-advise.ts +93 -0
  314. package/src/target/optimizer-apply.ts +63 -0
  315. package/src/target/optimizer-soa.ts +270 -0
  316. package/src/target/parallel-check.ts +135 -0
  317. package/src/target/pgo.ts +157 -0
  318. package/src/target/project-docs.ts +68 -0
  319. package/src/target/project-graph.ts +75 -0
  320. package/src/target/roblox.ts +78 -0
  321. package/src/target/security-check.ts +40 -0
  322. package/src/target/source-map.ts +36 -0
  323. package/src/target/studio-bridge.ts +140 -0
  324. package/src/types.ts +14 -0
  325. package/src/utils/project.ts +183 -9
  326. package/src/utils/rojo-mapper.ts +12 -3
  327. package/stylua.toml +9 -0
  328. package/templates/game/.vscode/extensions.json +3 -1
  329. package/templates/game/.vscode/settings.json +8 -1
  330. package/templates/game/include/clpp/datatypes.clh +337 -373
  331. package/templates/game/include/clpp/generated/enums.clh +1 -1
  332. package/templates/game/include/clpp/generated/instances.clh +41135 -41064
  333. package/templates/game/include/clpp/libs/axiom.clh +1 -1
  334. package/templates/game/include/clpp/libs/roster.clh +6 -0
  335. package/templates/game/include/clpp/libs/ward.clh +1 -0
  336. package/templates/game/include/clpp/roblox.clh +3 -4
  337. package/templates/game/src/ReplicatedStorage/Shared/Net/Net.flare +5 -0
  338. package/templates/game/src/ServerScriptService/Boot/DataBoot.server.clpp +2 -2
  339. package/templates/game/src/ServerScriptService/Handlers/PlayerHandler.server.clpp +1 -1
  340. package/templates/game/stylua.toml +9 -0
@@ -1,6 +1,6 @@
1
1
  import type { FlareField, FlareQuery, FlarePacket, FlareSchema, FlareSide, FlareType } from "./types.js";
2
2
 
3
- const TYPES: Record<string, FlareType> = {
3
+ export const FLARE_TYPE_ALIASES: Record<string, FlareType> = {
4
4
  u8: "u8",
5
5
  u16: "u16",
6
6
  u32: "u32",
@@ -28,14 +28,14 @@ const TYPES: Record<string, FlareType> = {
28
28
  Player: "Player",
29
29
  };
30
30
 
31
- const IDENT = "[A-Za-z_][A-Za-z0-9_]*";
31
+ export const FLARE_IDENT = "[A-Za-z_][A-Za-z0-9_]*";
32
32
 
33
33
  function fail(file: string, line: number, message: string): never {
34
34
  throw new Error(`cluaupp flare: ${file}:${line}: ${message}`);
35
35
  }
36
36
 
37
37
  function parseType(raw: string, file: string, line: number): FlareType {
38
- const mapped = TYPES[raw];
38
+ const mapped = FLARE_TYPE_ALIASES[raw];
39
39
  if (!mapped) {
40
40
  fail(file, line, `unknown type '${raw}'`);
41
41
  }
@@ -53,7 +53,7 @@ function parseFields(list: string, file: string, line: number): FlareField[] {
53
53
  if (!piece) {
54
54
  continue;
55
55
  }
56
- const match = piece.match(new RegExp(`^(${IDENT})\\s+(${IDENT})$`));
56
+ const match = piece.match(new RegExp(`^(${FLARE_IDENT})\\s+(${FLARE_IDENT})$`));
57
57
  if (!match) {
58
58
  fail(file, line, `expected 'Type name' in '${piece}'`);
59
59
  }
@@ -76,6 +76,7 @@ function parseSide(raw: string, file: string, line: number): FlareSide {
76
76
  export function parseFlare(source: string, filePath: string, defaultName: string): FlareSchema {
77
77
  const file = filePath.replace(/\\/g, "/");
78
78
  let name = defaultName;
79
+ let version: number | undefined;
79
80
  const packets: FlarePacket[] = [];
80
81
  const queries: FlareQuery[] = [];
81
82
  let packetId = 1;
@@ -89,6 +90,12 @@ export function parseFlare(source: string, filePath: string, defaultName: string
89
90
  continue;
90
91
  }
91
92
 
93
+ const verStmt = line.match(/^version\s+(\d+)\s*;?$/i);
94
+ if (verStmt) {
95
+ version = Number(verStmt[1]);
96
+ continue;
97
+ }
98
+
92
99
  const opt = line.match(/^opt\s+name\s*=\s*(.+)$/i);
93
100
  if (opt) {
94
101
  const value = opt[1].trim().replace(/^["']|["']$/g, "");
@@ -99,9 +106,15 @@ export function parseFlare(source: string, filePath: string, defaultName: string
99
106
  continue;
100
107
  }
101
108
 
109
+ const optVer = line.match(/^opt\s+version\s*=\s*(\d+)\s*;?$/i);
110
+ if (optVer) {
111
+ version = Number(optVer[1]);
112
+ continue;
113
+ }
114
+
102
115
  const packet = line.match(
103
116
  new RegExp(
104
- `^packet\\s+(${IDENT})\\s*\\((.*)\\)\\s+from\\s+(Client|Server)(?:\\s+(unreliable|reliable))?\\s*$`,
117
+ `^packet\\s+(${FLARE_IDENT})\\s*\\((.*)\\)\\s+from\\s+(Client|Server)(?:\\s+(unreliable|reliable))?\\s*$`,
105
118
  "i",
106
119
  ),
107
120
  );
@@ -121,7 +134,7 @@ export function parseFlare(source: string, filePath: string, defaultName: string
121
134
  continue;
122
135
  }
123
136
 
124
- const query = line.match(new RegExp(`^query\\s+(${IDENT})\\s*\\((.*)\\)\\s*->\\s*(${IDENT})\\s*$`, "i"));
137
+ const query = line.match(new RegExp(`^query\\s+(${FLARE_IDENT})\\s*\\((.*)\\)\\s*->\\s*(${FLARE_IDENT})\\s*$`, "i"));
125
138
  if (query) {
126
139
  if (queryId > 255) {
127
140
  fail(file, lineNo, "too many queries (max 255)");
@@ -143,5 +156,5 @@ export function parseFlare(source: string, filePath: string, defaultName: string
143
156
  fail(file, 1, "schema is empty — add a packet or query");
144
157
  }
145
158
 
146
- return { name, sourcePath: file, packets, queries };
159
+ return { name, sourcePath: file, version, packets, queries };
147
160
  }
@@ -48,6 +48,8 @@ export type FlareQuery = {
48
48
  export type FlareSchema = {
49
49
  name: string;
50
50
  sourcePath: string;
51
+ /** Optional schema version from `version N;` / `opt version = N`. */
52
+ version?: number;
51
53
  packets: FlarePacket[];
52
54
  queries: FlareQuery[];
53
55
  };
@@ -7,6 +7,7 @@ import type { ProjectConfig } from "./types.js";
7
7
  import { CLPP_INSTALL_HINT } from "./clpp/contract.js";
8
8
  import { hasClpp } from "./clpp/runner.js";
9
9
  import { isSourceFile } from "./clpp/paths.js";
10
+ import { editorBin } from "./editor-install.js";
10
11
 
11
12
  export type CluauppDiagnostic = {
12
13
  line: number;
@@ -82,17 +83,32 @@ function writeVscode(root: string): void {
82
83
  "*.server.clpp": "clpp",
83
84
  "*.client.clpp": "clpp",
84
85
  "*.plugin.clpp": "clpp",
86
+ "*.flare": "flare",
87
+ "*.hive": "hive",
88
+ "*.mint": "mint",
89
+ "*.bloom": "bloom",
90
+ "*.helm": "helm",
91
+ "*.shift": "shift",
92
+ "*.axiom": "axiom",
85
93
  };
86
94
  writeJson(settingsPath, {
87
95
  ...current,
88
96
  "files.associations": associations,
97
+ "[luau]": {
98
+ ...((current["[luau]"] && typeof current["[luau]"] === "object" && !Array.isArray(current["[luau]"]))
99
+ ? (current["[luau]"] as Record<string, unknown>)
100
+ : {}),
101
+ "editor.insertSpaces": false,
102
+ "editor.tabSize": 4,
103
+ "editor.detectIndentation": false,
104
+ },
89
105
  });
90
106
 
91
107
  const extensionsPath = path.join(root, ".vscode", "extensions.json");
92
108
  const extensions = readJsonObject(extensionsPath);
93
109
  writeJson(extensionsPath, {
94
110
  ...extensions,
95
- recommendations: uniqueStrings(extensions.recommendations),
111
+ recommendations: uniqueStrings(extensions.recommendations, ["kartzdev.cluaupp-flare"]),
96
112
  unwantedRecommendations: uniqueStrings(extensions.unwantedRecommendations, ["ms-vscode.cpptools"]),
97
113
  });
98
114
  }
@@ -103,8 +119,32 @@ export function diagnosticsFor(_source?: string, _fileName?: string): CluauppDia
103
119
  return [];
104
120
  }
105
121
 
122
+ function writeStylua(root: string): void {
123
+ const dest = path.join(root, "stylua.toml");
124
+ const body = `# cluaupp roblox luau
125
+ syntax = "Luau"
126
+ column_width = 120
127
+ line_endings = "Unix"
128
+ indent_type = "Tabs"
129
+ indent_width = 4
130
+ quote_style = "AutoPreferDouble"
131
+ call_parentheses = "Always"
132
+ collapse_simple_statement = "Never"
133
+ `;
134
+ if (fs.existsSync(dest)) {
135
+ const existing = fs.readFileSync(dest, "utf8");
136
+ if (!existing.includes("cluaupp roblox luau") && existing.trim() !== "") {
137
+ return;
138
+ }
139
+ }
140
+ if (!fs.existsSync(dest) || fs.readFileSync(dest, "utf8") !== body) {
141
+ fs.writeFileSync(dest, body, "utf8");
142
+ }
143
+ }
144
+
106
145
  export function syncEditorSupport(root: string, _config: Partial<ProjectConfig> = {}): void {
107
146
  writeVscode(root);
147
+ writeStylua(root);
108
148
  }
109
149
 
110
150
  export function installEditorExtension(cluauppRoot = PACKAGE_ROOT): string[] {
@@ -112,7 +152,7 @@ export function installEditorExtension(cluauppRoot = PACKAGE_ROOT): string[] {
112
152
  if (!fs.existsSync(from)) {
113
153
  return [];
114
154
  }
115
- const id = `kartzdev.cluaupp-diagnostics-${pkg.version}`;
155
+ const id = `kartzdev.cluaupp-flare-${pkg.version}`;
116
156
  const homes = [path.join(os.homedir(), ".cursor", "extensions"), path.join(os.homedir(), ".vscode", "extensions")];
117
157
  const installed: string[] = [];
118
158
  for (const home of homes) {
@@ -120,16 +160,50 @@ export function installEditorExtension(cluauppRoot = PACKAGE_ROOT): string[] {
120
160
  continue;
121
161
  }
122
162
  fs.mkdirSync(home, { recursive: true });
163
+ try {
164
+ for (const name of fs.readdirSync(home)) {
165
+ if (name.startsWith("kartzdev.cluaupp-flare-") && name !== id) {
166
+ fs.rmSync(path.join(home, name), { recursive: true, force: true });
167
+ }
168
+ }
169
+ } catch {
170
+ // ignore prune errors
171
+ }
123
172
  const dest = path.join(home, id);
124
173
  copyDir(from, dest);
125
174
  fs.writeFileSync(path.join(dest, "cluaupp.root"), path.resolve(cluauppRoot), "utf8");
175
+ const manifestPath = path.join(dest, "package.json");
176
+ if (fs.existsSync(manifestPath)) {
177
+ try {
178
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")) as Record<string, unknown>;
179
+ manifest.version = pkg.version;
180
+ fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, "\t")}\n`, "utf8");
181
+ } catch {
182
+ // keep the copied manifest
183
+ }
184
+ }
126
185
  installed.push(dest);
127
186
  }
187
+
188
+ const bin = editorBin();
189
+ if (bin && installed[0]) {
190
+ spawnSync(bin, ["--install-extension", installed[0], "--force"], {
191
+ encoding: "utf8",
192
+ windowsHide: true,
193
+ timeout: 120000,
194
+ });
195
+ }
128
196
  return installed;
129
197
  }
130
198
 
131
199
  export async function installEditorSupport() {
132
200
  const local = installEditorExtension();
201
+ if (local.length > 0) {
202
+ console.log("cluaupp: schema IntelliSense installed (.flare .hive .mint .bloom .helm .shift .axiom)");
203
+ for (const dest of local) {
204
+ console.log(" ", dest);
205
+ }
206
+ }
133
207
  if (hasClpp()) {
134
208
  const result = spawnSync("clpp", ["install"], { encoding: "utf8", windowsHide: true });
135
209
  if (result.status === 0) {
package/src/lsp.ts CHANGED
@@ -1,30 +1,173 @@
1
1
  import {
2
- createConnection,
2
+ CompletionItemKind,
3
+ DiagnosticSeverity,
4
+ MarkupKind,
5
+ ParameterInformation,
3
6
  ProposedFeatures,
7
+ SignatureInformation,
8
+ SymbolKind,
4
9
  TextDocuments,
5
10
  TextDocumentSyncKind,
11
+ createConnection,
12
+ type CompletionItem,
13
+ type Diagnostic,
14
+ type DocumentSymbol,
15
+ type Hover,
16
+ type SignatureHelp,
6
17
  } from "vscode-languageserver/node";
7
18
  import { TextDocument } from "vscode-languageserver-textdocument";
19
+ import {
20
+ completionsAt,
21
+ hoverAt,
22
+ isFlareDocument,
23
+ signatureAt,
24
+ symbolsIn,
25
+ diagnoseFlare,
26
+ type FlareCompletionKind,
27
+ type FlareSeverity,
28
+ } from "./flare/language.js";
8
29
  import { pkg } from "./package-info.js";
9
30
 
31
+ const KIND: Record<FlareCompletionKind, CompletionItemKind> = {
32
+ keyword: CompletionItemKind.Keyword,
33
+ type: CompletionItemKind.TypeParameter,
34
+ snippet: CompletionItemKind.Snippet,
35
+ value: CompletionItemKind.EnumMember,
36
+ field: CompletionItemKind.Field,
37
+ };
38
+
39
+ const SEVERITY: Record<FlareSeverity, DiagnosticSeverity> = {
40
+ error: DiagnosticSeverity.Error,
41
+ warning: DiagnosticSeverity.Warning,
42
+ hint: DiagnosticSeverity.Hint,
43
+ };
44
+
45
+ const SYMBOL: Record<"packet" | "query" | "option", SymbolKind> = {
46
+ packet: SymbolKind.Event,
47
+ query: SymbolKind.Method,
48
+ option: SymbolKind.Namespace,
49
+ };
50
+
51
+ function flareDiagnostics(document: TextDocument): Diagnostic[] {
52
+ return diagnoseFlare(document.getText(), document.uri).map((item) => ({
53
+ range: {
54
+ start: { line: item.line, character: item.start },
55
+ end: { line: item.line, character: Math.max(item.end, item.start + 1) },
56
+ },
57
+ message: item.message,
58
+ severity: SEVERITY[item.severity],
59
+ source: "flare",
60
+ }));
61
+ }
62
+
10
63
  export function start(_options: { projectRoot?: string } = {}): void {
11
64
  const connection = createConnection(ProposedFeatures.all);
12
65
  const documents = new TextDocuments(TextDocument);
13
66
 
14
67
  connection.onInitialize(() => ({
15
68
  capabilities: {
16
- textDocumentSync: TextDocumentSyncKind.Full,
69
+ textDocumentSync: TextDocumentSyncKind.Incremental,
70
+ completionProvider: {
71
+ triggerCharacters: [" ", "(", ",", ">", "="],
72
+ resolveProvider: false,
73
+ },
74
+ hoverProvider: true,
75
+ documentSymbolProvider: true,
76
+ signatureHelpProvider: {
77
+ triggerCharacters: ["(", ","],
78
+ },
17
79
  },
18
- serverInfo: { name: "cluaupp", version: pkg.version },
80
+ serverInfo: { name: "cluaupp-flare", version: pkg.version },
19
81
  }));
20
82
 
21
- const clear = (uri: string) => {
22
- connection.sendDiagnostics({ uri, diagnostics: [] });
83
+ const publish = (document: TextDocument) => {
84
+ if (!isFlareDocument(document.uri)) {
85
+ connection.sendDiagnostics({ uri: document.uri, diagnostics: [] });
86
+ return;
87
+ }
88
+ connection.sendDiagnostics({ uri: document.uri, diagnostics: flareDiagnostics(document) });
23
89
  };
24
90
 
25
- documents.onDidOpen((event) => clear(event.document.uri));
26
- documents.onDidChangeContent((event) => clear(event.document.uri));
27
- documents.onDidClose((event) => clear(event.document.uri));
91
+ documents.onDidOpen((event) => publish(event.document));
92
+ documents.onDidChangeContent((event) => publish(event.document));
93
+ documents.onDidClose((event) => {
94
+ connection.sendDiagnostics({ uri: event.document.uri, diagnostics: [] });
95
+ });
96
+
97
+ connection.onCompletion((params): CompletionItem[] => {
98
+ const document = documents.get(params.textDocument.uri);
99
+ if (!document || !isFlareDocument(document.uri)) {
100
+ return [];
101
+ }
102
+ return completionsAt(document.getText(), params.position.line, params.position.character).map((item) => ({
103
+ label: item.label,
104
+ kind: KIND[item.kind],
105
+ detail: item.detail,
106
+ documentation: { kind: MarkupKind.Markdown, value: item.documentation },
107
+ insertText: item.insertText,
108
+ insertTextFormat: item.snippet ? 2 : 1,
109
+ sortText: item.sortText,
110
+ }));
111
+ });
112
+
113
+ connection.onHover((params): Hover | null => {
114
+ const document = documents.get(params.textDocument.uri);
115
+ if (!document || !isFlareDocument(document.uri)) {
116
+ return null;
117
+ }
118
+ const hover = hoverAt(document.getText(), params.position.line, params.position.character);
119
+ if (!hover) {
120
+ return null;
121
+ }
122
+ return {
123
+ contents: { kind: MarkupKind.Markdown, value: hover.contents },
124
+ range: {
125
+ start: { line: params.position.line, character: hover.start },
126
+ end: { line: params.position.line, character: hover.end },
127
+ },
128
+ };
129
+ });
130
+
131
+ connection.onDocumentSymbol((params): DocumentSymbol[] => {
132
+ const document = documents.get(params.textDocument.uri);
133
+ if (!document || !isFlareDocument(document.uri)) {
134
+ return [];
135
+ }
136
+ return symbolsIn(document.getText()).map((symbol) => ({
137
+ name: symbol.name,
138
+ detail: symbol.detail,
139
+ kind: SYMBOL[symbol.kind],
140
+ range: {
141
+ start: { line: symbol.line, character: 0 },
142
+ end: { line: symbol.line, character: Math.max(symbol.end, 1) },
143
+ },
144
+ selectionRange: {
145
+ start: { line: symbol.line, character: symbol.start },
146
+ end: { line: symbol.line, character: Math.max(symbol.end, symbol.start + 1) },
147
+ },
148
+ }));
149
+ });
150
+
151
+ connection.onSignatureHelp((params): SignatureHelp | null => {
152
+ const document = documents.get(params.textDocument.uri);
153
+ if (!document || !isFlareDocument(document.uri)) {
154
+ return null;
155
+ }
156
+ const signature = signatureAt(document.getText(), params.position.line, params.position.character);
157
+ if (!signature) {
158
+ return null;
159
+ }
160
+ const info = SignatureInformation.create(
161
+ signature.label,
162
+ signature.documentation,
163
+ ...signature.parameters.map((parameter) => ParameterInformation.create(parameter.label, parameter.documentation)),
164
+ );
165
+ return {
166
+ signatures: [info],
167
+ activeSignature: 0,
168
+ activeParameter: signature.activeParameter,
169
+ };
170
+ });
28
171
 
29
172
  documents.listen(connection);
30
173
  connection.listen();
@@ -280,15 +280,14 @@ function compileHelm(source: string, file: string, defaultName: string): NativeE
280
280
  id += 1;
281
281
  }
282
282
  const { headerRel, luauRel } = emitPaths(file, name);
283
- const headerFns = commands
284
- .map((cmd) => {
285
- const params = cmd.args.map((arg) => `${arg.type === "i32" ? "int" : arg.type} ${arg.name}`).join(", ");
286
- return `\tvoid ${cmd.name}(${params});`;
287
- })
283
+ const headerCmds = commands
284
+ .map((cmd, index) => `\t// command ${cmd.name} permission ${cmd.permission}\n\tconst int ${cmd.name} = ${index + 1};`)
288
285
  .join("\n");
289
286
  const header = `${headerBanner("helm")}
287
+ #include <clpp/libs/helm.clh>
290
288
  namespace ${name} {
291
- ${headerFns}
289
+ ${headerCmds}
290
+ HelmSession Open(Player player);
292
291
  }
293
292
  `;
294
293
  const rows = commands
@@ -417,7 +416,7 @@ function compileHive(source: string, file: string, defaultName: string): NativeE
417
416
  #include <clpp/libs/hive.clh>
418
417
  namespace ${name} {
419
418
  ${components.map((item, index) => `\tconst int ${item.name} = ${index + 1};`).join("\n")}
420
- HiveWorld New();
419
+ auto New();
421
420
  }
422
421
  `;
423
422
  const rows = components
@@ -443,32 +442,242 @@ return ${name}
443
442
  return { headerRel, headerPath: headerRel, header, luauRel, luau };
444
443
  }
445
444
 
445
+ /** Mirrors Axiom.Groups + axiom.clh prototypes for IntelliSense on selected namespaces. */
446
+ const AXIOM_GROUP_DECLS: Record<string, string[]> = {
447
+ Scalar: [
448
+ "double Clamp(double value, double min, double max);",
449
+ "double Map(double value, double inMin, double inMax, double outMin, double outMax);",
450
+ "double Wrap(double value, double min, double max);",
451
+ "double Sign(double value);",
452
+ "double Round(double value);",
453
+ "double Round(double value, int digits);",
454
+ "double Snap(double value, double step);",
455
+ "double PingPong(double t, double length);",
456
+ "double Saturate(double value);",
457
+ "double Fract(double value);",
458
+ "double InvLerp(double from, double to, double value);",
459
+ "double Approach(double current, double target, double maxDelta);",
460
+ "bool IsFinite(double value);",
461
+ "double Abs(double value);",
462
+ "double Min(double a, double b);",
463
+ "double Max(double a, double b);",
464
+ "double Pow(double base, double exp);",
465
+ "double Sqrt(double value);",
466
+ "double Cbrt(double value);",
467
+ "double Hypot(double a, double b);",
468
+ "double Log(double value);",
469
+ "double Exp(double value);",
470
+ "double Smoothstep(double edge0, double edge1, double x);",
471
+ "double Smootherstep(double edge0, double edge1, double x);",
472
+ "double Gcd(double a, double b);",
473
+ "double Lcm(double a, double b);",
474
+ "bool IsEven(double value);",
475
+ "bool IsOdd(double value);",
476
+ "double Factorial(double n);",
477
+ "double Scale(double value, int index);",
478
+ ],
479
+ Lerp: [
480
+ "double Lerp(double from, double to, double alpha);",
481
+ "double LerpClamped(double from, double to, double alpha);",
482
+ "Vector2 LerpVector2(Vector2 from, Vector2 to, double alpha);",
483
+ "Vector3 LerpVector3(Vector3 from, Vector3 to, double alpha);",
484
+ "Vector3 LerpVector(Vector3 from, Vector3 to, double alpha);",
485
+ "Color3 LerpColor3(Color3 from, Color3 to, double alpha);",
486
+ "CFrame LerpCFrame(CFrame from, CFrame to, double alpha);",
487
+ "UDim2 LerpUDim2(UDim2 from, UDim2 to, double alpha);",
488
+ "double LerpAngle(double from, double to, double alpha);",
489
+ "double Inverse(double from, double to, double value);",
490
+ ],
491
+ Vector: [
492
+ "Vector3 Project(Vector3 a, Vector3 b);",
493
+ "Vector3 Reject(Vector3 a, Vector3 b);",
494
+ "Vector3 Reflect(Vector3 incident, Vector3 normal);",
495
+ "double Angle(Vector3 a, Vector3 b);",
496
+ "double Distance(Vector3 a, Vector3 b);",
497
+ "double Distance2(Vector2 a, Vector2 b);",
498
+ "CFrame Orthonormal(Vector3 forward);",
499
+ "CFrame Orthonormal(Vector3 forward, Vector3 up);",
500
+ "Vector3 Slerp(Vector3 a, Vector3 b, double t);",
501
+ "CFrame LookAt(Vector3 from, Vector3 look);",
502
+ ],
503
+ CFrame: ["CFrame SlerpCFrame(CFrame a, CFrame b, double t);", "CFrame Flat(CFrame cf);"],
504
+ Easing: [
505
+ "double InSine(double t);",
506
+ "double OutSine(double t);",
507
+ "double InOutSine(double t);",
508
+ "double InQuad(double t);",
509
+ "double OutQuad(double t);",
510
+ "double InOutQuad(double t);",
511
+ "double InCubic(double t);",
512
+ "double OutCubic(double t);",
513
+ "double InOutCubic(double t);",
514
+ "double InQuart(double t);",
515
+ "double OutQuart(double t);",
516
+ "double InOutQuart(double t);",
517
+ "double InQuint(double t);",
518
+ "double OutQuint(double t);",
519
+ "double InOutQuint(double t);",
520
+ "double InExpo(double t);",
521
+ "double OutExpo(double t);",
522
+ "double InOutExpo(double t);",
523
+ "double InCirc(double t);",
524
+ "double OutCirc(double t);",
525
+ "double InOutCirc(double t);",
526
+ "double InBack(double t);",
527
+ "double OutBack(double t);",
528
+ "double InOutBack(double t);",
529
+ "double InElastic(double t);",
530
+ "double OutElastic(double t);",
531
+ "double InOutElastic(double t);",
532
+ "double InBounce(double t);",
533
+ "double OutBounce(double t);",
534
+ "double InOutBounce(double t);",
535
+ "double Linear(double t);",
536
+ ],
537
+ Bezier: [
538
+ "Vector3 CubicBezier(double t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3);",
539
+ "Vector3 QuadraticBezier(double t, Vector3 p0, Vector3 p1, Vector3 p2);",
540
+ "CFrame Hover(double elapsed, Vector3 anchor, Vector3 look);",
541
+ "CFrame Float(double elapsed, Vector3 anchor, Vector3 look);",
542
+ "CFrame FloatSpin(double elapsed, CFrame anchor);",
543
+ ],
544
+ Geometry: [
545
+ "bool AabbContains(Vector3 point, Vector3 min, Vector3 max);",
546
+ "bool SphereContains(Vector3 point, Vector3 center, double radius);",
547
+ "Vector3 RayPlane(Vector3 origin, Vector3 dir, Vector3 point, Vector3 normal);",
548
+ "Vector3 Barycentric(Vector3 p, Vector3 a, Vector3 b, Vector3 c);",
549
+ "Vector3 ClosestPointOnSegment(Vector3 p, Vector3 a, Vector3 b);",
550
+ ],
551
+ Trig: [
552
+ "double Deg(double rad);",
553
+ "double Rad(double deg);",
554
+ "double DeltaAngle(double from, double to);",
555
+ "double AngleDiff(double from, double to);",
556
+ "double DeltaAngleDegrees(double from, double to);",
557
+ "double NormalizeAngle(double angle);",
558
+ "double Sin(double x);",
559
+ "double Cos(double x);",
560
+ "double Tan(double x);",
561
+ "double Asin(double x);",
562
+ "double Acos(double x);",
563
+ "double Atan2(double y, double x);",
564
+ ],
565
+ Probability: [
566
+ "double RandomRange(double min, double max);",
567
+ "double Random(double min, double max);",
568
+ "int Weighted(LuaArray<double> weights);",
569
+ "double Gaussian();",
570
+ "double Gaussian(double mean, double std);",
571
+ "double Average(LuaArray<double> values);",
572
+ "double Sum(LuaArray<double> values);",
573
+ ],
574
+ Noise: [
575
+ "double HashU32(double n);",
576
+ "double Value1(double x);",
577
+ "double Value2(double x, double y);",
578
+ "double Value3(double x, double y, double z);",
579
+ ],
580
+ Color: [
581
+ "Color3 FromHSV(double h, double s, double v);",
582
+ "Color3 Contrast(Color3 color, double amount);",
583
+ "Color3 LerpHSV(Color3 a, Color3 b, double t);",
584
+ ],
585
+ };
586
+
587
+ function axiomFnDecls(): Record<string, string[]> {
588
+ const byFn: Record<string, string[]> = {};
589
+ for (const decls of Object.values(AXIOM_GROUP_DECLS)) {
590
+ for (const decl of decls) {
591
+ const match = decl.match(/\b([A-Za-z_][A-Za-z0-9_]*)\s*\(/);
592
+ if (!match) {
593
+ continue;
594
+ }
595
+ const fn = match[1];
596
+ if (!byFn[fn]) {
597
+ byFn[fn] = [];
598
+ }
599
+ byFn[fn].push(decl);
600
+ }
601
+ }
602
+ return byFn;
603
+ }
604
+
605
+ const AXIOM_FN_DECLS = axiomFnDecls();
606
+
607
+ function axiomHeaderBody(groups: string[], fns: string[], file: string, lineNo: number): string {
608
+ const decls: string[] = [];
609
+ const seen = new Set<string>();
610
+ const pushDecl = (decl: string) => {
611
+ if (seen.has(decl)) {
612
+ return;
613
+ }
614
+ seen.add(decl);
615
+ decls.push(`\t${decl}`);
616
+ };
617
+ for (const group of groups) {
618
+ const key = Object.keys(AXIOM_GROUP_DECLS).find((name) => name.toLowerCase() === group.toLowerCase());
619
+ if (!key) {
620
+ fail(
621
+ file,
622
+ lineNo,
623
+ `unknown group "${group}" — use Scalar Lerp Vector CFrame Easing Bezier Geometry Trig Probability Noise Color`,
624
+ );
625
+ }
626
+ decls.push(`\t// group ${key}`);
627
+ for (const decl of AXIOM_GROUP_DECLS[key]) {
628
+ pushDecl(decl);
629
+ }
630
+ }
631
+ for (const fn of fns) {
632
+ const key = Object.keys(AXIOM_FN_DECLS).find((name) => name.toLowerCase() === fn.toLowerCase());
633
+ if (!key) {
634
+ fail(file, lineNo, `unknown fn "${fn}" — use a name from axiom.clh (e.g. fn LerpVector2)`);
635
+ }
636
+ decls.push(`\t// fn ${key}`);
637
+ for (const decl of AXIOM_FN_DECLS[key]) {
638
+ pushDecl(decl);
639
+ }
640
+ }
641
+ return decls.join("\n");
642
+ }
643
+
446
644
  function compileAxiom(source: string, file: string, defaultName: string): NativeEmit {
447
645
  const { name, lines } = parseOptName(source, file, defaultName);
448
646
  const groups: string[] = [];
647
+ const fns: string[] = [];
648
+ let lastLine = 1;
449
649
  for (const tagged of lines) {
450
650
  const [lineNoRaw, line] = tagged.split("\t");
451
651
  const lineNo = Number(lineNoRaw);
452
- const match = line.match(new RegExp(`^group\\s+(${IDENT})\\s*$`, "i"));
453
- if (!match) {
454
- fail(file, lineNo, `expected group Scalar`);
652
+ lastLine = lineNo;
653
+ const groupMatch = line.match(new RegExp(`^group\\s+(${IDENT})\\s*$`, "i"));
654
+ if (groupMatch) {
655
+ groups.push(groupMatch[1]);
656
+ continue;
657
+ }
658
+ const fnMatch = line.match(new RegExp(`^fn\\s+(${IDENT})\\s*$`, "i"));
659
+ if (fnMatch) {
660
+ fns.push(fnMatch[1]);
661
+ continue;
455
662
  }
456
- groups.push(match[1]);
663
+ fail(file, lineNo, `expected group Lerp or fn LerpVector2`);
457
664
  }
458
- if (groups.length === 0) {
459
- fail(file, 1, "schema is empty — add group Scalar");
665
+ if (groups.length === 0 && fns.length === 0) {
666
+ fail(file, 1, "schema is empty — add group Lerp or fn LerpVector2");
460
667
  }
668
+ const body = axiomHeaderBody(groups, fns, file, lastLine);
669
+ const selectArgs = [...groups, ...fns].map((item) => `"${item}"`).join(", ");
461
670
  const { headerRel, luauRel } = emitPaths(file, name);
462
671
  const header = `${headerBanner("axiom")}
463
672
  #include <clpp/libs/axiom.clh>
464
673
  namespace ${name} {
465
- ${groups.map((group) => `\t// group ${group}`).join("\n")}
674
+ ${body}
466
675
  }
467
676
  `;
468
677
  const luau = `${luauBanner()}local ReplicatedStorage = game:GetService("ReplicatedStorage")
469
678
  local Axiom = require(ReplicatedStorage.CluauppLibs.Axiom)
470
- local ${name} = Axiom.Select({ ${groups.map((group) => `"${group}"`).join(", ")} })
471
- -- out/ only binds groups: ${groups.join(", ")}
679
+ local ${name} = Axiom.Select({ ${selectArgs} })
680
+ -- out/ binds: ${[...groups.map((g) => `group ${g}`), ...fns.map((f) => `fn ${f}`)].join(", ")}
472
681
  return ${name}
473
682
  `;
474
683
  return { headerRel, headerPath: headerRel, header, luauRel, luau };