cluaupp 0.1.4 → 0.2.0

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 (113) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +12 -7
  3. package/bin/cluau.js +1 -1
  4. package/bin/cluaupp.js +1 -1
  5. package/docs/README.md +1 -1
  6. package/docs/architecture.md +52 -77
  7. package/docs/cli.md +29 -9
  8. package/docs/comparison.md +3 -3
  9. package/docs/config.md +6 -6
  10. package/docs/cpp-organization.md +2 -2
  11. package/docs/examples/index.md +2 -0
  12. package/docs/getting-started.md +7 -5
  13. package/docs/intellisense.md +49 -31
  14. package/docs/intro.md +2 -2
  15. package/docs/oop/file-tags.md +16 -17
  16. package/docs/oop/index.md +2 -2
  17. package/docs/oop/modules.md +3 -1
  18. package/docs/oop/services.md +7 -14
  19. package/docs/syntax.md +6 -5
  20. package/editors/vscode/extension.js +97 -146
  21. package/editors/vscode/package.json +6 -6
  22. package/examples/README.md +18 -0
  23. package/examples/game/.clangd +25 -0
  24. package/examples/game/.vscode/c_cpp_properties.json +27 -0
  25. package/examples/game/.vscode/extensions.json +5 -0
  26. package/examples/game/.vscode/settings.json +27 -0
  27. package/examples/game/Packages/.gitkeep +0 -0
  28. package/examples/game/cluaupp.config.json +6 -0
  29. package/examples/game/compile_flags.txt +6 -0
  30. package/examples/game/default.project.json +39 -0
  31. package/examples/game/package.json +9 -0
  32. package/examples/game/rokit.toml +5 -0
  33. package/examples/game/src/client/hud.client.cpp +12 -0
  34. package/{src → examples/game/src}/client/init.client.cpp +1 -0
  35. package/examples/game/src/server/combat.server.cpp +23 -0
  36. package/{src → examples/game/src}/server/leaderstats.server.cpp +1 -0
  37. package/examples/game/wally.toml +11 -0
  38. package/generated/api.js +51 -0
  39. package/generated/architecture.js +473 -0
  40. package/generated/ast.js +2 -0
  41. package/generated/cli.js +191 -0
  42. package/generated/compile.js +115 -0
  43. package/generated/editor-install.js +170 -0
  44. package/generated/emit.js +503 -0
  45. package/generated/emitter/luau-codegen.js +167 -0
  46. package/generated/emitter/translators.js +164 -0
  47. package/generated/headers.js +220 -0
  48. package/generated/index.html +49 -0
  49. package/generated/intellisense.js +277 -0
  50. package/generated/layout.js +112 -0
  51. package/generated/lex.js +146 -0
  52. package/generated/libs.js +484 -0
  53. package/generated/lsp.js +55 -0
  54. package/generated/package-info.js +11 -0
  55. package/generated/parse.js +607 -0
  56. package/generated/parser/collector.js +100 -0
  57. package/generated/parser/index.js +30 -0
  58. package/generated/preprocess.js +181 -0
  59. package/generated/system-understander.js +443 -0
  60. package/generated/transpile.js +66 -0
  61. package/generated/types.js +2 -0
  62. package/generated/understand.js +298 -0
  63. package/generated/utils/process-orchestrator.js +63 -0
  64. package/generated/utils/project.js +491 -0
  65. package/generated/utils/rojo-mapper.js +181 -0
  66. package/generated/utils/safe-paths.js +104 -0
  67. package/package.json +25 -8
  68. package/src/api.ts +53 -0
  69. package/src/{architecture.js → architecture.ts} +80 -33
  70. package/src/ast.ts +37 -0
  71. package/src/cli.ts +214 -0
  72. package/src/compile.ts +118 -0
  73. package/src/editor-install.ts +182 -0
  74. package/src/{emit.js → emit.ts} +5 -6
  75. package/src/emitter/luau-codegen.ts +187 -0
  76. package/src/emitter/translators.ts +168 -0
  77. package/src/headers.ts +233 -0
  78. package/src/intellisense.ts +286 -0
  79. package/src/{layout.js → layout.ts} +114 -129
  80. package/src/{lex.js → lex.ts} +4 -6
  81. package/src/{libs.js → libs.ts} +119 -46
  82. package/src/lsp.ts +60 -0
  83. package/src/package-info.ts +7 -0
  84. package/src/{parse.js → parse.ts} +3 -4
  85. package/src/parser/collector.ts +115 -0
  86. package/src/parser/index.ts +27 -0
  87. package/src/{preprocess.js → preprocess.ts} +64 -32
  88. package/src/system-understander.ts +501 -0
  89. package/src/transpile.ts +64 -0
  90. package/src/tree-sitter-cpp.d.ts +4 -0
  91. package/src/types.ts +81 -0
  92. package/src/{understand.js → understand.ts} +24 -213
  93. package/src/utils/process-orchestrator.ts +72 -0
  94. package/src/utils/project.ts +506 -0
  95. package/src/utils/rojo-mapper.ts +190 -0
  96. package/src/utils/safe-paths.ts +98 -0
  97. package/templates/game/.clangd +9 -0
  98. package/templates/game/.vscode/c_cpp_properties.json +1 -1
  99. package/templates/game/.vscode/extensions.json +5 -6
  100. package/templates/game/.vscode/settings.json +7 -6
  101. package/templates/game/cluaupp.config.json +2 -2
  102. package/templates/game/compile_flags.txt +1 -0
  103. package/templates/game/rokit.toml +5 -0
  104. package/templates/game/src/client/init.client.cpp +1 -0
  105. package/templates/game/src/server/leaderstats.server.cpp +1 -0
  106. package/src/api.js +0 -57
  107. package/src/cli.js +0 -481
  108. package/src/compile.js +0 -56
  109. package/src/editor-install.js +0 -218
  110. package/src/intellisense.js +0 -1368
  111. package/src/lsp.js +0 -227
  112. /package/{src → examples/game/src}/shared/config.cpp +0 -0
  113. /package/{src → examples/game/src}/shared/config.h +0 -0
