cluaupp 0.1.2 → 0.1.4

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 (64) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/README.md +5 -8
  3. package/docs/README.md +13 -10
  4. package/docs/architecture.md +18 -11
  5. package/docs/cli.md +2 -2
  6. package/docs/config.md +1 -1
  7. package/docs/cpp-advanced.md +10 -6
  8. package/docs/cpp-organization.md +3 -1
  9. package/docs/cpp-safety.md +11 -7
  10. package/docs/cpp-types.md +1 -1
  11. package/docs/examples/_category_.json +5 -0
  12. package/docs/examples/combat.md +239 -0
  13. package/docs/examples/data-boot.md +98 -0
  14. package/docs/examples/hud.md +96 -0
  15. package/docs/examples/index.md +43 -0
  16. package/docs/examples/leaderstats.md +140 -0
  17. package/docs/examples/shop.md +122 -0
  18. package/docs/examples/sword.md +108 -0
  19. package/docs/getting-started.md +12 -6
  20. package/docs/intellisense.md +31 -0
  21. package/docs/intro.md +2 -2
  22. package/docs/libraries/_category_.json +5 -0
  23. package/docs/libraries/dataservice.md +104 -0
  24. package/docs/libraries/index.md +22 -0
  25. package/docs/libraries/janitor.md +65 -0
  26. package/docs/libraries/more.md +79 -0
  27. package/docs/libraries/net.md +35 -0
  28. package/docs/libraries/promise.md +27 -0
  29. package/docs/oop/_category_.json +5 -0
  30. package/docs/oop/file-tags.md +49 -0
  31. package/docs/oop/index.md +22 -0
  32. package/docs/oop/modules.md +86 -0
  33. package/docs/oop/services.md +83 -0
  34. package/docs/print-cout.md +91 -0
  35. package/docs/syntax.md +59 -5
  36. package/editors/vscode/extension.js +146 -0
  37. package/editors/vscode/package.json +25 -0
  38. package/include/cluaupp/libs/janitor.hpp +7 -3
  39. package/include/cluaupp/roblox.hpp +19 -0
  40. package/package.json +66 -65
  41. package/runtime/Janitor/init.luau +4 -34
  42. package/src/architecture.js +108 -48
  43. package/src/cli.js +85 -10
  44. package/src/client/init.client.cpp +5 -0
  45. package/src/compile.js +20 -4
  46. package/src/editor-install.js +218 -0
  47. package/src/emit.js +320 -8
  48. package/src/intellisense.js +1368 -0
  49. package/src/layout.js +129 -0
  50. package/src/lex.js +17 -9
  51. package/src/libs.js +95 -16
  52. package/src/lsp.js +227 -0
  53. package/src/parse.js +187 -6
  54. package/src/preprocess.js +71 -3
  55. package/src/server/leaderstats.server.cpp +28 -0
  56. package/src/shared/config.cpp +5 -0
  57. package/src/shared/config.h +3 -0
  58. package/src/understand.js +64 -6
  59. package/templates/game/.clangd +11 -0
  60. package/templates/game/.vscode/c_cpp_properties.json +6 -2
  61. package/templates/game/.vscode/extensions.json +6 -0
  62. package/templates/game/.vscode/settings.json +16 -2
  63. package/templates/game/compile_flags.txt +2 -0
  64. package/docs/libraries.md +0 -80
package/src/parse.js CHANGED
@@ -199,8 +199,18 @@ function parse(source, fileName) {
199
199
  return left;
200
200
  }
201
201
 
