@velarscript/cli 0.12.0 → 0.13.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 (140) hide show
  1. package/README.md +4 -12
  2. package/dist/browser-process-owner.d.ts +23 -0
  3. package/dist/browser-process-owner.d.ts.map +1 -1
  4. package/dist/browser-process-owner.js +46 -2
  5. package/dist/browser-process-owner.js.map +1 -1
  6. package/dist/browser-test-runner.d.ts.map +1 -1
  7. package/dist/browser-test-runner.js +110 -14
  8. package/dist/browser-test-runner.js.map +1 -1
  9. package/dist/cli.js +131 -68
  10. package/dist/cli.js.map +1 -1
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +28 -4
  13. package/dist/config.js.map +1 -1
  14. package/dist/dev-server.d.ts +18 -0
  15. package/dist/dev-server.d.ts.map +1 -1
  16. package/dist/dev-server.js +168 -8
  17. package/dist/dev-server.js.map +1 -1
  18. package/dist/extension-metadata.d.ts +6 -0
  19. package/dist/extension-metadata.d.ts.map +1 -1
  20. package/dist/extension-metadata.js +70 -4
  21. package/dist/extension-metadata.js.map +1 -1
  22. package/dist/format-guard.d.ts +20 -0
  23. package/dist/format-guard.d.ts.map +1 -0
  24. package/dist/format-guard.js +13 -0
  25. package/dist/format-guard.js.map +1 -0
  26. package/dist/framework-host.d.ts.map +1 -1
  27. package/dist/framework-host.js +15 -2
  28. package/dist/framework-host.js.map +1 -1
  29. package/dist/language-server-bundle-entry.js +0 -2
  30. package/dist/language-server-bundle-entry.js.map +1 -1
  31. package/dist/language-server-tool.d.ts.map +1 -1
  32. package/dist/language-server-tool.js +0 -2
  33. package/dist/language-server-tool.js.map +1 -1
  34. package/dist/language-server.d.ts +1 -1
  35. package/dist/language-server.d.ts.map +1 -1
  36. package/dist/language-server.js +87 -293
  37. package/dist/language-server.js.map +1 -1
  38. package/dist/local-request-guard.d.ts +18 -0
  39. package/dist/local-request-guard.d.ts.map +1 -0
  40. package/dist/local-request-guard.js +75 -0
  41. package/dist/local-request-guard.js.map +1 -0
  42. package/dist/mechanical-fixer.d.ts.map +1 -1
  43. package/dist/mechanical-fixer.js +116 -2
  44. package/dist/mechanical-fixer.js.map +1 -1
  45. package/dist/module-assets.d.ts +23 -0
  46. package/dist/module-assets.d.ts.map +1 -1
  47. package/dist/module-assets.js +216 -14
  48. package/dist/module-assets.js.map +1 -1
  49. package/dist/node-application.d.ts +7 -2
  50. package/dist/node-application.d.ts.map +1 -1
  51. package/dist/node-application.js +44 -14
  52. package/dist/node-application.js.map +1 -1
  53. package/dist/node-runtime-dependencies.d.ts +2 -0
  54. package/dist/node-runtime-dependencies.d.ts.map +1 -0
  55. package/dist/node-runtime-dependencies.js +24 -0
  56. package/dist/node-runtime-dependencies.js.map +1 -0
  57. package/dist/npm.d.ts +29 -0
  58. package/dist/npm.d.ts.map +1 -1
  59. package/dist/npm.js +39 -2
  60. package/dist/npm.js.map +1 -1
  61. package/dist/ownership-graph.d.ts.map +1 -1
  62. package/dist/ownership-graph.js +155 -38
  63. package/dist/ownership-graph.js.map +1 -1
  64. package/dist/package-manager.d.ts.map +1 -1
  65. package/dist/package-manager.js +4 -1
  66. package/dist/package-manager.js.map +1 -1
  67. package/dist/preview-server.d.ts.map +1 -1
  68. package/dist/preview-server.js +12 -4
  69. package/dist/preview-server.js.map +1 -1
  70. package/dist/production-build.d.ts.map +1 -1
  71. package/dist/production-build.js +24 -6
  72. package/dist/production-build.js.map +1 -1
  73. package/dist/production-verifier.js +3 -0
  74. package/dist/production-verifier.js.map +1 -1
  75. package/dist/project-check.d.ts +14 -1
  76. package/dist/project-check.d.ts.map +1 -1
  77. package/dist/project-check.js +17 -4
  78. package/dist/project-check.js.map +1 -1
  79. package/dist/project-format.d.ts +9 -0
  80. package/dist/project-format.d.ts.map +1 -1
  81. package/dist/project-format.js +13 -0
  82. package/dist/project-format.js.map +1 -1
  83. package/dist/project-semantic.d.ts.map +1 -1
  84. package/dist/project-semantic.js +15 -4
  85. package/dist/project-semantic.js.map +1 -1
  86. package/dist/project.d.ts +25 -0
  87. package/dist/project.d.ts.map +1 -1
  88. package/dist/project.js +341 -42
  89. package/dist/project.js.map +1 -1
  90. package/dist/reproduction.d.ts +18 -0
  91. package/dist/reproduction.d.ts.map +1 -1
  92. package/dist/reproduction.js +47 -6
  93. package/dist/reproduction.js.map +1 -1
  94. package/dist/stable-order.d.ts +17 -0
  95. package/dist/stable-order.d.ts.map +1 -0
  96. package/dist/stable-order.js +19 -0
  97. package/dist/stable-order.js.map +1 -0
  98. package/dist/standard-modules.d.ts +2 -2
  99. package/dist/standard-modules.d.ts.map +1 -1
  100. package/dist/standard-modules.js +2 -2
  101. package/dist/standard-modules.js.map +1 -1
  102. package/dist/static-deployment.d.ts.map +1 -1
  103. package/dist/static-deployment.js +27 -0
  104. package/dist/static-deployment.js.map +1 -1
  105. package/dist/test-runner.d.ts.map +1 -1
  106. package/dist/test-runner.js +196 -100
  107. package/dist/test-runner.js.map +1 -1
  108. package/dist/test-worker.d.ts +64 -0
  109. package/dist/test-worker.d.ts.map +1 -0
  110. package/dist/test-worker.js +161 -0
  111. package/dist/test-worker.js.map +1 -0
  112. package/dist/typescript-declarations.d.ts +1 -1
  113. package/dist/typescript-declarations.d.ts.map +1 -1
  114. package/dist/typescript-declarations.js +233 -21
  115. package/dist/typescript-declarations.js.map +1 -1
  116. package/dist/unowned-errors.d.ts +15 -2
  117. package/dist/unowned-errors.d.ts.map +1 -1
  118. package/dist/unowned-errors.js +13 -2
  119. package/dist/unowned-errors.js.map +1 -1
  120. package/dist/version.d.ts +1 -1
  121. package/dist/version.js +1 -1
  122. package/dist/workspace-index.d.ts.map +1 -1
  123. package/dist/workspace-index.js +42 -17
  124. package/dist/workspace-index.js.map +1 -1
  125. package/package.json +7 -8
  126. package/skill/ai-skill-node.md +91 -12
  127. package/skill/ai-skill-web.md +51 -1
  128. package/skill/ai-skill.md +36 -13
  129. package/dist/official-script-language-service.d.ts +0 -2
  130. package/dist/official-script-language-service.d.ts.map +0 -1
  131. package/dist/official-script-language-service.js +0 -10
  132. package/dist/official-script-language-service.js.map +0 -1
  133. package/dist/official-tool-assets.d.ts +0 -3
  134. package/dist/official-tool-assets.d.ts.map +0 -1
  135. package/dist/official-tool-assets.js +0 -60
  136. package/dist/official-tool-assets.js.map +0 -1
  137. package/dist/script-language-service.d.ts +0 -82
  138. package/dist/script-language-service.d.ts.map +0 -1
  139. package/dist/script-language-service.js +0 -18
  140. package/dist/script-language-service.js.map +0 -1