@@ -16,34 +16,33 @@ The **filename** decides the Roblox instance. This is the same key idea as roblo
16
16
  | `Boot.legacy.server.cpp` | Script | **Legacy** |
17
17
  | `Boot.legacy.client.cpp` | LocalScript | Legacy |
18
18
  | `Damage.cpp` (no tag) | **ModuleScript** | — |
19
+ | `LeaderstatsServer.h` | **ModuleScript** (types) | — |
20
+ | `LeaderstatsServer.cpp` (sibling of `.h`) | **ModuleScript** (`*Impl`) | — |
19
21
 
20
- ## Server is not Legacy
22
+ ## One file in, one file out
21
23
 
22
- `.server.cpp` is **not** `init.server.luau`. Rojo maps `*.server.luau` to a Script with RunContext **Legacy**. Cluaupp emits:
24
+ Rojo infers the instance from the filename key: `*.server.luau` is a Script, `*.client.luau` is a LocalScript, untagged `.luau` is a ModuleScript.
23
25
 
24
26
  ```
25
- out/server/LeaderstatsServer/
26
- init.luau
27
- init.meta.json -- className Script, RunContext Server
27
+ src/server/boot/DataBoot.server.cpp → out/server/boot/DataBoot.server.luau
28
+ src/client/boot/DataBoot.client.cpp → out/client/boot/DataBoot.client.luau
29
+ src/shared/damage.cpp → out/shared/Damage.luau
30
+ src/server/services/leaderstats/LeaderstatsServer.h → LeaderstatsServer.luau (export type)
31
+ src/server/services/leaderstats/LeaderstatsServer.cpp → LeaderstatsServerImpl.luau
28
32
  ```
29
33
 
30
- `.legacy.server.cpp` is the 1:1 dump that *does* become Legacy.
31
-
32
- `.client.cpp` stays `init.client.luau` (LocalScript). Do not add `init.meta.json` on the client boot.
34
+ `.legacy.server.cpp` still emits a Legacy Script (`boot.server.luau`). `.client.cpp` stays a LocalScript. Do not invent `init.meta.json` on the default 1:1 path.
33
35
 
34
36
  ## Where files live
35
37
 
36
38
  ```
37
- src/server/.../*.server.cpp → out/server (ServerScriptService)
38
- src/client/.../*.client.cpp → out/client (StarterPlayerScripts)
39
- src/shared/.../*.cpp → out/shared (ReplicatedStorage)
39
+ src/server/.../*.server.cpp → out/server (ServerScriptService.Cluaupp)
40
+ src/client/.../*.client.cpp → out/client (StarterPlayerScripts.Cluaupp)
41
+ src/shared/.../*.cpp → out/shared (ReplicatedStorage.Cluaupp)
40
42
  ```
41
43
 
42
- Name the **system**, not `init.server.cpp`. `LeaderstatsServer.server.cpp` and `DataBoot.server.cpp` are two services.
43
-
44
- ## Skip the planner
44
+ Name the **system**, not `init.server.cpp`. `LeaderstatsServer.server.cpp` and `DataBoot.server.cpp` are two files.
45
45
 