202
+ function parseShift() {
203
+ let left = parseBinary();
204
+ while (at("op", "<<")) {
205
+ eat("op", "<<");
206
+ const right = parseBinary();
207
+ left = { type: "binary", op: "<<", left, right };
208
+ }
209
+ return left;
210
+ }
211
+
202
212
  function parseExpr() {
203
- const left = parseBinary();
213
+ const left = parseShift();
204
214
  if (at("op", "=")) {
205
215
  i += 1;
206
216
  return { type: "assign", left, right: parseExpr() };
@@ -223,6 +233,58 @@ function parse(source, fileName) {
223
233
  return body;
224
234
  }
225
235
 
236
+ function parseSwitch() {
237
+ eat("kw", "switch");
238
+ eat("op", "(");
239
+ const discriminant = parseExpr();
240
+ eat("op", ")");
241
+ eat("op", "{");
242
+ const cases = [];
243
+ let current = null;
244
+ const flush = () => {
245
+ if (current) {
246
+ cases.push(current);
247
+ current = null;
248
+ }
249
+ };
250
+ while (!at("eof") && !at("op", "}")) {
251
+ skipSemicolons();
252
+ if (at("op", "}")) {
253
+ break;
254
+ }
255
+ if (at("kw", "case")) {
256
+ i += 1;
257
+ const value = parseExpr();
258
+ eat("op", ":");
259
+ if (current && current.body.length === 0 && !current.isDefault) {
260
+ current.values.push(value);
261
+ } else {
262
+ flush();
263
+ current = { values: [value], body: [], isDefault: false };
264
+ }
265
+ continue;
266
+ }
267
+ if (at("kw", "default")) {
268
+ i += 1;
269
+ eat("op", ":");
270
+ flush();
271
+ current = { values: [], body: [], isDefault: true };
272
+ continue;
273
+ }
274
+ if (!current) {
275
+ throw error("switch body needs case or default before statements");
276
+ }
277
+ if (at("op", "{")) {
278
+ current.body.push(...parseBlock());
279
+ continue;
280
+ }
281
+ current.body.push(parseStmt());
282
+ }
283
+ flush();
284
+ eat("op", "}");
285
+ return { type: "switch", discriminant, cases };
286
+ }
287
+
226
288
  function parseIf() {
227
289
  eat("kw", "if");
228
290
  eat("op", "(");
@@ -285,6 +347,9 @@ function parse(source, fileName) {
285
347
  if (at("kw", "if")) {
286
348
  return parseIf();
287
349
  }
350
+ if (at("kw", "switch")) {
351
+ return parseSwitch();
352
+ }
288
353
  if (at("kw", "for")) {
289
354
  return parseFor();
290
355
  }
@@ -293,6 +358,11 @@ function parse(source, fileName) {
293
358
  const value = at("op", ";") || at("op", "}") ? null : parseExpr();
294
359
  return { type: "return", value };
295
360
  }
361
+ if (at("kw", "break")) {
362
+ i += 1;
363
+ skipSemicolons();
364
+ return { type: "break" };
365
+ }
296
366
  if (at("kw", "while")) {
297
367
  i += 1;
298
368
  eat("op", "(");
@@ -312,12 +382,25 @@ function parse(source, fileName) {
312
382
  return { name: "arg", valueType };
313
383
  }
314
384
 
385
+ function parseQualifiedName() {
386
+ const parts = [eat("ident").value];
387
+ while (at("op", "::")) {
388
+ i += 1;
389
+ parts.push(eat("ident").value);
390
+ }
391
+ return {
392
+ parts,
393
+ name: parts[parts.length - 1],
394
+ owner: parts.length > 1 ? parts.slice(0, -1).join("::") : null,
395
+ };
396
+ }
397
+
315
398
  function parseFunction() {
316
399
  const returnType = parseType();
317
400
  if (!returnType || !at("ident")) {
318
401
  throw error("invalid function declaration");
319
402
  }
320
- const name = eat("ident").value;
403
+ const qualified = parseQualifiedName();
321
404
  eat("op", "(");
322
405
  const params = [];
323
406
  if (!at("op", ")")) {
@@ -330,10 +413,96 @@ function parse(source, fileName) {
330
413
  eat("op", ")");
331
414
  if (at("op", ";")) {
332
415
  i += 1;
333
- return { type: "proto", name, returnType, params };
416
+ return { type: "proto", name: qualified.name, owner: qualified.owner, returnType, params };
334
417
  }
335
418
  const body = parseBlock();
336
- return { type: "function", name, returnType, params, body };
419
+ return { type: "function", name: qualified.name, owner: qualified.owner, returnType, params, body };
420
+ }
421
+
422
+ function parseStruct() {
423
+ eat("kw");
424
+ const name = at("ident") ? eat("ident").value : "_anon";
425
+ if (at("op", ";")) {
426
+ i += 1;
427
+ return { type: "struct", name, fields: [], methods: [] };
428
+ }
429
+ eat("op", "{");
430
+ const fields = [];
431
+ const methods = [];
432
+ while (!at("eof") && !at("op", "}")) {
433
+ skipSemicolons();
434
+ if (at("op", "}")) {
435
+ break;
436
+ }
437
+ if (at("kw", "public") || at("kw", "private") || at("kw", "protected")) {
438
+ i += 1;
439
+ if (at("op", ":")) {
440
+ i += 1;
441
+ }
442
+ continue;
443
+ }
444
+ if (at("kw", "struct") || at("kw", "class")) {
445
+ const nested = parseStruct();
446
+ if (nested.instance) {
447
+ fields.push({
448
+ type: "decl",
449
+ name: nested.instance.name,
450
+ valueType: nested.name,
451
+ value:
452
+ nested.instance.value || {
453
+ type: "initlist",
454
+ fields: nested.fields.map((field) => ({
455
+ name: field.name,
456
+ value: field.value || { type: "null" },
457
+ })),
458
+ },
459
+ isConst: false,
460
+ owner: name,
461
+ });
462
+ } else {
463
+ fields.push(...nested.fields);
464
+ methods.push(...nested.methods);
465
+ }
466
+ continue;
467
+ }
468
+ const saved = i;
469
+ const isConst = at("kw", "const");
470
+ if (isConst) {
471
+ i += 1;
472
+ }
473
+ const valueType = parseType();
474
+ if (valueType && at("ident")) {
475
+ if (peek(1).value === "(") {
476
+ i = saved;
477
+ const method = parseFunction();
478
+ method.owner = method.owner || name;
479
+ methods.push(method);
480
+ continue;
481
+ }
482
+ const fieldName = eat("ident").value;
483
+ let value = null;
484
+ if (at("op", "=")) {
485
+ i += 1;
486
+ value = parseExpr();
487
+ }
488
+ skipSemicolons();
489
+ fields.push({ type: "decl", name: fieldName, valueType, value, isConst, owner: name });
490
+ continue;
491
+ }
492
+ i = saved;
493
+ skipBalanced();
494
+ }
495
+ eat("op", "}");
496
+ let instance = null;
497
+ if (at("ident")) {
498
+ instance = { name: eat("ident").value, value: null };
499
+ if (at("op", "=")) {
500
+ i += 1;
501
+ instance.value = parseExpr();
502
+ }
503
+ }
504
+ skipSemicolons();
505
+ return { type: "struct", name, fields, methods, instance };
337
506
  }
338
507
 
339
508
  function parseTopLevelDecl(isConst, valueType) {
@@ -414,7 +583,13 @@ function parse(source, fileName) {
414
583
  skipSemicolons();
415
584
  continue;
416
585
  }
417
- if (at("kw", "struct") || at("kw", "class") || at("kw", "enum") || at("kw", "template") || at("kw", "typedef") || at("kw", "extern")) {
586
+ if (at("kw", "struct") || at("kw", "class")) {
587
+ const parsed = parseStruct();
588
+ decls.push(...parsed.fields);
589
+ decls.push(...parsed.methods);
590
+ continue;
591
+ }
592
+ if (at("kw", "enum") || at("kw", "template") || at("kw", "typedef") || at("kw", "extern")) {
418
593
  skipTypeDecl();
419
594
  continue;
420
595
  }
@@ -426,7 +601,7 @@ function parse(source, fileName) {
426
601
  const maybeType = parseType();
427
602
  if (maybeType && at("ident")) {
428
603
  const next = peek(1);
429
- if (next.value === "(") {
604
+ if (next.value === "(" || next.value === "::") {
430
605
  i = saved;
431
606
  decls.push(parseFunction());
432
607
  skipSemicolons();
@@ -438,6 +613,12 @@ function parse(source, fileName) {
438
613
  }
439
614
  }
440
615
  i = saved;
616
+ if (at("ident") && (peek(1).value === "::" || peek(1).value === "(" || peek(1).value === "." || peek(1).value === "->")) {
617
+ const expr = parseExpr();
618
+ skipSemicolons();
619
+ decls.push({ type: "expr", expr });
620
+ continue;
621
+ }
441
622
  decls.push(parseFunction());
442
623
  skipSemicolons();
443
624
  }
package/src/preprocess.js CHANGED
@@ -27,17 +27,75 @@ function isEngineStub(filePath) {
27
27
  );
28
28
  }
29
29
 
30
+ function includeVariants(name) {
31
+ const variants = [name];
32
+ if (/\.h$/i.test(name)) {
33
+ variants.push(name.replace(/\.h$/i, ".hpp"), name.replace(/\.h$/i, ".hh"));
34
+ } else if (/\.hpp$/i.test(name)) {
35
+ variants.push(name.replace(/\.hpp$/i, ".h"), name.replace(/\.hpp$/i, ".hh"));
36
+ } else if (/\.hh$/i.test(name)) {
37
+ variants.push(name.replace(/\.hh$/i, ".h"), name.replace(/\.hh$/i, ".hpp"));
38
+ }
39
+ return [...new Set(variants)];
40
+ }
41
+
42
+ function siblingImplementation(headerPath) {
43
+ if (!headerPath || !isHeaderFile(headerPath)) {
44
+ return null;
45
+ }
46
+ const base = headerPath.replace(/\.(h|hpp|hh)$/i, "");
47
+ for (const ext of [".cpp", ".cc", ".cxx", ".c"]) {
48
+ if (fs.existsSync(base + ext)) {
49
+ return base + ext;
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+
30
55
  function resolveInclude(name, fromFile, includeDirs) {
31
56
  const bases = [path.dirname(fromFile), ...(includeDirs || [])];
32
57
  for (const base of bases) {
33
- const candidate = path.resolve(base, name);
34
- if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
35
- return candidate;
58
+ for (const variant of includeVariants(name)) {
59
+ const candidate = path.resolve(base, variant);
60
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
61
+ return candidate;
62
+ }
36
63
  }
37
64
  }
38
65
  return null;
39
66
  }
40
67
 
68
+ function scanHeaderExports(source) {
69
+ const consts = [];
70
+ const structs = [];
71
+ for (const match of String(source).matchAll(/\bconst\s+(?:int|bool|float|double|auto|string)\s+(\w+)/g)) {
72
+ if (!consts.includes(match[1])) {
73
+ consts.push(match[1]);
74
+ }
75
+ }
76
+ for (const match of String(source).matchAll(/\bstruct\s+(\w+)/g)) {
77
+ if (!structs.includes(match[1])) {
78
+ structs.push(match[1]);
79
+ }
80
+ }
81
+ return { consts, structs };
82
+ }
83
+
84
+ function pushModuleInclude(options, resolved, impl) {
85
+ options.moduleIncludes = options.moduleIncludes || [];
86
+ const srcDir = options.srcDir || null;
87
+ const moduleFile = impl || resolved;
88
+ const rel = srcDir ? path.relative(srcDir, moduleFile).replace(/\\/g, "/") : path.basename(moduleFile);
89
+ const headerText = fs.readFileSync(resolved, "utf8");
90
+ options.moduleIncludes.push({
91
+ name: path.basename(resolved).replace(/\.(h|hpp|hh)$/i, ""),
92
+ outRel: rel.replace(/\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau"),
93
+ header: resolved,
94
+ impl: impl || null,
95
+ exports: scanHeaderExports(headerText),
96
+ });
97
+ }
98
+
41
99
  function preprocess(source, filePath, options = {}) {
42
100
  const seen = options.seen || new Set();
43
101
  const includeDirs = options.includeDirs || [];
@@ -56,6 +114,13 @@ function preprocess(source, filePath, options = {}) {
56
114
  if (!resolved || isEngineStub(resolved) || seen.has(resolved)) {
57
115
  continue;
58
116
  }
117
+ const impl = siblingImplementation(resolved);
118
+ const including = filePath ? path.resolve(filePath) : null;
119
+ const ownHeader = Boolean(impl && including && path.resolve(impl) === including);
120
+ if (!ownHeader) {
121
+ pushModuleInclude(options, resolved, impl);
122
+ continue;
123
+ }
59
124
  seen.add(resolved);
60
125
  const inner = fs.readFileSync(resolved, "utf8");
61
126
  out.push(preprocess(inner, resolved, { ...options, seen }));
@@ -76,5 +141,8 @@ module.exports = {
76
141
  isHeaderFile,
77
142
  toLuauPath,
78
143
  preprocess,
144
+ scanHeaderExports,
79
145
  isEngineStub,
146
+ siblingImplementation,
147
+ resolveInclude,
80
148
  };
@@ -0,0 +1,28 @@
1
+ #include <cluaupp/roblox.hpp>
2
+
3
+ void CreateLeaderstats(Player* player) {
4
+ if (player->FindFirstChild("leaderstats") != nullptr) {
5
+ return;
6
+ }
7
+
8
+ auto* leaderstats = new Folder(player);
9
+ leaderstats->Name = "leaderstats";
10
+
11
+ auto* coins = new IntValue(leaderstats);
12
+ coins->Name = "Coins";
13
+ coins->Value = 0;
14
+
15
+ auto* level = new IntValue(leaderstats);
16
+ level->Name = "Level";
17
+ level->Value = 0;
18
+ }
19
+
20
+ void init() {
21
+ auto* players = GetService<Players>();
22
+
23
+ for (auto* player : players->GetPlayers()) {
24
+ CreateLeaderstats(player);
25
+ }
26
+
27
+ players->PlayerAdded.Connect(CreateLeaderstats);
28
+ }
@@ -0,0 +1,5 @@
1
+ #include "config.h"
2
+
3
+ int startingCoins() {
4
+ return STARTING_COINS;
5
+ }
@@ -0,0 +1,3 @@
1
+ #include <cluaupp/roblox.hpp>
2
+
3
+ const int STARTING_COINS = 0;
package/src/understand.js CHANGED
@@ -94,7 +94,18 @@ const INTENTS = {
94
94
  data: {
95
95
  role: "controller",
96
96
  module: "DataController",
97
- tokens: ["DataStore", "DataStoreService", "DataService", "SetAsync", "GetAsync", "UpdateAsync", "ProfileStore"],
97
+ tokens: [
98
+ "DataStore",
99
+ "DataStoreService",
100
+ "DataService",
101
+ "SetAsync",
102
+ "GetAsync",
103
+ "UpdateAsync",
104
+ "ProfileStore",
105
+ "GetChangedSignal",
106
+ "WaitFor",
107
+ "Paths",
108
+ ],
98
109
  },
99
110
  character: {
100
111
  role: "controller",
@@ -187,19 +198,28 @@ function toPascalServiceName(fileName) {
187
198
 
188
199
  function parseFileTag(fileName) {
189
200
  const base = path.basename(fileName);
201
+ if (new RegExp(`\\.legacy\\.plugin\\.${EXT}$`, "i").test(base)) {
202
+ return { key: "legacy.plugin", emit: "legacy", runtime: "plugin", rojo: "Script", runContext: "Plugin" };
203
+ }
190
204
  if (new RegExp(`\\.legacy\\.server\\.${EXT}$`, "i").test(base)) {
191
- return { key: "legacy.server", emit: "legacy", runtime: "server", rojo: "Script" };
205
+ return { key: "legacy.server", emit: "legacy", runtime: "server", rojo: "Script", runContext: null };
192
206
  }
193
207
  if (new RegExp(`\\.legacy\\.client\\.${EXT}$`, "i").test(base)) {
194
- return { key: "legacy.client", emit: "legacy", runtime: "client", rojo: "LocalScript" };
208
+ return { key: "legacy.client", emit: "legacy", runtime: "client", rojo: "LocalScript", runContext: null };
209
+ }
210
+ if (new RegExp(`\\.legacy\\.${EXT}$`, "i").test(base)) {
211
+ return { key: "legacy", emit: "legacy", runtime: "server", rojo: "Script", runContext: null };
212
+ }
213
+ if (new RegExp(`\\.plugin\\.${EXT}$`, "i").test(base)) {
214
+ return { key: "plugin", emit: "service", runtime: "plugin", rojo: "Script", runContext: "Plugin" };
195
215
  }
196
216
  if (new RegExp(`\\.server\\.${EXT}$`, "i").test(base)) {
197
- return { key: "server", emit: "service", runtime: "server", rojo: "Script" };
217
+ return { key: "server", emit: "service", runtime: "server", rojo: "Script", runContext: "Server" };
198
218
  }
199
219
  if (new RegExp(`\\.client\\.${EXT}$`, "i").test(base)) {
200
- return { key: "client", emit: "service", runtime: "client", rojo: "LocalScript" };
220
+ return { key: "client", emit: "service", runtime: "client", rojo: "LocalScript", runContext: "Client" };
201
221
  }
202
- return { key: "module", emit: "module", runtime: "shared", rojo: "ModuleScript" };
222
+ return { key: "module", emit: "module", runtime: "shared", rojo: "ModuleScript", runContext: null };
203
223
  }
204
224
 
205
225
  function scoreIntents(named) {
@@ -362,9 +382,37 @@ function classifyFunctions(ast, intents) {
362
382
  absorb,
363
383
  });
364
384
  }
385
+ keepReferencedFunctions(ast, classified);
365
386
  return classified;
366
387
  }
367
388
 
389
+ function keepReferencedFunctions(ast, classified) {
390
+ const kept = new Set(["init"]);
391
+ for (const item of classified) {
392
+ if (item.absorb !== "cache") {
393
+ kept.add(item.name);
394
+ }
395
+ }
396
+
397
+ let changed = true;
398
+ while (changed) {
399
+ changed = false;
400
+ for (const decl of ast.body || []) {
401
+ if (decl.type !== "function" || !kept.has(decl.name)) {
402
+ continue;
403
+ }
404
+ const named = namesIn(decl);
405
+ for (const item of classified) {
406
+ if (item.absorb === "cache" && named.has(item.name)) {
407
+ item.absorb = "none";
408
+ kept.add(item.name);
409
+ changed = true;
410
+ }
411
+ }
412
+ }
413
+ }
414
+ }
415
+
368
416
  function analyze(ast, fileName) {
369
417
  const tag = parseFileTag(fileName);
370
418
  const named = namesIn(ast);
@@ -420,6 +468,7 @@ function analyze(ast, fileName) {
420
468
  serviceName,
421
469
  typesName: `${serviceName}Types`,
422
470
  isClient: tag.runtime === "client",
471
+ runContext: tag.runContext || null,
423
472
  intents,
424
473
  primaryDomain,
425
474
  roles,
@@ -432,10 +481,18 @@ function analyze(ast, fileName) {
432
481
  };
433
482
  }
434
483
 
484
+ function modernScriptOutName(relativeName) {
485
+ return String(relativeName)
486
+ .replace(/\\/g, "/")
487
+ .replace(/\.(server|client|plugin)\.(cpp|cc|cxx|c|h|hpp|hh)$/i, ".luau");
488
+ }
489
+
435
490
  function legacyOutName(relativeName) {
436
491
  return String(relativeName)
437
492
  .replace(/\\/g, "/")
493
+ .replace(/\.legacy\.plugin\./i, ".")
438
494
  .replace(/\.legacy\.(server|client)\./i, ".$1.")
495
+ .replace(/\.legacy\./i, ".server.")
439
496
  .replace(new RegExp(`\\.${EXT}$`, "i"), ".luau");
440
497
  }
441
498
 
@@ -449,5 +506,6 @@ module.exports = {
449
506
  scoreIntents,
450
507
  analyze,
451
508
  legacyOutName,
509
+ modernScriptOutName,
452
510
  looksLikeCacheSetup,
453
511
  };
@@ -1,5 +1,16 @@
1
1
  CompileFlags:
2
2
  Add:
3
+ - -xc++
3
4
  - -std=c++20
5
+ - -ferror-limit=0
4
6
  - -Iinclude
5
7
  - -Isrc
8
+
9
+ Diagnostics:
10
+ Suppress: '*'
11
+
12
+ ---
13
+ If:
14
+ PathMatch: (out|libs)/.*
15
+ Index:
16
+ Background: Skip
@@ -2,14 +2,18 @@
2
2
  "configurations": [
3
3
  {
4
4
  "name": "Cluaupp",
5
+ "compilerPath": "C:/Program Files/LLVM/bin/clang++.exe",
5
6
  "cStandard": "c17",
6
7
  "cppStandard": "c++20",
7
- "intelliSenseMode": "windows-gcc-x64",
8
+ "intelliSenseMode": "windows-clang-x64",
8
9
  "includePath": [
9
10
  "${workspaceFolder}/include",
10
11
  "${workspaceFolder}/src"
11
12
  ],
12
- "defines": [],
13
+ "forcedInclude": [
14
+ "${workspaceFolder}/include/cluaupp/roblox.hpp"
15
+ ],
16
+ "compileCommands": "${workspaceFolder}/compile_commands.json",
13
17
  "browse": {
14
18
  "path": [
15
19
  "${workspaceFolder}/include",
@@ -0,0 +1,6 @@
1
+ {
2
+ "recommendations": [
3
+ "ms-vscode.cpptools",
4
+ "kartzdev.cluaupp-intellisense"
5
+ ]
6
+ }
@@ -1,6 +1,9 @@
1
1
  {
2
+ "C_Cpp.intelliSenseEngine": "default",
2
3
  "C_Cpp.default.cppStandard": "c++20",
3
- "C_Cpp.default.intelliSenseMode": "windows-gcc-x64",
4
+ "C_Cpp.default.cStandard": "c17",
5
+ "C_Cpp.default.compilerPath": "C:/Program Files/LLVM/bin/clang++.exe",
6
+ "C_Cpp.default.intelliSenseMode": "windows-clang-x64",
4
7
  "C_Cpp.default.includePath": [
5
8
  "${workspaceFolder}/include",
6
9
  "${workspaceFolder}/src"
@@ -8,5 +11,16 @@
8
11
  "C_Cpp.default.browse.path": [
9
12
  "${workspaceFolder}/include",
10
13
  "${workspaceFolder}/src"
11
- ]
14
+ ],
15
+ "clangd.enable": false,
16
+ "files.associations": {
17
+ "*.hpp": "cpp",
18
+ "*.h": "cpp",
19
+ "*.server.cpp": "cpp",
20
+ "*.client.cpp": "cpp",
21
+ "*.plugin.cpp": "cpp",
22
+ "*.legacy.cpp": "cpp",
23
+ "*.legacy.server.cpp": "cpp",
24
+ "*.legacy.client.cpp": "cpp"
25
+ }
12
26
  }
@@ -1,3 +1,5 @@
1
+ -xc++
1
2
  -std=c++20
3
+ -ferror-limit=0
2
4
  -Iinclude
3
5
  -Isrc
package/docs/libraries.md DELETED
@@ -1,80 +0,0 @@
1
- ---
2
- title: Libraries
3
- sidebar_position: 20
4
- ---
5
-
6
- # Libraries
7
-
8
- Cluaupp ships **full library systems** in `CluauppLibs` (`runtime/` → `libs/` on `cluaupp init` / `build`). These are not typings and not `require(Packages.*)` stubs. `cluaupp build` copies the Luau source. `#include <cluaupp/libs/...>` injects `require(ReplicatedStorage.CluauppLibs.<Name>)`.
9
-
10
- Re-vendor from GitHub:
11
-
12
- ```bash
13
- cd cluau
14
- # clones live in vendor/ (gitignored)
15
- node scripts/vendor-libs.js
16
- ```
17
-
18
- Sources: [runtime/SOURCES.md](https://github.com/kartzDev/cluaupp/blob/main/runtime/SOURCES.md).
19
-
20
- ## Vendored from GitHub
21
-
22
- | Library | Header | GitHub | What you get |
23
- | --- | --- | --- | --- |
24
- | **Janitor** | `<cluaupp/libs/janitor.hpp>` | [howmanysmall/Janitor](https://github.com/howmanysmall/Janitor) | Add / AddPromise / LinkToInstance / Cleanup |
25
- | **Promise** | `<cluaupp/libs/promise.hpp>` | [evaera/roblox-lua-promise](https://github.com/evaera/roblox-lua-promise) | andThen + Cluaupp aliases Then / Catch / Await |
26
- | **Fusion** | `<cluaupp/libs/fusion.hpp>` | [dphfox/Fusion](https://github.com/dphfox/Fusion) | scoped, New, Value, Computed, Spring, Tween |
27
- | **Iris** | `<cluaupp/libs/iris.hpp>` | [SirMallard/Iris](https://github.com/SirMallard/Iris) | Immediate-mode debug UI |
28
- | **Cmdr** | `<cluaupp/libs/cmdr.hpp>` | [evaera/Cmdr](https://github.com/evaera/Cmdr) | Command console (server module + CmdrClient) |
29
- | **TopbarPlus** | `<cluaupp/libs/topbarplus.hpp>` | [1ForeverHD/TopbarPlus](https://github.com/1ForeverHD/TopbarPlus) | `Icon.new`, setLabel, dropdowns |
30
- | **Chrono** | `<cluaupp/libs/chrono.hpp>` | [Parihsz/Chrono](https://github.com/Parihsz/Chrono) | Custom character replication (`Chrono.Start()`) |
31
- | **DataService** | `<cluaupp/libs/dataservice.hpp>` | [KartzRbx/dataservicev2](https://github.com/KartzRbx/dataservicev2) | ProfileStore, QuickNet, Paths, Get vs GetPersisted |
32
- | **EzVisualz** | `<cluaupp/libs/ezvisual.hpp>` | [arxkdev/ezVisualz](https://github.com/arxkdev/ezVisualz) | UIGradient presets (Rainbow, Gold, …) |
33
- | **StateMachine** | `<cluaupp/libs/statemachine.hpp>` | [Prooheckcp/RobloxStateMachine](https://github.com/Prooheckcp/RobloxStateMachine) | States, transitions, LoadDirectory |
34
- | **Spring** | `<cluaupp/libs/spring.hpp>` | [nightcycle/spring](https://github.com/nightcycle/spring) | Damped spring (`new(damping, frequency, position)`) |
35
- | **Display** | `<cluaupp/libs/display.hpp>` | [nightcycle/display](https://github.com/nightcycle/display) | Pretty-print any value (`option` bundled) |
36
- | **Module3D** | `<cluaupp/libs/module3d.hpp>` | [TheNexusAvenger/Module3D](https://github.com/TheNexusAvenger/Module3D) | Model in a ViewportFrame |
37
- | **FormatNumber** | `<cluaupp/libs/formatnumber.hpp>` | [Blockzez/RobloxFormatNumber](https://github.com/Blockzez/RobloxFormatNumber) | ICU-style formatters + Abbreviate / Comma |
38
-
39
- ## Cluaupp originals
40
-
41
- | Library | Header | Role |
42
- | --- | --- | --- |
43
- | **Net** | `<cluaupp/libs/net.hpp>` | Buffer-packed RemoteEvent / RemoteFunction |
44
- | **MathUtils** | `<cluaupp/libs/math.hpp>` | Lerp, Map, Clamp, angles |
45
- | **Twinkle** | `<cluaupp/libs/twinkle.hpp>` | UI fade / slide / zoom / bounce |
46
- | **StickyBillboard** | `<cluaupp/libs/stickybillboard.hpp>` | BillboardGui on an adornee (no public GitHub) |
47
- | **VfxUtil** | `<cluaupp/libs/vfx.hpp>` | Emit / Play / CloneOnto ParticleEmitters |
48
- | **ArrayIndexer** | (types) | `Table<Manifest, "Name">` |
49
- | **Occlude** | (types) | `Keys<Data, "field">` |
50
-
51
- ```cpp
52
- #include <cluaupp/roblox.hpp>
53
- #include <cluaupp/libs/janitor.hpp>
54
- #include <cluaupp/libs/net.hpp>
55
- #include <cluaupp/libs/dataservice.hpp>
56
- #include <cluaupp/libs/formatnumber.hpp>
57
-
58
- void OnCoins(Player* player, int amount) {
59
- auto* stats = player->FindFirstChild("leaderstats");
60
- if (stats == nullptr) {
61
- return;
62
- }
63
- }
64
-
65
- void init() {
66
- auto* janitor = new Janitor();
67
- auto* coins = Net::Event("Coins");
68
- coins->On(OnCoins);
69
- print(FormatNumber::Abbreviate(1500));
70
- DataService::Server.Init(DataServiceOptions {
71
- .Template = { .Coins = 0 },
72
- .StoreName = "PlayerData",
73
- });
74
- janitor->Add(coins);
75
- }
76
- ```
77
-
78
- Wally is **optional**. The game template no longer depends on Wally for these packages. Add Wally only for extra community packages you want beside CluauppLibs.
79
-
80
- Typed Luau borders live in `runtime/<Lib>/init.luau` (Janitor, Promise, Net, MathUtils, FormatNumber, Module3D, Twinkle). Engine classes: [API](https://kartzrbx.github.io/Cluaupp/api/classes/).