@@ -1,16 +1,16 @@
1
1
  import { fileURLToPath, pathToFileURL } from "node:url";
2
2
  import { isAbsolute, relative, resolve } from "node:path";
3
- import { compile, CORE_CONTEXTUAL_KEYWORD_WORDS, CORE_PRELUDE_NAMES, formatSource, isSourceIdentifierPart, keywordKinds, PERMANENT_NAMESPACE_NAMES, permanentNamespaceCoveringModule, } from "@velarscript/compiler";
3
+ import { compile, CORE_CONTEXTUAL_KEYWORD_WORDS, CORE_PRELUDE_NAMES, isSourceIdentifierPart, keywordKinds, PERMANENT_NAMESPACE_NAMES, permanentNamespaceCoveringModule, } from "@velarscript/compiler";
4
4
  import { standardModuleInterfaces } from "./standard-modules.js";
5
5
  import { VelarProjectSessions } from "./project-session.js";
6
6
  import { VELAR_VERSION } from "./version.js";
7
7
  import { hostErrorMessage } from "./host-error.js";
8
+ import { formatSourceChecked } from "./format-guard.js";
8
9
  import { canonicalizePotentialPath, canonicalPathWithinCanonicalRoot } from "./canonical-path.js";
9
- import { createScriptLanguageDocument, scriptLanguageFor, } from "./script-language-service.js";
10
10
  import { buildOwnershipGraph, ownershipGraphRevision } from "./ownership-graph.js";
11
11
  import { projectDefinitionAt, projectCompletionsAt, projectCompletionContextAt, projectDocumentSymbols, projectExpressionAt, projectMemberSymbolAt, projectPrepareRenameAt, projectReferencesAt, projectRenameAt, projectSemanticTokens, projectSignatureAt, projectSymbolAt, projectWorkspaceSymbols, } from "./project-semantic.js";
12
12
  import { MAX_WORKSPACE_SEARCH_RESULTS, MAX_WORKSPACE_TEXT_FILES, MAX_WORKSPACE_CHANGE_PATHS, MAX_WORKSPACE_CHANGE_PATH_CODE_UNITS, MAX_WORKSPACE_CHANGE_TEXT_CODE_UNITS, WORKSPACE_TEXT_EXTENSIONS, WorkspaceIndexCancelledError, WorkspaceTextIndex, } from "./workspace-index.js";
13
- export const VELAR_LANGUAGE_SERVER_PROTOCOL_VERSION = 4;
13
+ export const VELAR_LANGUAGE_SERVER_PROTOCOL_VERSION = 5;
14
14
  let activePositionEncoding = "utf-16";
15
15
  let confinedWorkspaceRoot = null;
16
16
  let confinedCanonicalRoot = null;
@@ -25,6 +25,21 @@ const semanticTokenTypes = [
25
25
  "interface", "comment", "string", "keyword", "number", "regexp", "operator",
26
26
  ];
27
27
  const semanticTokenModifiers = ["declaration", "readonly", "static"];
