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/layout.js ADDED
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+
3
+ const SECTION = {
4
+ api: "API",
5
+ constants: "CONSTANTES",
6
+ variables: "VARIAVEIS",
7
+ types: "TIPAGENS",
8
+ support: "FUNÇÕES DE SUPORTE",
9
+ principal: "FUNÇÕES PRINCIPAIS",
10
+ cleanup: "LIMPEZA",
11
+ returns: "RETORNO",
12
+ };
13
+
14
+ const CLEANUP_FN = /^(OnClose|OnPlayerRemoving|Cleanup|Shutdown|OnDestroy)$/i;
15
+ const PRINCIPAL_FN = /^(init|start|main|setup)/i;
16
+ const ENTRY_FN = /^(init|main)$/i;
17
+
18
+ function isGetService(node) {
19
+ return Boolean(node && node.type === "getService");
20
+ }
21
+
22
+ function isDataServiceAlias(node) {
23
+ let current = node;
24
+ while (current && current.type === "member") {
25
+ if (current.object && current.object.type === "ident" && current.object.name === "DataService") {
26
+ return true;
27
+ }
28
+ current = current.object;
29
+ }
30
+ return false;
31
+ }
32
+
33
+ function isJanitorDecl(decl) {
34
+ if (!decl || decl.type !== "decl") {
35
+ return false;
36
+ }
37
+ const name = String(decl.name || "").toLowerCase();
38
+ const typeName = String(decl.valueType || "").toLowerCase();
39
+ const created = decl.value && decl.value.type === "new" && decl.value.className === "Janitor";
40
+ return name.includes("janitor") || typeName.includes("janitor") || created;
41
+ }
42
+
43
+ function isApiDecl(decl) {
44
+ if (!decl || decl.type !== "decl") {
45
+ return false;
46
+ }
47
+ return isGetService(decl.value) || isDataServiceAlias(decl.value);
48
+ }
49
+
50
+ function classifyDecl(decl) {
51
+ if (!decl || decl.type === "proto") {
52
+ return null;
53
+ }
54
+ if (decl.type === "decl") {
55
+ if (isApiDecl(decl)) {
56
+ return "api";
57
+ }
58
+ if (isJanitorDecl(decl)) {
59
+ return "variables";
60
+ }
61
+ if (decl.isConst) {
62
+ return "constants";
63
+ }
64
+ return "variables";
65
+ }
66
+ if (decl.type === "function") {
67
+ if (CLEANUP_FN.test(decl.name || "")) {
68
+ return "cleanup";
69
+ }
70
+ if (PRINCIPAL_FN.test(decl.name || "")) {
71
+ return "principal";
72
+ }
73
+ return "support";
74
+ }
75
+ return null;
76
+ }
77
+
78
+ function organizeDecls(keep) {
79
+ const groups = {
80
+ api: [],
81
+ constants: [],
82
+ variables: [],
83
+ support: [],
84
+ principal: [],
85
+ cleanup: [],
86
+ };
87
+ for (const decl of keep) {
88
+ const bucket = classifyDecl(decl);
89
+ if (bucket && groups[bucket]) {
90
+ groups[bucket].push(decl);
91
+ }
92
+ }
93
+ return groups;
94
+ }
95
+
96
+ function janitorNames(keep) {
97
+ return (keep || []).filter(isJanitorDecl).map((decl) => decl.name);
98
+ }
99
+
100
+ function emitSection(_title, body) {
101
+ const text = String(body || "").trimEnd();
102
+ if (!text) {
103
+ return "";
104
+ }
105
+ return `${text}\n\n`;
106
+ }
107
+
108
+ function joinBlocks(parts) {
109
+ return parts.filter((part) => Boolean(part && String(part).trim())).join("\n\n");
110
+ }
111
+
112
+ function commentLine(title) {
113
+ return `-- ${title}`;
114
+ }
115
+
116
+ module.exports = {
117
+ SECTION,
118
+ CLEANUP_FN,
119
+ PRINCIPAL_FN,
120
+ ENTRY_FN,
121
+ isJanitorDecl,
122
+ isApiDecl,
123
+ classifyDecl,
124
+ organizeDecls,
125
+ janitorNames,
126
+ emitSection,
127
+ joinBlocks,
128
+ commentLine,
129
+ };
package/src/lex.js CHANGED
@@ -28,6 +28,10 @@ const KEYWORDS = new Set([
28
28
  "typedef",
29
29
  "extern",
30
30
  "enum",
31
+ "switch",
32
+ "case",
33
+ "default",
34
+ "break",
31
35
  ]);