46
- - `"architecture": false` in `cluaupp.config.json`, or
47
- - `.legacy.server.cpp` / `.legacy.client.cpp` per file
46
+ ## Opt-in planner
48
47
 
49
- Use legacy only when you want one Luau file with no Main / Controller split.
48
+ `"architecture": true` in `cluaupp.config.json` restores PascalCase service folders (`Main`, Controller, Types). Default is off.
package/docs/oop/index.md CHANGED
@@ -11,12 +11,12 @@ The **live site** (not just these markdown files) is GitHub Pages:
11
11
  - [Guide → OOP](https://kartzrbx.github.io/Cluaupp/guide/oop.html)
12
12
  - [Examples](https://kartzrbx.github.io/Cluaupp/guide/examples.html)
13
13
 
14
- Cluaupp does **not** compile custom C++ `class` types yet. “OOP” here is how you **lay out systems** so Studio gets services, modules, and lifecycle — the same idea as roblox-ts filename keys + Flamework `@Service`, without decorators.
14
+ Cluaupp does **not** compile custom C++ `class` types yet. “OOP” here is how you **lay out systems** so Studio gets Script / LocalScript / ModuleScript from the filename tag — the same idea as roblox-ts. Flamework-style Start/Stop folders are opt-in (`"architecture": true`).
15
15
 
16
16
  | Page | What you learn |
17
17
  | --- | --- |
18
18
  | [File tags](file-tags.md) | `.server` / `.client` / `.plugin` / `.legacy` / untagged |
19
- | [Services](services.md) | One `.server.cpp` → Main, Controller, Start / Stop |
19
+ | [Services](services.md) | One `.server.cpp` → one `.server.luau` |
20
20
  | [Modules](modules.md) | Untagged files, structs, named functions as methods |
21
21
 
22
22
  Planner details: [Architecture](../architecture.md). Folder layout: [Organization](../cpp-organization.md). Full services: [Examples](../examples/index.md).
@@ -25,7 +25,9 @@ int DoubleCoins(int coins) {
25
25
  out/shared/util/Coins.luau -- ModuleScript, exported functions
26
26
  ```
27
27
 
28
- Shared **data and constants** belong in headers (`#include "TemplateData.hpp"`) they are inlined. Untagged `.cpp` is for a ModuleScript other Luau can `require`. Prefer headers for data and tagged `.server.cpp` / `.client.cpp` for behavior.
28
+ Quoted `#include "TemplateData.hpp"` is not inlined when the header has a sibling `.cpp` or is a shared module: `cluaupp build` emits `require(ReplicatedStorage.Cluaupp.constants.TemplateData)` / `require(ServerScriptService.Cluaupp.configurations.PlayerDataVersion)` (Rojo roots from `default.project.json`), never `script.Parent.Parent.shared`.
29
+
30
+ `.h` / `.hpp` are **type modules**. A service header becomes `export type Name = { init: (self: Name) -> (), … }` and a typed table. The sibling `.cpp` is the construction (`NameImpl.luau`); the header `require`s that impl and binds the functions. Data-only structs still emit a constructor so `TemplateData()` works. Untagged `.cpp` without a header is a ModuleScript other Luau can `require`. Prefer headers for the public type, tagged `.server.cpp` / `.client.cpp` for scripts, and the sibling `.cpp` for method bodies.
29
31
 
30
32
  ## Structs = data, not classes
31
33
 
@@ -5,7 +5,7 @@ sidebar_position: 3
5
5
 
6
6
  # Services
7
7
 
8
- Write **one tagged file per system**. Cluaupp reads the AST and emits a PascalCase folder: `Main`, managers, controllers, `*Types`.
8
+ Write **one tagged file per system**. Cluaupp emits **one** Luau instance: `LeaderstatsServer.server.luau` from `LeaderstatsServer.server.cpp`. It does not invent Main / Controller / Types unless `"architecture": true`.
9
9
 
10
10
  ## Example: leaderstats
11
11
 
@@ -44,29 +44,22 @@ void init() {
44
44
  Typical output:
45
45
 
46
46
  ```
47
- out/server/LeaderstatsServer/
48
- init.luau -- Script, RunContext Server
49
- init.meta.json
50
- Main.luau -- Start / Stop
51
- PlayersManager.luau -- PlayerAdded + Janitor
52
- DataController.luau -- your WaitFor / GetChangedSignal
53
- CacheController.luau -- if you create IntValue folders
54
- LeaderstatsServerTypes.luau
47
+ out/server/services/leaderstats/LeaderstatsServer.server.luau
55
48
  ```
56
49
 
57
- Your functions stay in the matching controller. The planner does not drop `SetupPlayerManager` because `init` still calls it.
50
+ Your functions stay in that file. `init()` runs at the end. There is no fake `Main` that only forwards to a Controller.
58
51
 
59
52
  ## Lifecycle
60
53
 
61
- Generated `Main` exposes `Start` / `Stop`. Managers own a Janitor. `Stop` cleans connections.
54
+ You own `init` and Janitor cleanup in the same file. You do not write `class LeaderstatsServer` name the **file** `LeaderstatsServer.server.cpp` and keep **functions** as the public API.
62
55
 
63
- You do not write `class LeaderstatsServer` name the **file** `LeaderstatsServer.server.cpp` and keep **functions** as the public API.
56
+ `"architecture": true` is the old ForeverHD split (`Main.Start` / `Main.Stop`, Managers, Types). Leave it off unless you want that.
64
57
 
65
58
  ## Boot vs gameplay
66
59
 
67
60
  Keep DataService `Init` in `DataBoot.server.cpp` / `DataBoot.client.cpp`. Keep leaderstats / combat in their own `.server.cpp`. Services `WaitFor` after boot has run.
68
61
 
69
- ## What becomes which role
62
+ ## Opt-in roles (`"architecture": true`)
70
63
 
71
64
  | Your C++ | Generated role |
72
65
  | --- | --- |
@@ -78,6 +71,6 @@ Keep DataService `Init` in `DataBoot.server.cpp` / `DataBoot.client.cpp`. Keep l
78
71
  | `UserInputService` | `InputController` |
79
72
  | wiring | `Main.Start` / `Main.Stop` |
80
73
 
81
- A tiny `print` in `init.client.cpp` stays a single LocalScript. The planner does not invent Managers for hello-world.
74
+ A tiny `print` in `init.client.cpp` stays a single LocalScript.
82
75
 
83
76
  Copy-paste: [Examples](../examples/index.md).
package/docs/syntax.md CHANGED
@@ -2,15 +2,16 @@
2
2
 
3
3
  Cluaupp is not a full C++ compiler. It is a **subset** aimed at Roblox scripts, in the same spirit as roblox-ts (restricted TypeScript → Luau).
4
4
 
5
- Generated Luau follows the current language: [`--!strict`](https://luau.org/getting-started), `local`, `const`, and `const function`. Injected `require` / `GetService` lines are `const`.
5
+ Generated Luau follows the current language: [`local`](https://luau.org/getting-started), `const`, and `const function`. Injected `require` / `GetService` lines are `const`. `--!strict` is emitted only when the file has `#pragma strict` (or `"strict": true` in config).
6
6
 
7
7
  ## Files
8
8
 
9
9
  - Extensions: `.cpp`, `.h`, `.hpp` (also `.cc`, `.hh`)
10
- - Quoted `#include "file.h"` is inlined (C preprocessor)
10
+ - Quoted `#include "file.h"` becomes a Rojo `require` (or is inlined for a header that belongs to the same `.cpp`)
11
11
  - `#include <cluaupp/roblox.hpp>` and `<cluaupp/libs/*.hpp>` are IntelliSense only; library headers also inject `require(CluauppLibs.*)`
12
- - Other `#` lines (`#pragma once`) are ignored
13
- - Function prototypes (`void foo();`) are skipped; only functions with a body are emitted
12
+ - `#pragma once` is ignored
13
+ - `#pragma strict` / `#pragma nstrict` control `--!strict` for that compilation unit
14
+ - Function prototypes in a `.h` / `.hpp` become `export type` fields (`init: (self: Name) -> ()`). Only `.cpp` files emit function bodies.
14
15
 
15
16
  ## Functions
16
17
 
@@ -199,6 +200,6 @@ Full table and IntelliSense notes: [print and cout](print-cout.md).
199
200
 
200
201
  ## Not supported yet
201
202
 
202
- Custom C++ classes, generic templates besides `GetService<T>`, pointer arithmetic, `std::`, overloading, macros (except skipping `#` lines).
203
+ Custom C++ classes, generic templates besides `GetService<T>`, pointer arithmetic, `std::`, overloading, macros (except `#pragma strict` / `#pragma nstrict` / `#pragma once`).
203
204
 
204
205
  If you need one of those patterns, open an issue with the C++ and the expected Luau.
@@ -1,146 +1,97 @@
1
- "use strict";
2
-
3
- const fs = require("fs");
4
- const path = require("path");
5
- const vscode = require("vscode");
6
-
7
- function loadEngine(context) {
8
- const folders = vscode.workspace.workspaceFolders || [];
9
- const workspaceFolder = folders[0] ? folders[0].uri.fsPath : null;
10
- const rootFile = path.join(context.extensionPath, "cluaupp.root");
11
- const roots = [];
12
- if (fs.existsSync(rootFile)) {
13
- roots.push(fs.readFileSync(rootFile, "utf8").trim());
14
- }
15
- if (workspaceFolder) {
16
- roots.push(path.join(workspaceFolder, "..", "Cluaupp"));
17
- roots.push(path.join(workspaceFolder, "node_modules", "cluaupp"));
18
- }
19
- roots.push(path.join(context.extensionPath, "..", ".."));
20
- for (const root of roots) {
21
- const engine = path.join(root, "src", "intellisense.js");
22
- if (fs.existsSync(engine)) {
23
- return { engine: require(engine), root, workspaceFolder };
24
- }
25
- }
26
- throw new Error("Cluaupp IntelliSense engine not found. Run `cluaupp intellisense` in the game folder.");
27
- }
28
-
29
- function kindOf(kind) {
30
- const map = {
31
- method: vscode.CompletionItemKind.Method,
32
- function: vscode.CompletionItemKind.Function,
33
- constructor: vscode.CompletionItemKind.Constructor,
34
- property: vscode.CompletionItemKind.Property,
35
- variable: vscode.CompletionItemKind.Variable,
36
- class: vscode.CompletionItemKind.Class,
37
- enum: vscode.CompletionItemKind.Enum,
38
- keyword: vscode.CompletionItemKind.Keyword,
39
- event: vscode.CompletionItemKind.Event,
40
- file: vscode.CompletionItemKind.File,
41
- };
42
- return map[kind] || vscode.CompletionItemKind.Text;
43
- }
44
-
45
- function activate(context) {
46
- let loaded;
47
- try {
48
- loaded = loadEngine(context);
49
- } catch (err) {
50
- vscode.window.showWarningMessage(String(err.message || err));
51
- return;
52
- }
53
-
54
- const selector = { language: "cpp", scheme: "file" };
55
- const diagnostics = vscode.languages.createDiagnosticCollection("cluaupp");
56
- context.subscriptions.push(diagnostics);
57
-
58
- const optionsFor = (document) => ({
59
- projectRoot: loaded.workspaceFolder || path.dirname(document.uri.fsPath),
60
- file: document.uri.fsPath,
61
- filePath: document.uri.fsPath,
62
- includeDirs: [
63
- path.dirname(document.uri.fsPath),
64
- path.join(loaded.workspaceFolder || "", "src"),
65
- path.join(loaded.workspaceFolder || "", "include"),
66
- ],
67
- });
68
-
69
- const refreshDiagnostics = (document) => {
70
- if (!document || document.languageId !== "cpp") {
71
- return;
72
- }
73
- const items = loaded.engine.diagnosticsFor(document.getText(), document.uri.fsPath, optionsFor(document));
74
- diagnostics.set(
75
- document.uri,
76
- items.map((item) => {
77
- const line = Math.max(0, (item.line || 1) - 1);
78
- const col = Math.max(0, (item.col || 1) - 1);
79
- const range = new vscode.Range(line, col, line, col + 1);
80
- return new vscode.Diagnostic(range, item.message, vscode.DiagnosticSeverity.Error);
81
- }),
82
- );
83
- };
84
-
85
- context.subscriptions.push(
86
- vscode.languages.registerCompletionItemProvider(
87
- selector,
88
- {
89
- provideCompletionItems(document, position) {
90
- const offset = document.offsetAt(position);
91
- const items = loaded.engine.completeAt(document.getText(), offset, optionsFor(document));
92
- return items.map((item) => {
93
- const completion = new vscode.CompletionItem(item.name, kindOf(item.kind));
94
- completion.detail = item.detail || item.type || "";
95
- completion.sortText = `${item.kind === "keyword" ? "2" : "0"}_${item.name}`;
96
- completion.insertText = item.name;
97
- return completion;
98
- });
99
- },
100
- },
101
- ".",
102
- ":",
103
- ">",
104
- "<",
105
- '"',
106
- "/",
107
- ),
108
- vscode.languages.registerHoverProvider(selector, {
109
- provideHover(document, position) {
110
- const hover = loaded.engine.hoverAt(document.getText(), document.offsetAt(position), optionsFor(document));
111
- if (!hover) {
112
- return null;
113
- }
114
- return new vscode.Hover(new vscode.MarkdownString(`**${hover.name}**\n\n\`${hover.detail || hover.type || ""}\``));
115
- },
116
- }),
117
- vscode.languages.registerDefinitionProvider(selector, {
118
- provideDefinition(document, position) {
119
- const def = loaded.engine.definitionAt(document.getText(), document.offsetAt(position), optionsFor(document));
120
- if (!def || !def.file) {
121
- return null;
122
- }
123
- return new vscode.Location(vscode.Uri.file(def.file), new vscode.Position(Math.max(0, (def.line || 1) - 1), 0));
124
- },
125
- }),
126
- vscode.workspace.onDidChangeTextDocument((event) => refreshDiagnostics(event.document)),
127
- vscode.workspace.onDidOpenTextDocument(refreshDiagnostics),
128
- vscode.commands.registerCommand("cluaupp.restartIntelliSense", () => {
129
- try {
130
- delete require.cache[require.resolve(path.join(loaded.root, "src", "intellisense.js"))];
131
- loaded = loadEngine(context);
132
- vscode.window.showInformationMessage("Cluaupp IntelliSense reloaded.");
133
- } catch (err) {
134
- vscode.window.showErrorMessage(String(err.message || err));
135
- }
136
- }),
137
- );
138
-
139
- for (const document of vscode.workspace.textDocuments) {
140
- refreshDiagnostics(document);
141
- }
142
- }
143
-
144
- function deactivate() {}
145
-
146
- module.exports = { activate, deactivate };
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const vscode = require("vscode");
6
+
7
+ function loadEngine(context) {
8
+ const folders = vscode.workspace.workspaceFolders || [];
9
+ const workspaceFolder = folders[0] ? folders[0].uri.fsPath : null;
10
+ const rootFile = path.join(context.extensionPath, "cluaupp.root");
11
+ const roots = [];
12
+ if (fs.existsSync(rootFile)) {
13
+ roots.push(fs.readFileSync(rootFile, "utf8").trim());
14
+ }
15
+ if (workspaceFolder) {
16
+ roots.push(path.join(workspaceFolder, "..", "Cluaupp"));
17
+ roots.push(path.join(workspaceFolder, "node_modules", "cluaupp"));
18
+ }
19
+ roots.push(path.join(context.extensionPath, "..", ".."));
20
+ for (const root of roots) {
21
+ const candidates = [
22
+ path.join(root, "generated", "intellisense.js"),
23
+ path.join(root, "src", "intellisense.js"),
24
+ ];
25
+ for (const engine of candidates) {
26
+ if (fs.existsSync(engine)) {
27
+ return { engine: require(engine), root, workspaceFolder, enginePath: engine };
28
+ }
29
+ }
30
+ }
31
+ throw new Error("Cluaupp diagnostics engine not found. Run `cluaupp intellisense` in the game folder.");
32
+ }
33
+
34
+ function activate(context) {
35
+ let loaded;
36
+ try {
37
+ loaded = loadEngine(context);
38
+ } catch (err) {
39
+ vscode.window.showWarningMessage(String(err.message || err));
40
+ return;
41
+ }
42
+
43
+ const diagnostics = vscode.languages.createDiagnosticCollection("cluaupp");
44
+ context.subscriptions.push(diagnostics);
45
+
46
+ const optionsFor = (document) => ({
47
+ projectRoot: loaded.workspaceFolder || path.dirname(document.uri.fsPath),
48
+ file: document.uri.fsPath,
49
+ filePath: document.uri.fsPath,
50
+ includeDirs: [
51
+ path.dirname(document.uri.fsPath),
52
+ path.join(loaded.workspaceFolder || "", "src"),
53
+ path.join(loaded.workspaceFolder || "", "include"),
54
+ ],
55
+ });
56
+
57
+ const refreshDiagnostics = (document) => {
58
+ if (!document || document.languageId !== "cpp") {
59
+ return;
60
+ }
61
+ if (typeof loaded.engine.diagnosticsFor !== "function") {
62
+ return;
63
+ }
64
+ const items = loaded.engine.diagnosticsFor(document.getText(), document.uri.fsPath, optionsFor(document));
65
+ diagnostics.set(
66
+ document.uri,
67
+ items.map((item) => {
68
+ const line = Math.max(0, (item.line || 1) - 1);
69
+ const col = Math.max(0, (item.col || 1) - 1);
70
+ const range = new vscode.Range(line, col, line, col + 1);
71
+ return new vscode.Diagnostic(range, item.message, vscode.DiagnosticSeverity.Error);
72
+ }),
73
+ );
74
+ };
75
+
76
+ context.subscriptions.push(
77
+ vscode.workspace.onDidChangeTextDocument((event) => refreshDiagnostics(event.document)),
78
+ vscode.workspace.onDidOpenTextDocument(refreshDiagnostics),
79
+ vscode.commands.registerCommand("cluaupp.restartIntelliSense", () => {
80
+ try {
81
+ delete require.cache[require.resolve(loaded.enginePath)];
82
+ loaded = loadEngine(context);
83
+ vscode.window.showInformationMessage("Cluaupp diagnostics reloaded. C++ completion is clangd.");
84
+ } catch (err) {
85
+ vscode.window.showErrorMessage(String(err.message || err));
86
+ }
87
+ }),
88
+ );
89
+
90
+ for (const document of vscode.workspace.textDocuments) {
91
+ refreshDiagnostics(document);
92
+ }
93
+ }
94
+
95
+ function deactivate() {}
96
+
97
+ module.exports = { activate, deactivate };
@@ -1,14 +1,14 @@
1
1
  {
2
- "name": "cluaupp-intellisense",
3
- "displayName": "Cluaupp IntelliSense",
4
- "description": "Completions, hover, and go-to-definition for Cluaupp C++ (Roblox APIs, DataService paths, project structs).",
5
- "version": "0.1.4",
2
+ "name": "cluaupp-diagnostics",
3
+ "displayName": "Cluaupp",
4
+ "description": "Cluaupp subset parse errors. C++ completion, hover, and go-to-definition come from clangd + include/cluaupp/roblox.hpp.",
5
+ "version": "0.2.0",
6
6
  "publisher": "kartzdev",
7
7
  "engines": {
8
8
  "vscode": "^1.74.0"
9
9
  },
10
10
  "categories": [
11
- "Programming Languages"
11
+ "Linters"
12
12
  ],
13
13
  "activationEvents": [
14
14
  "onLanguage:cpp"
@@ -18,7 +18,7 @@
18
18
  "commands": [
19
19
  {
20
20
  "command": "cluaupp.restartIntelliSense",
21
- "title": "Cluaupp: Restart IntelliSense"
21
+ "title": "Cluaupp: Reload subset diagnostics"
22
22
  }
23
23
  ]
24
24
  }
@@ -0,0 +1,18 @@
1
+ # Sample game
2
+
3
+ This is the C++ a Cluaupp game looks like — **not** the compiler.
4
+
5
+ | Folder | Rojo | Tag |
6
+ | --- | --- | --- |
7
+ | `src/server/*.server.cpp` | Script | server |
8
+ | `src/client/*.client.cpp` | LocalScript | client |
9
+ | `src/shared/*` | ModuleScript | none |
10
+
11
+ `src/` of **this repo** is the TypeScript CLI ([roblox-ts](https://github.com/roblox-ts/roblox-ts) does the same). The sample scripts live here so they never mix with `understand.ts`.
12
+
13
+ ```bash
14
+ cd examples/game
15
+ npx cluaupp build .
16
+ ```
17
+
18
+ `cluaupp init` copies `templates/game` (the slim scaffold). This folder adds combat / HUD so `SystemUnderstander` has real tokens to score.
@@ -0,0 +1,25 @@
1
+ CompileFlags:
2
+ CompilationDatabase: .
3
+ Add:
4
+ - -xc++
5
+ - -std=c++20
6
+ - -ferror-limit=0
7
+ - -Iinclude
8
+ - -Isrc
9
+ - -I../../include
10
+
11
+ Diagnostics:
12
+ Suppress: '*'
13
+
14
+ ---
15
+ If:
16
+ PathMatch: .*\.(cpp|cc|cxx|c|h|hpp|hh)$
17
+ CompileFlags:
18
+ Add:
19
+ - -xc++
20
+
21
+ ---
22
+ If:
23
+ PathMatch: (out|libs)/.*
24
+ Index:
25
+ Background: Skip
@@ -0,0 +1,27 @@
1
+ {
2
+ "configurations": [
3
+ {
4
+ "name": "Cluaupp",
5
+ "compilerPath": "clang++",
6
+ "cStandard": "c17",
7
+ "cppStandard": "c++20",
8
+ "intelliSenseMode": "windows-clang-x64",
9
+ "includePath": [
10
+ "${workspaceFolder}/include",
11
+ "${workspaceFolder}/src"
12
+ ],
13
+ "forcedInclude": [
14
+ "${workspaceFolder}/include/cluaupp/roblox.hpp"
15
+ ],
16
+ "compileCommands": "${workspaceFolder}/compile_commands.json",
17
+ "browse": {
18
+ "path": [
19
+ "${workspaceFolder}/include",
20
+ "${workspaceFolder}/src"
21
+ ],
22
+ "limitSymbolsToIncludedHeaders": true
23
+ }
24
+ }
25
+ ],
26
+ "version": 4
27
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "recommendations": [
3
+ "llvm-vs-code-extensions.vscode-clangd"
4
+ ]
5
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "C_Cpp.intelliSenseEngine": "Disabled",
3
+ "C_Cpp.default.cppStandard": "c++20",
4
+ "C_Cpp.default.cStandard": "c17",
5
+ "C_Cpp.default.compilerPath": "clang++",
6
+ "C_Cpp.default.intelliSenseMode": "windows-clang-x64",
7
+ "C_Cpp.default.includePath": [
8
+ "${workspaceFolder}/include",
9
+ "${workspaceFolder}/src"
10
+ ],
11
+ "clangd.enable": true,
12
+ "clangd.arguments": [
13
+ "--compile-commands-dir=${workspaceFolder}",
14
+ "--header-insertion=never",
15
+ "--query-driver=**/clang++*,**/clang++.exe,**/g++*,**/g++.exe"
16
+ ],
17
+ "files.associations": {
18
+ "*.hpp": "cpp",
19
+ "*.h": "cpp",
20
+ "*.server.cpp": "cpp",
21
+ "*.client.cpp": "cpp",
22
+ "*.plugin.cpp": "cpp",
23
+ "*.legacy.cpp": "cpp",
24
+ "*.legacy.server.cpp": "cpp",
25
+ "*.legacy.client.cpp": "cpp"
26
+ }
27
+ }
File without changes
@@ -0,0 +1,6 @@
1
+ {
2
+ "rootDir": "src",
3
+ "outDir": "out",
4
+ "strict": false,
5
+ "architecture": false
6
+ }
@@ -0,0 +1,6 @@
1
+ -xc++
2
+ -std=c++20
3
+ -ferror-limit=0
4
+ -Iinclude
5
+ -Isrc
6
+ -I../../include
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "cluaupp-game",
3
+ "tree": {
4
+ "$className": "DataModel",
5
+ "ServerScriptService": {
6
+ "$className": "ServerScriptService",
7
+ "Cluaupp": {
8
+ "$path": "out/server"
9
+ }
10
+ },
11
+ "ReplicatedStorage": {
12
+ "$className": "ReplicatedStorage",
13
+ "Cluaupp": {
14
+ "$path": "out/shared"
15
+ },
16
+ "CluauppLibs": {
17
+ "$path": "libs"
18
+ },
19
+ "Packages": {
20
+ "$path": "Packages"
21
+ }
22
+ },
23
+ "StarterPlayer": {
24
+ "$className": "StarterPlayer",
25
+ "StarterPlayerScripts": {
26
+ "$className": "StarterPlayerScripts",
27
+ "Cluaupp": {
28
+ "$path": "out/client"
29
+ }
30
+ }
31
+ },
32
+ "HttpService": {
33
+ "$className": "HttpService",
34
+ "$properties": {
35
+ "HttpEnabled": true
36
+ }
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "cluaupp-game",
3
+ "private": true,
4
+ "scripts": {
5
+ "build": "cluaupp build",
6
+ "watch": "cluaupp watch",
7
+ "dev": "cluaupp watch"
8
+ }
9
+ }
@@ -0,0 +1,5 @@
1
+ # This file lists tools managed by Rokit, a toolchain manager for Roblox projects.
2
+ # https://github.com/rojo-rbx/rokit
3
+
4
+ [tools]
5
+ rojo = "rojo-rbx/rojo@7.7.0"
@@ -0,0 +1,12 @@
1
+ #pragma strict
2
+ #include <cluaupp/roblox.hpp>
3
+
4
+ void init() {
5
+ auto* playerGui = GetService<Players>()->LocalPlayer->FindFirstChild("PlayerGui");
6
+ auto* screen = new ScreenGui();
7
+ screen->Name = "Hud";
8
+ auto* coins = new TextLabel();
9
+ coins->Name = "Coins";
10
+ coins->Parent = screen;
11
+ print("hud ready");
12
+ }
@@ -1,3 +1,4 @@
1
+ #pragma strict
1
2
  #include <cluaupp/roblox.hpp>
2
3
 
3
4
  void init() {