28
+ const nonVelarDocumentResults = new Map([
29
+ ["textDocument/completion", { isIncomplete: false, items: [] }],
30
+ ["textDocument/hover", null],
31
+ ["textDocument/definition", null],
32
+ ["textDocument/references", []],
33
+ ["textDocument/documentHighlight", []],
34
+ ["textDocument/prepareRename", null],
35
+ ["textDocument/rename", null],
36
+ ["textDocument/documentSymbol", []],
37
+ ["textDocument/signatureHelp", null],
38
+ ["textDocument/inlayHint", []],
39
+ ["textDocument/semanticTokens/full", { data: [] }],
40
+ ["textDocument/codeAction", []],
41
+ ["textDocument/formatting", []],
42
+ ]);
28
43
  const keywordDocumentation = new Map([
29
44
  ["assert", "Requires a boolean or optional invariant and narrows stable values in following statements."],
30
45
  ["not", "Negates a checked condition; use `not in` for negative membership and `is not` for a negative runtime type test."],
@@ -134,7 +149,6 @@ export async function runLanguageServer() {
134
149
  const configuredCanonical = configuredCanonicalRoot() ?? confinedWorkspaceRoot;
135
150
  confinedCanonicalRoot = configuredCanonical ? await canonicalizePotentialPath(configuredCanonical) : null;
136
151
  const documents = new Map();
137
- const scriptDocuments = new Map();
138
152
  const sessions = new VelarProjectSessions();
139
153
  const workspaceIndex = new WorkspaceTextIndex();
140
154
  const pendingRequests = new Set();
@@ -166,13 +180,13 @@ export async function runLanguageServer() {
166
180
  process.stdout.write(`Content-Length: ${Buffer.byteLength(fallbackJson, "utf8")}\r\n\r\n${fallbackJson}`);
167
181
  };
168
182
  const overrides = () => new Map([...documents.values()].flatMap((item) => {
169
- if (scriptDocuments.has(item.uri))
183
+ if (!isVelarDocument(item))
170
184
  return [];
171
185
  const itemPath = pathOf(item.uri);
172
186
  return itemPath ? [[itemPath, item.text]] : [];
173
187
  }));
174
188
  const projectFor = async (document) => {
175
- if (scriptDocuments.has(document.uri))
189
+ if (!isVelarDocument(document))
176
190
  return null;
177
191
  const path = pathOf(document.uri);
178
192
  if (!path)
@@ -204,20 +218,16 @@ export async function runLanguageServer() {
204
218
  const current = documents.get(document.uri);
205
219
  if (!current || current.version !== document.version)
206
220
  return;
207
- const script = scriptDocuments.get(document.uri);
208
- if (script) {
221
+ if (!isVelarDocument(document)) {
209
222
  send({
210
223
  jsonrpc: "2.0",
211
224
  method: "textDocument/publishDiagnostics",
212
- params: {
213
- uri: document.uri,
214
- version: document.version,
215
- diagnostics: boundedScriptDiagnostics(document.text, script.analysis()),
216
- },
225
+ params: { uri: document.uri, version: document.version, diagnostics: [] },
217
226
  });
218
227
  return;
219
228
  }
220
229
  let diagnostics = [];
230
+ let advisories = [];
221
231
  let notices = [];
222
232
  let source;
223
233
  try {
@@ -232,24 +242,28 @@ export async function runLanguageServer() {
232
242
  .filter((failure) => failure.path === path)
233
243
  .map((failure) => ({ code: "VEL9001", message: failure.message, span: { start: 0, end: 1 } })),
234
244
  ];
245
+ advisories = module.result.advisories;
235
246
  notices = (project?.notices ?? []).filter((notice) => notice.path === path).map((notice) => notice.message);
236
247
  source = module.result.source;
237
248
  }
238
249
  else {
239
250
  const result = compile(document.text, { path });
240
251
  diagnostics = result.diagnostics;
252
+ advisories = result.advisories;
241
253
  source = result.source;
242
254
  }
243
255
  }
244
256
  else {
245
257
  const result = compile(document.text, { path: document.uri });
246
258
  diagnostics = result.diagnostics;
259
+ advisories = result.advisories;
247
260
  source = result.source;
248
261
  }
249
262
  }
250
263
  catch (error) {
251
264
  const result = compile(document.text, { path: document.uri });
252
265
  diagnostics = [{ code: "VEL9001", message: hostErrorMessage(error), span: { start: 0, end: 1 } }];
266
+ advisories = [];
253
267
  source = result.source;
254
268
  }
255
269
  if (documents.get(document.uri)?.version !== document.version)
@@ -260,7 +274,7 @@ export async function runLanguageServer() {
260
274
  params: {
261
275
  uri: document.uri,
262
276
  version: document.version,
263
- diagnostics: boundedDiagnostics(source, diagnostics, notices),
277
+ diagnostics: boundedDiagnostics(source, diagnostics, notices, advisories),
264
278
  },
265
279
  });
266
280
  };
@@ -290,7 +304,7 @@ export async function runLanguageServer() {
290
304
  const currentOverrides = overrides();
291
305
  const roots = new Map();
292
306
  for (const document of documents.values()) {
293
- if (scriptDocuments.has(document.uri))
307
+ if (!isVelarDocument(document))
294
308
  continue;
295
309
  const documentPath = pathOf(document.uri);
296
310
  if (!documentPath)
@@ -338,6 +352,14 @@ export async function runLanguageServer() {
338
352
  return;
339
353
  }
340
354
  const params = message.params;
355
+ if (message.id !== undefined && message.method && nonVelarDocumentResults.has(message.method)) {
356
+ const descriptor = params?.textDocument;
357
+ const document = descriptor?.uri ? documents.get(descriptor.uri) : undefined;
358
+ if (document && !isVelarDocument(document)) {
359
+ respond(message.id, nonVelarDocumentResults.get(message.method));
360
+ return;
361
+ }
362
+ }
341
363
  switch (message.method) {
342
364
  case "initialize":
343
365
  activePositionEncoding = requestedPositionEncoding(params);
@@ -375,9 +397,6 @@ export async function runLanguageServer() {
375
397
  watchedFiles: true,
376
398
  workspaceRescan: true,
377
399
  cancellation: true,
378
- scriptLanguages: ["javascript", "typescript"],
379
- scriptImplementation: "velarscript",
380
- incrementalScriptLexing: true,
381
400
  workspaceSearch: true,
382
401
  workspaceTextExtensions: WORKSPACE_TEXT_EXTENSIONS,
383
402
  workspaceTextFileLimit: MAX_WORKSPACE_TEXT_FILES,
@@ -422,11 +441,7 @@ export async function runLanguageServer() {
422
441
  documents.set(value.uri, value);
423
442
  if (path)
424
443
  workspaceIndex.openDocument(path, value.text);
425
- const scriptLanguage = scriptLanguageFor(path ?? value.uri, value.languageId);
426
- const script = scriptLanguage ? createScriptLanguageDocument(scriptLanguage, value.text) : null;
427
- if (script)
428
- scriptDocuments.set(value.uri, script);
429
- if (path && !script) {
444
+ if (path && isVelarDocument(value)) {
430
445
  try {
431
446
  await sessions.snapshot(path, overrides());
432
447
  }
@@ -448,10 +463,6 @@ export async function runLanguageServer() {
448
463
  const nextPath = pathOf(next.uri);
449
464
  if (nextPath)
450
465
  workspaceIndex.changeDocument(nextPath, next.text);
451
- const script = scriptDocuments.get(next.uri);
452
- const edit = script ? scriptTextEdit(current.text, next.text) : null;
453
- if (script && edit)
454
- script.apply([edit]);
455
466
  schedulePublish(next.uri);
456
467
  break;
457
468
  }
@@ -464,14 +475,13 @@ export async function runLanguageServer() {
464
475
  }
465
476
  case "textDocument/didClose": {
466
477
  const descriptor = params?.textDocument;
478
+ const current = documents.get(descriptor.uri);
467
479
  documents.delete(descriptor.uri);
468
- const script = scriptDocuments.get(descriptor.uri);
469
- scriptDocuments.delete(descriptor.uri);
470
480
  diagnosticUris.delete(descriptor.uri);
471
481
  const path = pathOf(descriptor.uri);
472
482
  if (path)
473
483
  await queueWorkspaceIndex(() => workspaceIndex.closeDocument(path));
474
- if (path && !script)
484
+ if (path && current && isVelarDocument(current))
475
485
  await sessions.update(path, new Set([path]), overrides());
476
486
  send({ jsonrpc: "2.0", method: "textDocument/publishDiagnostics", params: { uri: descriptor.uri, diagnostics: [] } });
477
487
  break;
@@ -597,7 +607,7 @@ export async function runLanguageServer() {
597
607
  break;
598
608
  }
599
609
  const document = documents.get(descriptor.uri);
600
- if (!document || scriptDocuments.has(descriptor.uri)) {
610
+ if (!document || !isVelarDocument(document)) {
601
611
  respondError(message.id, "velar/ownershipGraph requires an open VelarScript document", -32602);
602
612
  break;
603
613
  }
@@ -661,7 +671,7 @@ export async function runLanguageServer() {
661
671
  break;
662
672
  }
663
673
  const document = documents.get(descriptor.uri);
664
- if (!document || scriptDocuments.has(descriptor.uri)) {
674
+ if (!document || !isVelarDocument(document)) {
665
675
  respondError(message.id, "velar/emittedJavaScript requires an open VelarScript document", -32602);
666
676
  break;
667
677
  }
@@ -690,7 +700,7 @@ export async function runLanguageServer() {
690
700
  generatedChars: code?.length ?? 0,
691
701
  sourceMapChars: sourceMap?.length ?? 0,
692
702
  limitReached: (code?.length ?? 0) > limit || (sourceMap?.length ?? 0) > limit,
693
- diagnostics: boundedDiagnostics(module.result.source, module.result.diagnostics, []),
703
+ diagnostics: boundedDiagnostics(module.result.source, module.result.diagnostics, [], module.result.advisories),
694
704
  });
695
705
  break;
696
706
  }
@@ -707,7 +717,7 @@ export async function runLanguageServer() {
707
717
  await waitForWorkspaceIndex(message.id);
708
718
  const projects = new Map();
709
719
  for (const document of documents.values()) {
710
- if (scriptDocuments.has(document.uri))
720
+ if (!isVelarDocument(document))
711
721
  continue;
712
722
  const path = pathOf(document.uri);
713
723
  if (!path)
@@ -758,16 +768,6 @@ export async function runLanguageServer() {
758
768
  const descriptor = params?.textDocument;
759
769
  const position = params?.position;
760
770
  const document = documents.get(descriptor.uri);
761
- const script = scriptDocuments.get(descriptor.uri);
762
- if (document && script) {
763
- const items = script.completionsAt(scriptOffsetAt(document.text, position)).slice(0, MAX_LSP_RESULT_ITEMS).map((item) => ({
764
- label: clipLspText(item.label),
765
- kind: lspCompletionKind(item.kind),
766
- detail: clipLspText(item.detail),
767
- }));
768
- respond(message.id, { isIncomplete: false, items });
769
- break;
770
- }
771
771
  const path = pathOf(descriptor.uri);
772
772
  const project = document ? await projectFor(document) : null;
773
773
  const offset = document ? offsetAt(document.text, position) : 0;
@@ -790,15 +790,6 @@ export async function runLanguageServer() {
790
790
  const descriptor = params?.textDocument;
791
791
  const position = params?.position;
792
792
  const document = documents.get(descriptor.uri);
793
- const script = scriptDocuments.get(descriptor.uri);
794
- if (document && script) {
795
- const result = script.hoverAt(scriptOffsetAt(document.text, position));
796
- respond(message.id, result ? {
797
- contents: { kind: "markdown", value: `\`\`${clipLspText(result.contents)}\`\`` },
798
- range: scriptRange(document.text, result),
799
- } : null);
800
- break;
801
- }
802
793
  respond(message.id, document ? await hover(document, position, await projectFor(document)) : null);
803
794
  break;
804
795
  }
@@ -806,12 +797,6 @@ export async function runLanguageServer() {
806
797
  const descriptor = params?.textDocument;
807
798
  const position = params?.position;
808
799
  const document = documents.get(descriptor.uri);
809
- const script = scriptDocuments.get(descriptor.uri);
810
- if (document && script) {
811
- const location = script.definitionAt(scriptOffsetAt(document.text, position));
812
- respond(message.id, location ? { uri: descriptor.uri, range: scriptRange(document.text, location) } : null);
813
- break;
814
- }
815
800
  const path = pathOf(descriptor.uri);
816
801
  const project = document ? await projectFor(document) : null;
817
802
  const location = document && path && project ? projectDefinitionAt(project, path, offsetAt(document.text, position)) : null;
@@ -823,14 +808,6 @@ export async function runLanguageServer() {
823
808
  const position = params?.position;
824
809
  const context = params?.context;
825
810
  const document = documents.get(descriptor.uri);
826
- const script = scriptDocuments.get(descriptor.uri);
827
- if (document && script) {
828
- const locations = script.referencesAt(scriptOffsetAt(document.text, position), context?.includeDeclaration ?? false)
829
- .slice(0, MAX_LSP_RESULT_ITEMS)
830
- .map((span) => ({ uri: descriptor.uri, range: scriptRange(document.text, span) }));
831
- respond(message.id, locations);
832
- break;
833
- }
834
811
  const path = pathOf(descriptor.uri);
835
812
  const project = document ? await projectFor(document) : null;
836
813
  const locations = document && path && project
@@ -844,14 +821,6 @@ export async function runLanguageServer() {
844
821
  const descriptor = params?.textDocument;
845
822
  const position = params?.position;
846
823
  const document = documents.get(descriptor.uri);
847
- const script = scriptDocuments.get(descriptor.uri);
848
- if (document && script) {
849
- const locations = script.referencesAt(scriptOffsetAt(document.text, position), true)
850
- .slice(0, MAX_LSP_RESULT_ITEMS)
851
- .map((span) => ({ range: scriptRange(document.text, span), kind: 1 }));
852
- respond(message.id, locations);
853
- break;
854
- }
855
824
  const path = pathOf(descriptor.uri);
856
825
  const project = document ? await projectFor(document) : null;
857
826
  const locations = document && path && project
@@ -869,12 +838,6 @@ export async function runLanguageServer() {
869
838
  const descriptor = params?.textDocument;
870
839
  const position = params?.position;
871
840
  const document = documents.get(descriptor.uri);
872
- const script = scriptDocuments.get(descriptor.uri);
873
- if (document && script) {
874
- const symbol = script.symbolAt(scriptOffsetAt(document.text, position));
875
- respond(message.id, symbol ? { range: scriptRange(document.text, symbol), placeholder: symbol.name } : null);
876
- break;
877
- }
878
841
  const path = pathOf(descriptor.uri);
879
842
  const project = document ? await projectFor(document) : null;
880
843
  const prepared = document && path && project ? projectPrepareRenameAt(project, path, offsetAt(document.text, position)) : null;
@@ -887,19 +850,6 @@ export async function runLanguageServer() {
887
850
  const position = params?.position;
888
851
  const newName = params?.newName;
889
852
  const document = documents.get(descriptor.uri);
890
- const script = scriptDocuments.get(descriptor.uri);
891
- if (document && script) {
892
- const renamed = script.renameAt(scriptOffsetAt(document.text, position), newName);
893
- if (renamed.error)
894
- respondError(message.id, renamed.error);
895
- else if (renamed.edits.length > MAX_LSP_RESULT_ITEMS)
896
- respondError(message.id, `Rename affects more than ${MAX_LSP_RESULT_ITEMS} locations`);
897
- else
898
- respond(message.id, {
899
- changes: { [descriptor.uri]: renamed.edits.map((edit) => ({ range: scriptRange(document.text, edit), newText: edit.replacement })) },
900
- });
901
- break;
902
- }
903
853
  const path = pathOf(descriptor.uri);
904
854
  const project = document ? await projectFor(document) : null;
905
855
  const renamed = document && path && project ? projectRenameAt(project, path, offsetAt(document.text, position), newName) : "No renameable VelarScript symbol at this position";
@@ -914,17 +864,6 @@ export async function runLanguageServer() {
914
864
  case "textDocument/documentSymbol": {
915
865
  const descriptor = params?.textDocument;
916
866
  const document = documents.get(descriptor.uri);
917
- const script = scriptDocuments.get(descriptor.uri);
918
- if (document && script) {
919
- respond(message.id, script.analysis().symbols.slice(0, MAX_LSP_RESULT_ITEMS).map((symbol) => ({
920
- name: clipLspText(symbol.name),
921
- detail: clipLspText(symbol.type),
922
- kind: lspSymbolKind(symbol.kind),
923
- range: scriptRange(document.text, symbol),
924
- selectionRange: scriptRange(document.text, symbol),
925
- })));
926
- break;
927
- }
928
867
  const path = pathOf(descriptor.uri);
929
868
  const project = document ? await projectFor(document) : null;
930
869
  respond(message.id, path && project ? projectDocumentSymbols(project, path).slice(0, MAX_LSP_RESULT_ITEMS).map((symbol) => ({
@@ -940,10 +879,6 @@ export async function runLanguageServer() {
940
879
  const descriptor = params?.textDocument;
941
880
  const position = params?.position;
942
881
  const document = documents.get(descriptor.uri);
943
- if (scriptDocuments.has(descriptor.uri)) {
944
- respond(message.id, null);
945
- break;
946
- }
947
882
  const path = pathOf(descriptor.uri);
948
883
  const project = document ? await projectFor(document) : null;
949
884
  const signature = document && path && project ? projectSignatureAt(project, path, offsetAt(document.text, position)) : null;
@@ -958,10 +893,6 @@ export async function runLanguageServer() {
958
893
  const descriptor = params?.textDocument;
959
894
  const range = params?.range;
960
895
  const document = documents.get(descriptor.uri);
961
- if (scriptDocuments.has(descriptor.uri)) {
962
- respond(message.id, []);
963
- break;
964
- }
965
896
  const path = pathOf(descriptor.uri);
966
897
  const project = document ? await projectFor(document) : null;
967
898
  respond(message.id, document && path && project ? projectInlayHints(project, path, document.text, range) : []);
@@ -970,11 +901,6 @@ export async function runLanguageServer() {
970
901
  case "textDocument/semanticTokens/full": {
971
902
  const descriptor = params?.textDocument;
972
903
  const document = documents.get(descriptor.uri);
973
- const script = scriptDocuments.get(descriptor.uri);
974
- if (document && script) {
975
- respond(message.id, { data: scriptSemanticTokenData(document.text, script.analysis()) });
976
- break;
977
- }
978
904
  const path = pathOf(descriptor.uri);
979
905
  const project = document ? await projectFor(document) : null;
980
906
  const tokens = path && project ? projectSemanticTokens(project, path).slice(0, MAX_LSP_RESULT_ITEMS) : [];
@@ -985,10 +911,6 @@ export async function runLanguageServer() {
985
911
  const descriptor = params?.textDocument;
986
912
  const context = params?.context;
987
913
  const document = documents.get(descriptor.uri);
988
- if (scriptDocuments.has(descriptor.uri)) {
989
- respond(message.id, []);
990
- break;
991
- }
992
914
  const acceptsQuickFix = !context?.only || context.only.some((kind) => kind === "quickfix" || kind.startsWith("quickfix."));
993
915
  const fixPath = pathOf(descriptor.uri);
994
916
  const fixProject = document && acceptsQuickFix ? await projectFor(document) : null;
@@ -1004,13 +926,13 @@ export async function runLanguageServer() {
1004
926
  respond(message.id, []);
1005
927
  break;
1006
928
  }
1007
- if (scriptDocuments.has(descriptor.uri)) {
1008
- respond(message.id, []);
1009
- break;
1010
- }
1011
929
  const project = await projectFor(document);
1012
- const formatted = formatSource(document.text, { extensions: project?.compilerExtensions ?? [] });
1013
- respond(message.id, formatted === document.text ? [] : [{ range: fullRange(document.text), newText: formatted }]);
930
+ const { text: formatted, stable } = formatSourceChecked(document.text, { extensions: project?.compilerExtensions ?? [] });
931
+ // Formatting is idempotent by contract, so a result the formatter would
932
+ // change again is a formatter defect. Offering it as an edit hands
933
+ // format-on-save a module the next save corrupts, and an editor cannot
934
+ // undo what it never saw as a change; no edits is the honest answer.
935
+ respond(message.id, !stable || formatted === document.text ? [] : [{ range: fullRange(document.text), newText: formatted }]);
1014
936
  break;
1015
937
  }
1016
938
  default:
@@ -1096,49 +1018,33 @@ function isRpcMessage(value) {
1096
1018
  function clipLspText(value) {
1097
1019
  return value.length <= MAX_LSP_TEXT_CHARS ? value : `${value.slice(0, MAX_LSP_TEXT_CHARS - 1)}…`;
1098
1020
  }
1099
- function boundedDiagnostics(source, diagnostics, notices) {
1021
+ function boundedDiagnostics(source, diagnostics, notices, advisories) {
1100
1022
  const output = [];
1101
1023
  for (const diagnostic of diagnostics) {
1102
1024
  if (output.length >= MAX_LSP_RESULT_ITEMS)
1103
1025
  break;
1104
1026
  output.push(lspDiagnostic(source, diagnostic));
1105
1027
  }
1028
+ // D89: advisories are published on the same channel the protocol gives an
1029
+ // editor, one severity below an error, so the squiggle a reader sees says
1030
+ // "this spelling means something else" without claiming the file failed.
1031
+ for (const item of advisories) {
1032
+ if (output.length >= MAX_LSP_RESULT_ITEMS)
1033
+ break;
1034
+ output.push(lspAdvisory(source, item));
1035
+ }
1106
1036
  for (const notice of notices) {
1107
1037
  if (output.length >= MAX_LSP_RESULT_ITEMS)
1108
1038
  break;
1109
1039
  output.push(lspNotice(source, notice));
1110
1040
  }
1111
- if (diagnostics.length + notices.length > MAX_LSP_RESULT_ITEMS) {
1041
+ if (diagnostics.length + advisories.length + notices.length > MAX_LSP_RESULT_ITEMS) {
1112
1042
  if (output.length >= MAX_LSP_RESULT_ITEMS)
1113
1043
  output.pop();
1114
1044
  output.push(lspNotice(source, `Diagnostics were truncated to ${MAX_LSP_RESULT_ITEMS} items`));
1115
1045
  }
1116
1046
  return output;
1117
1047
  }
1118
- function boundedScriptDiagnostics(text, analysis) {
1119
- const diagnostics = analysis.diagnostics.slice(0, MAX_LSP_RESULT_ITEMS);
1120
- const ends = diagnostics.map((diagnostic) => Math.max(diagnostic.start + 1, diagnostic.end));
1121
- const positions = scriptPositionsAt(text, diagnostics.flatMap((diagnostic, index) => [diagnostic.start, ends[index]]));
1122
- const output = diagnostics.map((diagnostic, index) => ({
1123
- range: scriptRangeFromPositions(positions, diagnostic.start, ends[index]),
1124
- severity: diagnostic.severity === "error" ? 1 : 2,
1125
- code: diagnostic.code,
1126
- source: "velar-script",
1127
- message: clipLspText(diagnostic.message),
1128
- }));
1129
- if (analysis.diagnostics.length > MAX_LSP_RESULT_ITEMS) {
1130
- if (output.length >= MAX_LSP_RESULT_ITEMS)
1131
- output.pop();
1132
- output.push({
1133
- range: scriptRange(text, { start: 0, end: Math.min(1, codePointCount(text, 0, text.length)) }),
1134
- severity: 2,
1135
- code: "SCRIPT9001",
1136
- source: "velar-script",
1137
- message: `Script diagnostics were truncated to ${MAX_LSP_RESULT_ITEMS} items`,
1138
- });
1139
- }
1140
- return output;
1141
- }
1142
1048
  function oversizedDiagnosticsFallback(params) {
1143
1049
  const input = params && typeof params === "object" && !Array.isArray(params)
1144
1050
  ? params
@@ -1173,6 +1079,21 @@ function lspDiagnostic(source, item) {
1173
1079
  message: clipLspText(item.message),
1174
1080
  };
1175
1081
  }
1082
+ /** D89: severity 2 is Warning — an advisory is reported, never a failure. */
1083
+ function lspAdvisory(source, item) {
1084
+ const start = lspSourcePosition(source, item.span.start);
1085
+ const end = lspSourcePosition(source, Math.max(item.span.start + 1, item.span.end));
1086
+ return {
1087
+ range: {
1088
+ start,
1089
+ end,
1090
+ },
1091
+ severity: 2,
1092
+ code: item.code,
1093
+ source: "velar",
1094
+ message: clipLspText(item.message),
1095
+ };
1096
+ }
1176
1097
  function lspNotice(source, message) {
1177
1098
  const start = lspSourcePosition(source, 0);
1178
1099
  const end = lspSourcePosition(source, Math.min(1, source.text.length));
@@ -1267,74 +1188,20 @@ function semanticTokenData(source, tokens) {
1267
1188
  }
1268
1189
  return data;
1269
1190
  }
1270
- function scriptSemanticTokenData(text, analysis) {
1271
- const tokens = analysis.tokens.slice(0, MAX_LSP_RESULT_ITEMS);
1272
- const positions = scriptPositionsAt(text, tokens.flatMap((token) => [token.start, token.end]));
1273
- const declarations = new Map(analysis.symbols.map((symbol) => [`${symbol.start}:${symbol.end}`, symbol]));
1274
- const references = new Map(analysis.references.map((reference) => [`${reference.start}:${reference.end}`, reference]));
1275
- const symbols = new Map(analysis.symbols.map((symbol) => [symbol.id, symbol]));
1276
- const data = [];
1277
- let previousLine = 0;
1278
- let previousCharacter = 0;
1279
- for (const token of tokens) {
1280
- const key = `${token.start}:${token.end}`;
1281
- const declaration = declarations.get(key);
1282
- const reference = references.get(key);
1283
- const symbol = declaration ?? (reference ? symbols.get(reference.symbolId) : undefined);
1284
- const type = symbol ? scriptSemanticSymbolType(symbol.kind) : scriptLexicalTokenType(token.kind);
1285
- if (!type)
1286
- continue;
1287
- const range = scriptRangeFromPositions(positions, token.start, token.end);
1288
- if (range.start.line !== range.end.line || range.end.character <= range.start.character)
1289
- continue;
1290
- const deltaLine = range.start.line - previousLine;
1291
- const deltaCharacter = deltaLine === 0 ? range.start.character - previousCharacter : range.start.character;
1292
- const tokenType = semanticTokenTypes.indexOf(type);
1293
- if (tokenType < 0 || deltaLine < 0 || deltaCharacter < 0)
1294
- continue;
1295
- const modifiers = (declaration ? 1 << semanticTokenModifiers.indexOf("declaration") : 0)
1296
- | (symbol && (symbol.kind === "constant" || symbol.kind === "import") ? 1 << semanticTokenModifiers.indexOf("readonly") : 0);
1297
- data.push(deltaLine, deltaCharacter, range.end.character - range.start.character, tokenType, modifiers);
1298
- previousLine = range.start.line;
1299
- previousCharacter = range.start.character;
1300
- }
1301
- return data;
1302
- }
1303
- function scriptSemanticSymbolType(kind) {
1304
- switch (kind) {
1305
- case "class": return "class";
1306
- case "interface": return "interface";
1307
- case "type": return "type";
1308
- case "enum": return "enum";
1309
- case "function": return "function";
1310
- case "parameter": return "parameter";
1311
- default: return "variable";
1312
- }
1313
- }
1314
- function scriptLexicalTokenType(kind) {
1315
- switch (kind) {
1316
- case "comment": return "comment";
1317
- case "string":
1318
- case "template": return "string";
1319
- case "keyword": return "keyword";
1320
- case "number": return "number";
1321
- case "regexp": return "regexp";
1322
- case "operator": return "operator";
1323
- case "identifier": return "variable";
1324
- default: return null;
1325
- }
1326
- }
1327
1191
  /**
1328
1192
  * D38 §48: an editor quick fix is the same mechanical rewrite `velar fix`
1329
1193
  * applies, read from the diagnostic that named it. The compiler registers the
1330
1194
  * rewrite where it reports the diagnostic, so the editor never re-derives one
1331
- * from message text and the two surfaces can never drift apart.
1195
+ * from message text and the two surfaces can never drift apart. D89: an
1196
+ * advisory that names one rewrite registers it the same way, so the editor
1197
+ * offers A2's swap as an ordinary quick fix; `velar fix` still reads
1198
+ * diagnostics only, because which name binds which value is a judgment.
1332
1199
  */
1333
1200
  function quickFixes(document, module, diagnostics) {
1334
1201
  if (!module)
1335
1202
  return [];
1336
1203
  const source = module.result.source;
1337
- const registered = module.result.diagnostics
1204
+ const registered = [...module.result.diagnostics, ...module.result.advisories]
1338
1205
  .filter((item) => item.fix && item.fix.edits.length > 0)
1339
1206
  .map((item) => ({ code: item.code, range: lspRange(source, boundedDiagnosticSpan(source, item.span)), fix: item.fix }));
1340
1207
  if (registered.length === 0)
@@ -1461,6 +1328,13 @@ function rawPathOf(uri) {
1461
1328
  return null;
1462
1329
  }
1463
1330
  }
1331
+ function isVelarDocument(document) {
1332
+ const language = document.languageId.trim().toLowerCase();
1333
+ if (language === "velar" || language === "velarscript")
1334
+ return true;
1335
+ const path = rawPathOf(document.uri);
1336
+ return path !== null && path.toLowerCase().endsWith(".vel");
1337
+ }
1464
1338
  async function authorizedPathOf(uri) {
1465
1339
  const path = pathOf(uri);
1466
1340
  if (!path || !confinedCanonicalRoot)
@@ -1485,24 +1359,6 @@ function applyContentChanges(text, changes) {
1485
1359
  }
1486
1360
  return result;
1487
1361
  }
1488
- function scriptTextEdit(previous, next) {
1489
- if (previous === next)
1490
- return null;
1491
- const before = Array.from(previous);
1492
- const after = Array.from(next);
1493
- const limit = Math.min(before.length, after.length);
1494
- let prefix = 0;
1495
- while (prefix < limit && before[prefix] === after[prefix])
1496
- prefix += 1;
1497
- let suffix = 0;
1498
- while (suffix < limit - prefix && before[before.length - suffix - 1] === after[after.length - suffix - 1])
1499
- suffix += 1;
1500
- return {
1501
- start: prefix,
1502
- end: before.length - suffix,
1503
- replacement: after.slice(prefix, after.length - suffix).join(""),
1504
- };
1505
- }
1506
1362
  function offsetAt(text, position) {
1507
1363
  const requestedLine = Number.isSafeInteger(position?.line) && position.line >= 0
1508
1364
  ? position.line
@@ -1520,68 +1376,6 @@ function offsetAt(text, position) {
1520
1376
  ? Math.min(end, offset + requestedCharacter)
1521
1377
  : codeUnitOffsetAt(text, offset, end, requestedCharacter);
1522
1378
  }
1523
- function scriptOffsetAt(text, position) {
1524
- return codePointCount(text, 0, offsetAt(text, position));
1525
- }
1526
- function scriptRange(text, span) {
1527
- const positions = scriptPositionsAt(text, [span.start, span.end]);
1528
- return scriptRangeFromPositions(positions, span.start, span.end);
1529
- }
1530
- function scriptRangeFromPositions(positions, start, end) {
1531
- const startPosition = positions.get(start);
1532
- const endPosition = positions.get(end);
1533
- if (!startPosition || !endPosition)
1534
- throw new Error("Script coordinate map is incomplete");
1535
- return { start: startPosition, end: endPosition };
1536
- }
1537
- function scriptPositionsAt(text, offsets) {
1538
- const requested = [...new Set(offsets.map((offset) => Math.max(0, offset)))].sort((left, right) => left - right);
1539
- const output = new Map();
1540
- let codeUnit = 0;
1541
- let codePoint = 0;
1542
- let line = 0;
1543
- let lineCodeUnit = 0;
1544
- let lineCodePoint = 0;
1545
- let pendingCarriageReturn = false;
1546
- for (const requestedOffset of requested) {
1547
- while (codePoint < requestedOffset && codeUnit < text.length) {
1548
- const value = text.codePointAt(codeUnit);
1549
- const width = value > 0xffff ? 2 : 1;
1550
- const character = text[codeUnit];
1551
- codeUnit += width;
1552
- codePoint += 1;
1553
- if (pendingCarriageReturn) {
1554
- pendingCarriageReturn = false;
1555
- if (character === "\n") {
1556
- line += 1;
1557
- lineCodeUnit = codeUnit;
1558
- lineCodePoint = codePoint;
1559
- continue;
1560
- }
1561
- }
1562
- if (character === "\r") {
1563
- if (text[codeUnit] === "\n")
1564
- pendingCarriageReturn = true;
1565
- else {
1566
- line += 1;
1567
- lineCodeUnit = codeUnit;
1568
- lineCodePoint = codePoint;
1569
- }
1570
- }
1571
- else if (character === "\n") {
1572
- line += 1;
1573
- lineCodeUnit = codeUnit;
1574
- lineCodePoint = codePoint;
1575
- }
1576
- }
1577
- const boundedOffset = Math.min(requestedOffset, codePoint);
1578
- output.set(requestedOffset, {
1579
- line,
1580
- character: activePositionEncoding === "utf-16" ? codeUnit - lineCodeUnit : boundedOffset - lineCodePoint,
1581
- });
1582
- }
1583
- return output;
1584
- }
1585
1379
  function positionAt(text, requestedOffset) {
1586
1380
  const offset = Math.max(0, Math.min(text.length, requestedOffset));
1587
1381
  let line = 0;