32
36
 
33
37
  function tokenize(source) {
@@ -36,8 +40,8 @@ function tokenize(source) {
36
40
  let line = 1;
37
41
  let col = 1;
38
42
 
39
- const push = (type, value, startLine, startCol) => {
40
- tokens.push({ type, value, line: startLine, col: startCol });
43
+ const push = (type, value, startLine, startCol, start, end) => {
44
+ tokens.push({ type, value, line: startLine, col: startCol, start, end });
41
45
  };
42
46
 
43
47
  while (i < source.length) {
@@ -87,6 +91,7 @@ function tokenize(source) {
87
91
  const startCol = col;
88
92
 
89
93
  if (c === '"') {
94
+ const start = i;
90
95
  i += 1;
91
96
  col += 1;
92
97
  let value = "";
@@ -103,46 +108,49 @@ function tokenize(source) {
103
108
  }
104
109
  i += 1;
105
110
  col += 1;
106
- push("string", value, startLine, startCol);
111
+ push("string", value, startLine, startCol, start, i);
107
112
  continue;
108
113
  }
109
114
 
110
115
  if (/[0-9]/.test(c)) {
116
+ const start = i;
111
117
  let value = "";
112
118
  while (i < source.length && /[0-9.]/.test(source[i])) {
113
119
  value += source[i];
114
120
  i += 1;
115
121
  col += 1;
116
122
  }
117
- push("number", value, startLine, startCol);
123
+ push("number", value, startLine, startCol, start, i);
118
124
  continue;
119
125
  }
120
126
 
121
127
  if (/[A-Za-z_]/.test(c)) {
128
+ const start = i;
122
129
  let value = "";
123
130
  while (i < source.length && /[A-Za-z0-9_]/.test(source[i])) {
124
131
  value += source[i];
125
132
  i += 1;
126
133
  col += 1;
127
134
  }
128
- push(KEYWORDS.has(value) ? "kw" : "ident", value, startLine, startCol);
135
+ push(KEYWORDS.has(value) ? "kw" : "ident", value, startLine, startCol, start, i);
129
136
  continue;
130
137
  }
131
138
 
132
139
  const two = source.slice(i, i + 2);
133
- if (["->", "==", "!=", "<=", ">=", "&&", "||", "::"].includes(two)) {
134
- push("op", two, startLine, startCol);
140
+ const start = i;
141
+ if (["->", "==", "!=", "<=", ">=", "&&", "||", "::", "<<", ">>"].includes(two)) {
142
+ push("op", two, startLine, startCol, start, i + 2);
135
143
  i += 2;
136
144
  col += 2;
137
145
  continue;
138
146
  }
139
147
 
140
- push("op", c, startLine, startCol);
148
+ push("op", c, startLine, startCol, start, i + 1);
141
149
  i += 1;
142
150
  col += 1;
143
151
  }
144
152
 
145
- push("eof", "", line, col);
153
+ push("eof", "", line, col, source.length, source.length);
146
154
  return tokens;
147
155
  }
148
156
 
package/src/libs.js CHANGED
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
 
3
+ const path = require("path");
4
+
3
5
  const TYPE_EXPORTS = {
4
6
  Janitor: "Janitor",
5
7
  Promise: "Promise",
@@ -100,12 +102,12 @@ const LIBRARY_METHODS = new Set([
100
102
  "Remove",
101
103
  "RemoveNoClean",
102
104
  "RemoveList",
105
+ "RemoveListNoClean",
103
106
  "GetAll",
104
107
  "Cleanup",
105
108
  "Destroy",
106
109
  "LinkToInstance",
107
110
  "LinkToInstances",
108
- "Has",
109
111
  "Then",
110
112
  "Catch",
111
113
  "Finally",
@@ -301,42 +303,118 @@ function requireCluauppLib(name) {
301
303
  return `require(ReplicatedStorage.CluauppLibs.${name})`;
302
304
  }
303
305
 
304
- function emitRequires(libraries) {
306
+ function emitRequireParts(libraries) {
307
+ const api = [];
308
+ const types = [];
305
309
  if (!libraries.length) {
306
- return "";
310
+ return { api: "", types: "" };
307
311
  }
308
- const lines = ['local ReplicatedStorage = game:GetService("ReplicatedStorage")'];
312
+ api.push('const ReplicatedStorage = game:GetService("ReplicatedStorage")');
309
313
  for (const spec of libraries) {
310
- lines.push(`local ${spec.bind} = ${requireCluauppLib(spec.file)}`);
314
+ api.push(`const ${spec.bind} = ${requireCluauppLib(spec.file)}`);
311
315
  const exported = TYPE_EXPORTS[spec.bind];
312
316
  if (exported) {
313
- lines.push(`type ${spec.bind} = ${spec.bind}.${exported}`);
317
+ types.push(`type ${spec.bind} = ${spec.bind}.${exported}`);
314
318
  }
315
319
  const extra = EXTRA_TYPE_EXPORTS[spec.bind];
316
320
  if (extra) {
317
321
  for (const [alias, exportedName] of Object.entries(extra)) {
318
- lines.push(`type ${alias} = ${spec.bind}.${exportedName}`);
322
+ types.push(`type ${alias} = ${spec.bind}.${exportedName}`);
319
323
  }
320
324
  }
321
325
  }
322
- return `${lines.join("\n")}\n`;
326
+ return {
327
+ api: `${api.join("\n")}\n`,
328
+ types: types.length ? `${types.join("\n")}\n` : "",
329
+ };
330
+ }
331
+
332
+ function emitRequires(libraries) {
333
+ const { api, types } = emitRequireParts(libraries);
334
+ return [api, types].filter((part) => Boolean(part && part.trim())).join("\n");
323
335
  }
324
336
 
325
337
  function insertRequires(luau, libraries) {
326
- const block = emitRequires(libraries);
338
+ const block = emitRequires(libraries).trimEnd();
327
339
  if (!block) {
328
340
  return luau;
329
341
  }
330
- const marker = "-- Compiled by Cluaupp — C++ × Luau";
331
- const index = luau.indexOf(marker);
332
- if (index === -1) {
342
+ const match = String(luau).match(/^(?:--[^\n]*\n)+/);
343
+ if (!match) {
333
344
  return `${block}\n${luau}`;
334
345
  }
335
- const insertAt = luau.indexOf("\n", index);
336
- if (insertAt === -1) {
337
- return `${luau}\n${block}`;
346
+ const insertAt = match[0].length;
347
+ const rest = luau.slice(insertAt).replace(/^\n*/, "\n");
348
+ return `${luau.slice(0, insertAt)}\n${block}\n${rest}`;
349
+ }
350
+
351
+ function robloxRequireFrom(fromOutRel, toOutRel) {
352
+ const fromDir = path.posix.dirname(String(fromOutRel || "module.luau").replace(/\\/g, "/"));
353
+ const toMod = String(toOutRel || "module.luau")
354
+ .replace(/\\/g, "/")
355
+ .replace(/\.luau$/i, "");
356
+ let rel = path.posix.relative(fromDir, toMod);
357
+ if (!rel || rel === ".") {
358
+ rel = path.posix.basename(toMod);
359
+ }
360
+ const parts = rel.split("/");
361
+ let expr = "script.Parent";
362
+ for (const part of parts) {
363
+ if (part === "..") {
364
+ expr += ".Parent";
365
+ } else if (part && part !== ".") {
366
+ expr += `.${part}`;
367
+ }
368
+ }
369
+ return `require(${expr})`;
370
+ }
371
+
372
+ function moduleIsUsed(luau, spec) {
373
+ if (String(luau).includes(spec.name)) {
374
+ return true;
375
+ }
376
+ const exported = [...((spec.exports && spec.exports.consts) || []), ...((spec.exports && spec.exports.structs) || [])];
377
+ return exported.some((name) => String(luau).includes(name));
378
+ }
379
+
380
+ function insertModuleRequires(luau, modules, fromOutRel) {
381
+ if (!modules || modules.length === 0) {
382
+ return luau;
383
+ }
384
+ const lines = [];
385
+ const seen = new Set();
386
+ for (const spec of modules) {
387
+ if (!spec || !spec.name || seen.has(spec.name)) {
388
+ continue;
389
+ }
390
+ if (fromOutRel && !moduleIsUsed(luau, spec)) {
391
+ continue;
392
+ }
393
+ seen.add(spec.name);
394
+ lines.push(`const ${spec.name} = ${robloxRequireFrom(fromOutRel, spec.outRel)}`);
395
+ for (const name of (spec.exports && spec.exports.consts) || []) {
396
+ if (name !== spec.name) {
397
+ lines.push(`const ${name} = ${spec.name}.${name}`);
398
+ }
399
+ }
400
+ if ((spec.exports && spec.exports.structs || []).includes(spec.name) && !spec.impl) {
401
+ lines.push(`type ${spec.name} = typeof(${spec.name}())`);
402
+ }
403
+ }
404
+ if (lines.length === 0) {
405
+ return luau;
406
+ }
407
+ return insertBlock(luau, lines.join("\n"));
408
+ }
409
+
410
+ function insertBlock(luau, block) {
411
+ const match = String(luau).match(/^(?:--[^\n]*\n)+/);
412
+ if (!match) {
413
+ return `${block}\n${luau}`;
338
414
  }
339
- return `${luau.slice(0, insertAt + 1)}\n${block}${luau.slice(insertAt + 1)}`;
415
+ const insertAt = match[0].length;
416
+ const rest = luau.slice(insertAt).replace(/^\n*/, "\n");
417
+ return `${luau.slice(0, insertAt)}\n${block}\n${rest}`;
340
418
  }
341
419
 
342
420
  module.exports = {
@@ -352,4 +430,5 @@ module.exports = {
352
430
  requireCluauppLib,
353
431
  emitRequires,
354
432
  insertRequires,
433
+ insertModuleRequires,
355
434
  };
package/src/lsp.js ADDED
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+
3
+ const { completeAt, hoverAt, definitionAt, diagnosticsFor } = require("./intellisense");
4
+
5
+ function readMessage(buffer) {
6
+ const headerEnd = buffer.indexOf("\r\n\r\n");
7
+ if (headerEnd < 0) {
8
+ return null;
9
+ }
10
+ const header = buffer.slice(0, headerEnd).toString("utf8");
11
+ const match = header.match(/Content-Length:\s*(\d+)/i);
12
+ if (!match) {
13
+ return null;
14
+ }
15
+ const length = Number(match[1]);
16
+ const start = headerEnd + 4;
17
+ if (buffer.length < start + length) {
18
+ return null;
19
+ }
20
+ const json = buffer.slice(start, start + length).toString("utf8");
21
+ return { message: JSON.parse(json), rest: buffer.slice(start + length) };
22
+ }
23
+
24
+ function send(message) {
25
+ const json = JSON.stringify(message);
26
+ const payload = Buffer.from(json, "utf8");
27
+ process.stdout.write(`Content-Length: ${payload.length}\r\n\r\n`);
28
+ process.stdout.write(payload);
29
+ }
30
+
31
+ function posToOffset(text, position) {
32
+ const lines = text.split(/\n/);
33
+ let offset = 0;
34
+ for (let i = 0; i < position.line; i += 1) {
35
+ offset += (lines[i] || "").length + 1;
36
+ }
37
+ return offset + (position.character || 0);
38
+ }
39
+
40
+ function kindNumber(kind) {
41
+ switch (kind) {
42
+ case "method":
43
+ case "function":
44
+ return 3;
45
+ case "constructor":
46
+ return 4;
47
+ case "property":
48
+ case "variable":
49
+ return 6;
50
+ case "class":
51
+ return 7;
52
+ case "enum":
53
+ return 13;
54
+ case "keyword":
55
+ return 14;
56
+ case "event":
57
+ return 23;
58
+ case "file":
59
+ return 17;
60
+ default:
61
+ return 1;
62
+ }
63
+ }
64
+
65
+ function start(options = {}) {
66
+ const projectRoot = options.projectRoot || process.cwd();
67
+ const documents = new Map();
68
+ let buffer = Buffer.alloc(0);
69
+ let id = 0;
70
+
71
+ const uriPath = (uri) => {
72
+ let file = String(uri || "").replace(/^file:\/\//, "");
73
+ if (/^\/[A-Za-z]:/.test(file)) {
74
+ file = file.slice(1);
75
+ }
76
+ return decodeURIComponent(file);
77
+ };
78
+
79
+ const docText = (uri) => documents.get(uri) || "";
80
+
81
+ const publishDiagnostics = (uri) => {
82
+ const file = uriPath(uri);
83
+ const text = docText(uri);
84
+ const items = diagnosticsFor(text, file, {
85
+ filePath: file,
86
+ projectRoot,
87
+ includeDirs: [pathDirname(file), require("path").join(projectRoot, "src"), require("path").join(projectRoot, "include")],
88
+ });
89
+ send({
90
+ jsonrpc: "2.0",
91
+ method: "textDocument/publishDiagnostics",
92
+ params: {
93
+ uri,
94
+ diagnostics: items.map((item) => ({
95
+ range: {
96
+ start: { line: Math.max(0, (item.line || 1) - 1), character: Math.max(0, (item.col || 1) - 1) },
97
+ end: { line: Math.max(0, (item.line || 1) - 1), character: Math.max(1, item.col || 1) },
98
+ },
99
+ severity: 1,
100
+ source: "cluaupp",
101
+ message: item.message,
102
+ })),
103
+ },
104
+ });
105
+ };
106
+
107
+ const pathDirname = (file) => require("path").dirname(file);
108
+
109
+ const handle = (message) => {
110
+ const { method, params, id: reqId } = message;
111
+ if (method === "initialize") {
112
+ send({
113
+ jsonrpc: "2.0",
114
+ id: reqId,
115
+ result: {
116
+ capabilities: {
117
+ textDocumentSync: 1,
118
+ completionProvider: { triggerCharacters: [".", ":", ">", "<", '"', "/"] },
119
+ hoverProvider: true,
120
+ definitionProvider: true,
121
+ },
122
+ serverInfo: { name: "cluaupp", version: require("../package.json").version },
123
+ },
124
+ });
125
+ return;
126
+ }
127
+ if (method === "initialized" || method === "shutdown") {
128
+ if (reqId !== undefined) {
129
+ send({ jsonrpc: "2.0", id: reqId, result: null });
130
+ }
131
+ return;
132
+ }
133
+ if (method === "exit") {
134
+ process.exit(0);
135
+ }
136
+ if (method === "textDocument/didOpen") {
137
+ documents.set(params.textDocument.uri, params.textDocument.text);
138
+ publishDiagnostics(params.textDocument.uri);
139
+ return;
140
+ }
141
+ if (method === "textDocument/didChange") {
142
+ const last = params.contentChanges[params.contentChanges.length - 1];
143
+ if (last && last.text !== undefined && !last.range) {
144
+ documents.set(params.textDocument.uri, last.text);
145
+ }
146
+ publishDiagnostics(params.textDocument.uri);
147
+ return;
148
+ }
149
+ if (method === "textDocument/didClose") {
150
+ documents.delete(params.textDocument.uri);
151
+ return;
152
+ }
153
+ if (method === "textDocument/completion") {
154
+ const uri = params.textDocument.uri;
155
+ const text = docText(uri);
156
+ const offset = posToOffset(text, params.position);
157
+ const items = completeAt(text, offset, { projectRoot, file: uriPath(uri) });
158
+ send({
159
+ jsonrpc: "2.0",
160
+ id: reqId,
161
+ result: {
162
+ isIncomplete: false,
163
+ items: items.map((item) => ({
164
+ label: item.name,
165
+ kind: kindNumber(item.kind),
166
+ detail: item.detail,
167
+ sortText: `${item.kind === "keyword" ? "2" : "0"}_${item.name}`,
168
+ insertText: item.name,
169
+ })),
170
+ },
171
+ });
172
+ return;
173
+ }
174
+ if (method === "textDocument/hover") {
175
+ const uri = params.textDocument.uri;
176
+ const text = docText(uri);
177
+ const offset = posToOffset(text, params.position);
178
+ const hover = hoverAt(text, offset, { projectRoot, file: uriPath(uri) });
179
+ send({
180
+ jsonrpc: "2.0",
181
+ id: reqId,
182
+ result: hover
183
+ ? { contents: { kind: "markdown", value: `**${hover.name}**\n\n\`${hover.detail || hover.type || ""}\`` } }
184
+ : null,
185
+ });
186
+ return;
187
+ }
188
+ if (method === "textDocument/definition") {
189
+ const uri = params.textDocument.uri;
190
+ const text = docText(uri);
191
+ const offset = posToOffset(text, params.position);
192
+ const def = definitionAt(text, offset, { projectRoot, file: uriPath(uri) });
193
+ if (!def) {
194
+ send({ jsonrpc: "2.0", id: reqId, result: null });
195
+ return;
196
+ }
197
+ send({
198
+ jsonrpc: "2.0",
199
+ id: reqId,
200
+ result: {
201
+ uri: "file:///" + String(def.file).replace(/\\/g, "/"),
202
+ range: {
203
+ start: { line: Math.max(0, (def.line || 1) - 1), character: 0 },
204
+ end: { line: Math.max(0, (def.line || 1) - 1), character: 0 },
205
+ },
206
+ },
207
+ });
208
+ return;
209
+ }
210
+ if (reqId !== undefined) {
211
+ send({ jsonrpc: "2.0", id: reqId, result: null });
212
+ }
213
+ id += 1;
214
+ };
215
+
216
+ process.stdin.on("data", (chunk) => {
217
+ buffer = Buffer.concat([buffer, chunk]);
218
+ let parsed = readMessage(buffer);
219
+ while (parsed) {
220
+ handle(parsed.message);
221
+ buffer = parsed.rest;
222
+ parsed = readMessage(buffer);
223
+ }
224
+ });
225
+ }
226
+
227
+ module.exports = { start };