@typespec/compiler 0.52.0-dev.12 → 0.52.0-dev.14

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 (61) hide show
  1. package/dist/manifest.js +2 -2
  2. package/dist/src/core/messages.d.ts +61 -2
  3. package/dist/src/core/messages.d.ts.map +1 -1
  4. package/dist/src/core/messages.js +19 -0
  5. package/dist/src/core/messages.js.map +1 -1
  6. package/dist/src/core/mime-type.d.ts +7 -0
  7. package/dist/src/core/mime-type.d.ts.map +1 -0
  8. package/dist/src/core/mime-type.js +19 -0
  9. package/dist/src/core/mime-type.js.map +1 -0
  10. package/dist/src/core/program.d.ts.map +1 -1
  11. package/dist/src/core/program.js +18 -9
  12. package/dist/src/core/program.js.map +1 -1
  13. package/dist/src/lib/decorators.d.ts +1 -0
  14. package/dist/src/lib/decorators.d.ts.map +1 -1
  15. package/dist/src/lib/decorators.js +1 -0
  16. package/dist/src/lib/decorators.js.map +1 -1
  17. package/dist/src/lib/encoded-names.d.ts +30 -0
  18. package/dist/src/lib/encoded-names.d.ts.map +1 -0
  19. package/dist/src/lib/encoded-names.js +132 -0
  20. package/dist/src/lib/encoded-names.js.map +1 -0
  21. package/dist/src/server/classify.d.ts +8 -0
  22. package/dist/src/server/classify.d.ts.map +1 -0
  23. package/dist/src/server/classify.js +310 -0
  24. package/dist/src/server/classify.js.map +1 -0
  25. package/dist/src/server/compile-service.d.ts +42 -0
  26. package/dist/src/server/compile-service.d.ts.map +1 -0
  27. package/dist/src/server/compile-service.js +163 -0
  28. package/dist/src/server/compile-service.js.map +1 -0
  29. package/dist/src/server/constants.d.ts +7 -0
  30. package/dist/src/server/constants.d.ts.map +1 -0
  31. package/dist/src/server/constants.js +13 -0
  32. package/dist/src/server/constants.js.map +1 -0
  33. package/dist/src/server/file-service.d.ts +18 -0
  34. package/dist/src/server/file-service.d.ts.map +1 -0
  35. package/dist/src/server/file-service.js +56 -0
  36. package/dist/src/server/file-service.js.map +1 -0
  37. package/dist/src/server/file-system-cache.d.ts +25 -0
  38. package/dist/src/server/file-system-cache.d.ts.map +1 -0
  39. package/dist/src/server/file-system-cache.js +27 -0
  40. package/dist/src/server/file-system-cache.js.map +1 -0
  41. package/dist/src/server/index.d.ts +2 -1
  42. package/dist/src/server/index.d.ts.map +1 -1
  43. package/dist/src/server/index.js +2 -1
  44. package/dist/src/server/index.js.map +1 -1
  45. package/dist/src/server/server.js.map +1 -1
  46. package/dist/src/server/serverlib.d.ts +1 -71
  47. package/dist/src/server/serverlib.d.ts.map +1 -1
  48. package/dist/src/server/serverlib.js +115 -622
  49. package/dist/src/server/serverlib.js.map +1 -1
  50. package/dist/src/server/types.d.ts +76 -0
  51. package/dist/src/server/types.d.ts.map +1 -0
  52. package/dist/src/server/types.js +26 -0
  53. package/dist/src/server/types.js.map +1 -0
  54. package/dist/src/server/update-manager.d.ts +19 -0
  55. package/dist/src/server/update-manager.d.ts.map +1 -0
  56. package/dist/src/server/update-manager.js +70 -0
  57. package/dist/src/server/update-manager.js.map +1 -0
  58. package/dist/src/testing/types.d.ts +2 -2
  59. package/dist/src/testing/types.d.ts.map +1 -1
  60. package/lib/decorators.tsp +26 -0
  61. package/package.json +1 -1
@@ -1,67 +1,40 @@
1
1
  import { DiagnosticSeverity, DiagnosticTag, DocumentHighlightKind, MarkupKind, Range, SemanticTokensBuilder, TextDocumentSyncKind, TextEdit, Diagnostic as VSDiagnostic, } from "vscode-languageserver/node.js";
2
- import { defaultConfig, findTypeSpecConfigPath, loadTypeSpecConfigFile, } from "../config/config-loader.js";
3
- import { resolveOptionsFromConfig } from "../config/config-to-options.js";
4
2
  import { codePointBefore, isIdentifierContinue } from "../core/charcode.js";
5
- import { compilerAssert, createSourceFile, formatDiagnostic, getSourceLocation, } from "../core/diagnostics.js";
3
+ import { compilerAssert, createSourceFile, getSourceLocation } from "../core/diagnostics.js";
6
4
  import { formatTypeSpec } from "../core/formatter.js";
7
5
  import { getTypeName } from "../core/helpers/type-name-utils.js";
8
6
  import { getNodeAtPosition, visitChildren } from "../core/parser.js";
9
- import { ensureTrailingDirectorySeparator, getDirectoryPath, joinPaths, } from "../core/path-utils.js";
10
- import { compile as compileProgram } from "../core/program.js";
11
- import { Token, TokenFlags, createScanner, isKeyword, isPunctuation, skipTrivia, skipWhiteSpace, } from "../core/scanner.js";
7
+ import { ensureTrailingDirectorySeparator } from "../core/path-utils.js";
8
+ import { skipTrivia, skipWhiteSpace } from "../core/scanner.js";
12
9
  import { SyntaxKind, } from "../core/types.js";
13
- import { doIO, getNormalizedRealPath, getSourceFileKindFromExt, loadFile, resolveTspMain, } from "../core/util.js";
10
+ import { getNormalizedRealPath, getSourceFileKindFromExt } from "../core/util.js";
11
+ import { getSemanticTokens } from "./classify.js";
12
+ import { createCompileService } from "./compile-service.js";
14
13
  import { resolveCompletion } from "./completion.js";
14
+ import { createFileService } from "./file-service.js";
15
+ import { createFileSystemCache } from "./file-system-cache.js";
15
16
  import { getPositionBeforeTrivia } from "./server-utils.js";
16
17
  import { getSymbolStructure } from "./symbol-structure.js";
17
18
  import { getParameterDocumentation, getSymbolDetails, getTemplateParameterDocumentation, } from "./type-details.js";
18
- export var SemanticTokenKind;
19
- (function (SemanticTokenKind) {
20
- SemanticTokenKind[SemanticTokenKind["Namespace"] = 0] = "Namespace";
21
- SemanticTokenKind[SemanticTokenKind["Type"] = 1] = "Type";
22
- SemanticTokenKind[SemanticTokenKind["Class"] = 2] = "Class";
23
- SemanticTokenKind[SemanticTokenKind["Enum"] = 3] = "Enum";
24
- SemanticTokenKind[SemanticTokenKind["Interface"] = 4] = "Interface";
25
- SemanticTokenKind[SemanticTokenKind["Struct"] = 5] = "Struct";
26
- SemanticTokenKind[SemanticTokenKind["TypeParameter"] = 6] = "TypeParameter";
27
- SemanticTokenKind[SemanticTokenKind["Parameter"] = 7] = "Parameter";
28
- SemanticTokenKind[SemanticTokenKind["Variable"] = 8] = "Variable";
29
- SemanticTokenKind[SemanticTokenKind["Property"] = 9] = "Property";
30
- SemanticTokenKind[SemanticTokenKind["EnumMember"] = 10] = "EnumMember";
31
- SemanticTokenKind[SemanticTokenKind["Event"] = 11] = "Event";
32
- SemanticTokenKind[SemanticTokenKind["Function"] = 12] = "Function";
33
- SemanticTokenKind[SemanticTokenKind["Method"] = 13] = "Method";
34
- SemanticTokenKind[SemanticTokenKind["Macro"] = 14] = "Macro";
35
- SemanticTokenKind[SemanticTokenKind["Keyword"] = 15] = "Keyword";
36
- SemanticTokenKind[SemanticTokenKind["Comment"] = 16] = "Comment";
37
- SemanticTokenKind[SemanticTokenKind["String"] = 17] = "String";
38
- SemanticTokenKind[SemanticTokenKind["Number"] = 18] = "Number";
39
- SemanticTokenKind[SemanticTokenKind["Regexp"] = 19] = "Regexp";
40
- SemanticTokenKind[SemanticTokenKind["Operator"] = 20] = "Operator";
41
- SemanticTokenKind[SemanticTokenKind["DocCommentTag"] = 21] = "DocCommentTag";
42
- })(SemanticTokenKind || (SemanticTokenKind = {}));
43
- const serverOptions = {
44
- noEmit: true,
45
- designTimeBuild: true,
46
- parseOptions: {
47
- comments: true,
48
- docs: true,
49
- },
50
- };
19
+ import { SemanticTokenKind, } from "./types.js";
51
20
  export function createServer(host) {
52
- // Remember original URL when we convert it to a local path so that we can
53
- // get it back. We can't convert it back because things like URL-encoding
54
- // could give us back an equivalent but non-identical URL but the original
55
- // URL is used as a key into the opened documents and so we must reproduce
56
- // it exactly.
57
- const pathToURLMap = new Map();
21
+ const fileService = createFileService({ serverHost: host });
58
22
  // Cache all file I/O. Only open documents are sent over the LSP pipe. When
59
23
  // the compiler reads a file that isn't open, we use this cache to avoid
60
24
  // hitting the disk. Entries are invalidated when LSP client notifies us of
61
25
  // a file change.
62
- const fileSystemCache = createFileSystemCache();
26
+ const fileSystemCache = createFileSystemCache({
27
+ fileService,
28
+ });
63
29
  const compilerHost = createCompilerHost();
64
- const oldPrograms = new Map();
30
+ const compileService = createCompileService({
31
+ fileService,
32
+ fileSystemCache,
33
+ compilerHost,
34
+ serverHost: host,
35
+ log,
36
+ });
37
+ compileService.on("compileEnd", (result) => reportDiagnostics(result));
65
38
  let workspaceFolders = [];
66
39
  let isInitialized = false;
67
40
  let pendingMessages = [];
@@ -72,7 +45,7 @@ export function createServer(host) {
72
45
  get workspaceFolders() {
73
46
  return workspaceFolders;
74
47
  },
75
- compile,
48
+ compile: compileService.compile,
76
49
  initialize,
77
50
  initialized,
78
51
  workspaceFoldersChanged,
@@ -85,7 +58,7 @@ export function createServer(host) {
85
58
  findDocumentHighlight,
86
59
  prepareRename,
87
60
  rename,
88
- getSemanticTokens,
61
+ getSemanticTokens: getSemanticTokensForDocument,
89
62
  buildSemanticTokens,
90
63
  checkChange,
91
64
  getFoldingRanges,
@@ -131,7 +104,7 @@ export function createServer(host) {
131
104
  for (const w of (_b = params.workspaceFolders) !== null && _b !== void 0 ? _b : []) {
132
105
  workspaceFolders.push({
133
106
  ...w,
134
- path: ensureTrailingDirectorySeparator(await fileURLToRealPath(w.uri)),
107
+ path: ensureTrailingDirectorySeparator(await fileService.fileURLToRealPath(w.uri)),
135
108
  });
136
109
  }
137
110
  capabilities.workspace = {
@@ -148,8 +121,9 @@ export function createServer(host) {
148
121
  name: "<root>",
149
122
  // eslint-disable-next-line deprecation/deprecation
150
123
  uri: params.rootUri,
124
+ path: ensureTrailingDirectorySeparator(
151
125
  // eslint-disable-next-line deprecation/deprecation
152
- path: ensureTrailingDirectorySeparator(await fileURLToRealPath(params.rootUri)),
126
+ await fileService.fileURLToRealPath(params.rootUri)),
153
127
  },
154
128
  ];
155
129
  // eslint-disable-next-line deprecation/deprecation
@@ -182,7 +156,7 @@ export function createServer(host) {
182
156
  for (const folder of e.added) {
183
157
  map.set(folder.uri, {
184
158
  ...folder,
185
- path: ensureTrailingDirectorySeparator(await fileURLToRealPath(folder.uri)),
159
+ path: ensureTrailingDirectorySeparator(await fileService.fileURLToRealPath(folder.uri)),
186
160
  });
187
161
  }
188
162
  workspaceFolders = Array.from(map.values());
@@ -191,85 +165,8 @@ export function createServer(host) {
191
165
  function watchedFilesChanged(params) {
192
166
  fileSystemCache.notify(params.changes);
193
167
  }
194
- async function compile(document, callback) {
195
- const path = await getPath(document);
196
- const mainFile = await getMainFileForDocument(path);
197
- const config = await getConfig(mainFile);
198
- const [optionsFromConfig, _] = resolveOptionsFromConfig(config, { cwd: path });
199
- const options = {
200
- ...optionsFromConfig,
201
- ...serverOptions,
202
- };
203
- if (!upToDate(document)) {
204
- return undefined;
205
- }
206
- let program;
207
- try {
208
- program = await compileProgram(compilerHost, mainFile, options, oldPrograms.get(mainFile));
209
- oldPrograms.set(mainFile, program);
210
- if (!upToDate(document)) {
211
- return undefined;
212
- }
213
- if (mainFile !== path && !program.sourceFiles.has(path)) {
214
- // If the file that changed wasn't imported by anything from the main
215
- // file, retry using the file itself as the main file.
216
- program = await compileProgram(compilerHost, path, options, oldPrograms.get(path));
217
- oldPrograms.set(path, program);
218
- }
219
- if (!upToDate(document)) {
220
- return undefined;
221
- }
222
- if (callback) {
223
- const doc = "version" in document ? document : host.getOpenDocumentByURL(document.uri);
224
- compilerAssert(doc, "Failed to get document.");
225
- const path = await getPath(doc);
226
- const script = program.sourceFiles.get(path);
227
- compilerAssert(script, "Failed to get script.");
228
- return await callback(program, doc, script);
229
- }
230
- return program;
231
- }
232
- catch (err) {
233
- if (host.throwInternalErrors) {
234
- throw err;
235
- }
236
- host.sendDiagnostics({
237
- uri: document.uri,
238
- diagnostics: [
239
- {
240
- severity: DiagnosticSeverity.Error,
241
- range: Range.create(0, 0, 0, 0),
242
- message: `Internal compiler error!\nFile issue at https://github.com/microsoft/typespec\n\n` +
243
- err.stack,
244
- },
245
- ],
246
- });
247
- return undefined;
248
- }
249
- }
250
- async function getConfig(mainFile) {
251
- const entrypointStat = await host.compilerHost.stat(mainFile);
252
- const lookupDir = entrypointStat.isDirectory() ? mainFile : getDirectoryPath(mainFile);
253
- const configPath = await findTypeSpecConfigPath(compilerHost, lookupDir, true);
254
- if (!configPath) {
255
- return { ...defaultConfig, projectRoot: getDirectoryPath(mainFile) };
256
- }
257
- const cached = await fileSystemCache.get(configPath);
258
- if (cached === null || cached === void 0 ? void 0 : cached.data) {
259
- return cached.data;
260
- }
261
- const config = await loadTypeSpecConfigFile(compilerHost, configPath);
262
- await fileSystemCache.setData(configPath, config);
263
- return config;
264
- }
265
- async function getScript(document) {
266
- var _a;
267
- const file = await compilerHost.readFile(await getPath(document));
268
- const cached = (_a = compilerHost.parseCache) === null || _a === void 0 ? void 0 : _a.get(file);
269
- return cached !== null && cached !== void 0 ? cached : (await compile(document, (_, __, script) => script));
270
- }
271
168
  async function getFoldingRanges(params) {
272
- const ast = await getScript(params.textDocument);
169
+ const ast = await compileService.getScript(params.textDocument);
273
170
  if (!ast) {
274
171
  return [];
275
172
  }
@@ -323,29 +220,29 @@ export function createServer(host) {
323
220
  }
324
221
  }
325
222
  async function getDocumentSymbols(params) {
326
- const ast = await getScript(params.textDocument);
223
+ const ast = await compileService.getScript(params.textDocument);
327
224
  if (!ast) {
328
225
  return [];
329
226
  }
330
227
  return getSymbolStructure(ast);
331
228
  }
332
229
  async function findDocumentHighlight(params) {
333
- let highlights = [];
334
- await compile(params.textDocument, (program, document, file) => {
335
- const identifiers = findReferenceIdentifiers(program, file, document.offsetAt(params.position), [file]);
336
- highlights = identifiers.map((identifier) => ({
337
- range: getRange(identifier, file.file),
338
- kind: DocumentHighlightKind.Read,
339
- }));
340
- });
341
- return highlights;
230
+ const result = await compileService.compile(params.textDocument);
231
+ if (result === undefined) {
232
+ return [];
233
+ }
234
+ const { program, document, script } = result;
235
+ const identifiers = findReferenceIdentifiers(program, script, document.offsetAt(params.position), [script]);
236
+ return identifiers.map((identifier) => ({
237
+ range: getRange(identifier, script.file),
238
+ kind: DocumentHighlightKind.Read,
239
+ }));
342
240
  }
343
241
  async function checkChange(change) {
242
+ compileService.notifyChange(change.document);
243
+ }
244
+ async function reportDiagnostics({ program, document }) {
344
245
  var _a, _b, _c;
345
- const program = await compile(change.document);
346
- if (!program) {
347
- return;
348
- }
349
246
  // Group diagnostics by file.
350
247
  //
351
248
  // Initialize diagnostics for all source files in program to empty array
@@ -353,7 +250,7 @@ export function createServer(host) {
353
250
  // stale diagnostics from a previous run will stick around in the IDE.
354
251
  //
355
252
  const diagnosticMap = new Map();
356
- diagnosticMap.set(change.document, []);
253
+ diagnosticMap.set(document, []);
357
254
  for (const each of program.sourceFiles.values()) {
358
255
  const document = (_a = each.file) === null || _a === void 0 ? void 0 : _a.document;
359
256
  if (document) {
@@ -361,10 +258,10 @@ export function createServer(host) {
361
258
  }
362
259
  }
363
260
  for (const each of program.diagnostics) {
364
- let document;
261
+ let diagDocument;
365
262
  const location = getSourceLocation(each.target, { locateId: true });
366
263
  if (location === null || location === void 0 ? void 0 : location.file) {
367
- document = location.file.document;
264
+ diagDocument = location.file.document;
368
265
  }
369
266
  else {
370
267
  // https://github.com/microsoft/language-server-protocol/issues/256
@@ -372,20 +269,20 @@ export function createServer(host) {
372
269
  // LSP does not currently allow sending a diagnostic with no location so
373
270
  // we report diagnostics with no location on the document that changed to
374
271
  // trigger.
375
- document = change.document;
272
+ diagDocument = document;
376
273
  }
377
- if (!document || !upToDate(document)) {
274
+ if (!diagDocument || !fileService.upToDate(diagDocument)) {
378
275
  continue;
379
276
  }
380
- const start = document.positionAt((_b = location === null || location === void 0 ? void 0 : location.pos) !== null && _b !== void 0 ? _b : 0);
381
- const end = document.positionAt((_c = location === null || location === void 0 ? void 0 : location.end) !== null && _c !== void 0 ? _c : 0);
277
+ const start = diagDocument.positionAt((_b = location === null || location === void 0 ? void 0 : location.pos) !== null && _b !== void 0 ? _b : 0);
278
+ const end = diagDocument.positionAt((_c = location === null || location === void 0 ? void 0 : location.end) !== null && _c !== void 0 ? _c : 0);
382
279
  const range = Range.create(start, end);
383
280
  const severity = convertSeverity(each.severity);
384
281
  const diagnostic = VSDiagnostic.create(range, each.message, severity, each.code, "TypeSpec");
385
282
  if (each.code === "deprecated") {
386
283
  diagnostic.tags = [DiagnosticTag.Deprecated];
387
284
  }
388
- const diagnostics = diagnosticMap.get(document);
285
+ const diagnostics = diagnosticMap.get(diagDocument);
389
286
  compilerAssert(diagnostics, "Diagnostic reported against a source file that was not added to the program.");
390
287
  diagnostics.push(diagnostic);
391
288
  }
@@ -394,40 +291,42 @@ export function createServer(host) {
394
291
  }
395
292
  }
396
293
  async function getHover(params) {
397
- const docString = await compile(params.textDocument, (program, document, file) => {
398
- const id = getNodeAtPosition(file, document.offsetAt(params.position));
399
- const sym = (id === null || id === void 0 ? void 0 : id.kind) === SyntaxKind.Identifier ? program.checker.resolveIdentifier(id) : undefined;
400
- if (sym) {
401
- return getSymbolDetails(program, sym);
402
- }
403
- return undefined;
404
- });
294
+ const result = await compileService.compile(params.textDocument);
295
+ if (result === undefined) {
296
+ return { contents: [] };
297
+ }
298
+ const { program, document, script } = result;
299
+ const id = getNodeAtPosition(script, document.offsetAt(params.position));
300
+ const sym = (id === null || id === void 0 ? void 0 : id.kind) === SyntaxKind.Identifier ? program.checker.resolveIdentifier(id) : undefined;
405
301
  const markdown = {
406
302
  kind: MarkupKind.Markdown,
407
- value: docString !== null && docString !== void 0 ? docString : "",
303
+ value: sym ? getSymbolDetails(program, sym) : "",
408
304
  };
409
305
  return {
410
306
  contents: markdown,
411
307
  };
412
308
  }
413
309
  async function getSignatureHelp(params) {
414
- return await compile(params.textDocument, (program, document, file) => {
415
- const data = getSignatureHelpNodeAtPosition(file, document.offsetAt(params.position));
416
- if (data === undefined) {
417
- return undefined;
418
- }
419
- const { node, argumentIndex } = data;
420
- switch (node.kind) {
421
- case SyntaxKind.TypeReference:
422
- return getSignatureHelpForTemplate(program, node, argumentIndex);
423
- case SyntaxKind.DecoratorExpression:
424
- case SyntaxKind.AugmentDecoratorStatement:
425
- return getSignatureHelpForDecorator(program, node, argumentIndex);
426
- default:
427
- const _assertNever = node;
428
- compilerAssert(false, "Unreachable");
429
- }
430
- });
310
+ const result = await compileService.compile(params.textDocument);
311
+ if (result === undefined) {
312
+ return undefined;
313
+ }
314
+ const { script, document, program } = result;
315
+ const data = getSignatureHelpNodeAtPosition(script, document.offsetAt(params.position));
316
+ if (data === undefined) {
317
+ return undefined;
318
+ }
319
+ const { node, argumentIndex } = data;
320
+ switch (node.kind) {
321
+ case SyntaxKind.TypeReference:
322
+ return getSignatureHelpForTemplate(program, node, argumentIndex);
323
+ case SyntaxKind.DecoratorExpression:
324
+ case SyntaxKind.AugmentDecoratorStatement:
325
+ return getSignatureHelpForDecorator(program, node, argumentIndex);
326
+ default:
327
+ const _assertNever = node;
328
+ compilerAssert(false, "Unreachable");
329
+ }
431
330
  }
432
331
  function getSignatureHelpForTemplate(program, node, argumentIndex) {
433
332
  const sym = program.checker.resolveIdentifier(node.target.kind === SyntaxKind.MemberExpression ? node.target.id : node.target);
@@ -551,10 +450,12 @@ export function createServer(host) {
551
450
  return TextEdit.replace(Range.create(pos0, pos1), newText);
552
451
  }
553
452
  async function gotoDefinition(params) {
554
- const sym = await compile(params.textDocument, (program, document, file) => {
555
- const id = getNodeAtPosition(file, document.offsetAt(params.position));
556
- return (id === null || id === void 0 ? void 0 : id.kind) === SyntaxKind.Identifier ? program.checker.resolveIdentifier(id) : undefined;
557
- });
453
+ const result = await compileService.compile(params.textDocument);
454
+ if (result === undefined) {
455
+ return [];
456
+ }
457
+ const id = getNodeAtPosition(result.script, result.document.offsetAt(params.position));
458
+ const sym = (id === null || id === void 0 ? void 0 : id.kind) === SyntaxKind.Identifier ? result.program.checker.resolveIdentifier(id) : undefined;
558
459
  return getLocations(sym === null || sym === void 0 ? void 0 : sym.declarations);
559
460
  }
560
461
  async function complete(params) {
@@ -562,32 +463,41 @@ export function createServer(host) {
562
463
  isIncomplete: false,
563
464
  items: [],
564
465
  };
565
- await compile(params.textDocument, async (program, document, file) => {
566
- const node = getCompletionNodeAtPosition(file, document.offsetAt(params.position));
466
+ const result = await compileService.compile(params.textDocument);
467
+ if (result) {
468
+ const { script, document, program } = result;
469
+ const node = getCompletionNodeAtPosition(script, document.offsetAt(params.position));
567
470
  await resolveCompletion({
568
471
  program,
569
- file,
472
+ file: script,
570
473
  completions,
571
474
  params,
572
475
  }, node);
573
- });
476
+ }
574
477
  return completions;
575
478
  }
576
479
  async function findReferences(params) {
577
- const identifiers = await compile(params.textDocument, (program, document, file) => findReferenceIdentifiers(program, file, document.offsetAt(params.position)));
480
+ const result = await compileService.compile(params.textDocument);
481
+ if (result === undefined) {
482
+ return [];
483
+ }
484
+ const identifiers = findReferenceIdentifiers(result.program, result.script, result.document.offsetAt(params.position));
578
485
  return getLocations(identifiers);
579
486
  }
580
487
  async function prepareRename(params) {
581
- return await compile(params.textDocument, (_, document, file) => {
582
- var _a;
583
- const id = getNodeAtPosition(file, document.offsetAt(params.position));
584
- return (id === null || id === void 0 ? void 0 : id.kind) === SyntaxKind.Identifier ? (_a = getLocation(id)) === null || _a === void 0 ? void 0 : _a.range : undefined;
585
- });
488
+ var _a;
489
+ const result = await compileService.compile(params.textDocument);
490
+ if (result === undefined) {
491
+ return undefined;
492
+ }
493
+ const id = getNodeAtPosition(result.script, result.document.offsetAt(params.position));
494
+ return (id === null || id === void 0 ? void 0 : id.kind) === SyntaxKind.Identifier ? (_a = getLocation(id)) === null || _a === void 0 ? void 0 : _a.range : undefined;
586
495
  }
587
496
  async function rename(params) {
588
497
  const changes = {};
589
- await compile(params.textDocument, (program, document, file) => {
590
- const identifiers = findReferenceIdentifiers(program, file, document.offsetAt(params.position));
498
+ const result = await compileService.compile(params.textDocument);
499
+ if (result) {
500
+ const identifiers = findReferenceIdentifiers(result.program, result.script, result.document.offsetAt(params.position));
591
501
  for (const id of identifiers) {
592
502
  const location = getLocation(id);
593
503
  if (!location) {
@@ -601,7 +511,7 @@ export function createServer(host) {
601
511
  changes[location.uri] = [change];
602
512
  }
603
513
  }
604
- });
514
+ }
605
515
  return { changes };
606
516
  }
607
517
  function findReferenceIdentifiers(program, file, pos, searchFiles = program.sourceFiles.values()) {
@@ -627,317 +537,17 @@ export function createServer(host) {
627
537
  }
628
538
  return references;
629
539
  }
630
- async function getSemanticTokens(params) {
631
- const ignore = -1;
632
- const defer = -2;
633
- const ast = await getScript(params.textDocument);
540
+ async function getSemanticTokensForDocument(params) {
541
+ const ast = await compileService.getScript(params.textDocument);
634
542
  if (!ast) {
635
543
  return [];
636
544
  }
637
- const file = ast.file;
638
- const tokens = mapTokens();
639
- classifyNode(ast);
640
- return Array.from(tokens.values()).filter((t) => t.kind !== undefined);
641
- function mapTokens() {
642
- const tokens = new Map();
643
- const scanner = createScanner(file, () => { });
644
- const templateStack = [];
645
- while (scanner.scan() !== Token.EndOfFile) {
646
- if (scanner.tokenFlags & TokenFlags.DocComment) {
647
- classifyDocComment({ pos: scanner.tokenPosition, end: scanner.position });
648
- }
649
- else {
650
- handleToken(scanner.token, scanner.tokenFlags, {
651
- pos: scanner.tokenPosition,
652
- end: scanner.position,
653
- });
654
- }
655
- }
656
- return tokens;
657
- function classifyDocComment(range) {
658
- scanner.scanRange(range, () => {
659
- while (scanner.scanDoc() !== Token.EndOfFile) {
660
- const kind = classifyDocToken(scanner.token);
661
- if (kind === ignore) {
662
- continue;
663
- }
664
- tokens.set(scanner.tokenPosition, {
665
- kind: kind === defer ? undefined : kind,
666
- pos: scanner.tokenPosition,
667
- end: scanner.position,
668
- });
669
- }
670
- });
671
- }
672
- function handleToken(token, tokenFlags, range) {
673
- switch (token) {
674
- case Token.StringTemplateHead:
675
- templateStack.push([token, tokenFlags]);
676
- classifyStringTemplate(token, range);
677
- break;
678
- case Token.OpenBrace:
679
- // If we don't have anything on the template stack,
680
- // then we aren't trying to keep track of a previously scanned template head.
681
- if (templateStack.length > 0) {
682
- templateStack.push([token, tokenFlags]);
683
- }
684
- handleSimpleToken(token, range);
685
- break;
686
- case Token.CloseBrace:
687
- // If we don't have anything on the template stack,
688
- // then we aren't trying to keep track of a previously scanned template head.
689
- if (templateStack.length > 0) {
690
- const [lastToken, lastTokenFlags] = templateStack[templateStack.length - 1];
691
- if (lastToken === Token.StringTemplateHead) {
692
- token = scanner.reScanStringTemplate(lastTokenFlags);
693
- // Only pop on a TemplateTail; a TemplateMiddle indicates there is more for us.
694
- if (token === Token.StringTemplateTail) {
695
- templateStack.pop();
696
- classifyStringTemplate(token, {
697
- pos: scanner.tokenPosition,
698
- end: scanner.position,
699
- });
700
- }
701
- else {
702
- compilerAssert(token === Token.StringTemplateMiddle, "Should have been a template middle.");
703
- classifyStringTemplate(token, {
704
- pos: scanner.tokenPosition,
705
- end: scanner.position,
706
- });
707
- }
708
- }
709
- else {
710
- compilerAssert(lastToken === Token.OpenBrace, "Should have been an open brace");
711
- templateStack.pop();
712
- }
713
- break;
714
- }
715
- handleSimpleToken(token, range);
716
- break;
717
- default:
718
- handleSimpleToken(token, range);
719
- }
720
- }
721
- function handleSimpleToken(token, range) {
722
- const kind = classifyToken(scanner.token);
723
- if (kind === ignore) {
724
- return;
725
- }
726
- tokens.set(range.pos, {
727
- kind: kind === defer ? undefined : kind,
728
- ...range,
729
- });
730
- }
731
- function classifyStringTemplate(token, range) {
732
- const stringStart = token === Token.StringTemplateHead ? range.pos : range.pos + 1;
733
- const stringEnd = token === Token.StringTemplateTail ? range.end : range.end - 2;
734
- if (stringStart !== range.pos) {
735
- tokens.set(range.pos, {
736
- kind: SemanticTokenKind.Operator,
737
- pos: range.pos,
738
- end: stringStart,
739
- });
740
- }
741
- tokens.set(stringStart, {
742
- kind: SemanticTokenKind.String,
743
- pos: stringStart,
744
- end: stringEnd,
745
- });
746
- if (stringEnd !== range.end) {
747
- tokens.set(stringEnd, {
748
- kind: SemanticTokenKind.Operator,
749
- pos: stringEnd,
750
- end: range.end,
751
- });
752
- }
753
- }
754
- }
755
- function classifyToken(token) {
756
- switch (token) {
757
- case Token.Identifier:
758
- return defer;
759
- case Token.StringLiteral:
760
- return SemanticTokenKind.String;
761
- case Token.NumericLiteral:
762
- return SemanticTokenKind.Number;
763
- case Token.MultiLineComment:
764
- case Token.SingleLineComment:
765
- return SemanticTokenKind.Comment;
766
- default:
767
- if (isKeyword(token)) {
768
- return SemanticTokenKind.Keyword;
769
- }
770
- if (isPunctuation(token)) {
771
- return SemanticTokenKind.Operator;
772
- }
773
- return ignore;
774
- }
775
- }
776
- /** Classify tokens when scanning doc comment. */
777
- function classifyDocToken(token) {
778
- switch (token) {
779
- case Token.NewLine:
780
- case Token.Whitespace:
781
- return ignore;
782
- case Token.DocText:
783
- case Token.Star:
784
- case Token.Identifier:
785
- return SemanticTokenKind.Comment;
786
- case Token.At:
787
- return defer;
788
- default:
789
- return ignore;
790
- }
791
- }
792
- function classifyNode(node) {
793
- switch (node.kind) {
794
- case SyntaxKind.DirectiveExpression:
795
- classify(node.target, SemanticTokenKind.Keyword);
796
- break;
797
- case SyntaxKind.TemplateParameterDeclaration:
798
- classify(node.id, SemanticTokenKind.TypeParameter);
799
- break;
800
- case SyntaxKind.ModelProperty:
801
- case SyntaxKind.UnionVariant:
802
- if (node.id) {
803
- classify(node.id, SemanticTokenKind.Property);
804
- }
805
- break;
806
- case SyntaxKind.AliasStatement:
807
- classify(node.id, SemanticTokenKind.Struct);
808
- break;
809
- case SyntaxKind.ModelStatement:
810
- classify(node.id, SemanticTokenKind.Struct);
811
- break;
812
- case SyntaxKind.ScalarStatement:
813
- classify(node.id, SemanticTokenKind.Type);
814
- break;
815
- case SyntaxKind.EnumStatement:
816
- classify(node.id, SemanticTokenKind.Enum);
817
- break;
818
- case SyntaxKind.EnumMember:
819
- classify(node.id, SemanticTokenKind.EnumMember);
820
- break;
821
- case SyntaxKind.NamespaceStatement:
822
- classify(node.id, SemanticTokenKind.Namespace);
823
- break;
824
- case SyntaxKind.InterfaceStatement:
825
- classify(node.id, SemanticTokenKind.Interface);
826
- break;
827
- case SyntaxKind.OperationStatement:
828
- classify(node.id, SemanticTokenKind.Function);
829
- break;
830
- case SyntaxKind.DecoratorDeclarationStatement:
831
- classify(node.id, SemanticTokenKind.Function);
832
- break;
833
- case SyntaxKind.FunctionDeclarationStatement:
834
- classify(node.id, SemanticTokenKind.Function);
835
- break;
836
- case SyntaxKind.FunctionParameter:
837
- classify(node.id, SemanticTokenKind.Parameter);
838
- break;
839
- case SyntaxKind.AugmentDecoratorStatement:
840
- classifyReference(node.targetType, SemanticTokenKind.Type);
841
- classifyReference(node.target, SemanticTokenKind.Macro);
842
- break;
843
- case SyntaxKind.DecoratorExpression:
844
- classifyReference(node.target, SemanticTokenKind.Macro);
845
- break;
846
- case SyntaxKind.TypeReference:
847
- classifyReference(node.target);
848
- break;
849
- case SyntaxKind.MemberExpression:
850
- classifyReference(node);
851
- break;
852
- case SyntaxKind.ProjectionStatement:
853
- classifyReference(node.selector);
854
- classify(node.id, SemanticTokenKind.Variable);
855
- break;
856
- case SyntaxKind.Projection:
857
- classify(node.directionId, SemanticTokenKind.Keyword);
858
- for (const modifierId of node.modifierIds) {
859
- classify(modifierId, SemanticTokenKind.Keyword);
860
- }
861
- break;
862
- case SyntaxKind.ProjectionParameterDeclaration:
863
- classifyReference(node.id, SemanticTokenKind.Parameter);
864
- break;
865
- case SyntaxKind.ProjectionCallExpression:
866
- classifyReference(node.target, SemanticTokenKind.Function);
867
- for (const arg of node.arguments) {
868
- classifyReference(arg);
869
- }
870
- break;
871
- case SyntaxKind.ProjectionMemberExpression:
872
- classifyReference(node.id);
873
- break;
874
- case SyntaxKind.DocParamTag:
875
- case SyntaxKind.DocTemplateTag:
876
- classifyDocTag(node.tagName, SemanticTokenKind.DocCommentTag);
877
- classifyOverride(node.paramName, SemanticTokenKind.Variable);
878
- break;
879
- case SyntaxKind.DocReturnsTag:
880
- classifyDocTag(node.tagName, SemanticTokenKind.DocCommentTag);
881
- break;
882
- case SyntaxKind.DocUnknownTag:
883
- classifyDocTag(node.tagName, SemanticTokenKind.Macro);
884
- break;
885
- case SyntaxKind.TemplateArgument:
886
- if (node.name)
887
- classify(node.name, SemanticTokenKind.TypeParameter);
888
- break;
889
- default:
890
- break;
891
- }
892
- visitChildren(node, classifyNode);
893
- }
894
- function classifyDocTag(node, kind) {
895
- classifyOverride(node, kind);
896
- const token = tokens.get(node.pos - 1); // Get the `@` token
897
- if (token) {
898
- token.kind = kind;
899
- }
900
- }
901
- function classify(node, kind) {
902
- const token = tokens.get(node.pos);
903
- if (token && token.kind === undefined) {
904
- token.kind = kind;
905
- }
906
- }
907
- function classifyOverride(node, kind) {
908
- const token = tokens.get(node.pos);
909
- if (token) {
910
- token.kind = kind;
911
- }
912
- }
913
- function classifyReference(node, kind = SemanticTokenKind.Type) {
914
- switch (node.kind) {
915
- case SyntaxKind.MemberExpression:
916
- classifyIdentifier(node.base, SemanticTokenKind.Namespace);
917
- classifyIdentifier(node.id, kind);
918
- break;
919
- case SyntaxKind.ProjectionMemberExpression:
920
- classifyReference(node.base, SemanticTokenKind.Namespace);
921
- classifyIdentifier(node.id, kind);
922
- break;
923
- case SyntaxKind.TypeReference:
924
- classifyIdentifier(node.target, kind);
925
- break;
926
- case SyntaxKind.Identifier:
927
- classify(node, kind);
928
- break;
929
- }
930
- }
931
- function classifyIdentifier(node, kind) {
932
- if (node.kind === SyntaxKind.Identifier) {
933
- classify(node, kind);
934
- }
935
- }
545
+ return getSemanticTokens(ast);
936
546
  }
937
547
  async function buildSemanticTokens(params) {
938
548
  const builder = new SemanticTokensBuilder();
939
- const tokens = await getSemanticTokens(params);
940
- const file = await compilerHost.readFile(await getPath(params.textDocument));
549
+ const tokens = await getSemanticTokensForDocument(params);
550
+ const file = await compilerHost.readFile(await fileService.getPath(params.textDocument));
941
551
  const starts = file.getLineStarts();
942
552
  for (const token of tokens) {
943
553
  const start = file.getLineAndCharacterOfPosition(token.pos);
@@ -965,7 +575,7 @@ export function createServer(host) {
965
575
  return undefined;
966
576
  }
967
577
  return {
968
- uri: getURL(location.file.path),
578
+ uri: fileService.getURL(location.file.path),
969
579
  range: getRange(location, location.file),
970
580
  };
971
581
  }
@@ -1004,123 +614,6 @@ export function createServer(host) {
1004
614
  diagnostics,
1005
615
  });
1006
616
  }
1007
- /**
1008
- * Determine if the given document is the latest version.
1009
- *
1010
- * A document can become out-of-date if a change comes in during an async
1011
- * operation.
1012
- */
1013
- function upToDate(document) {
1014
- var _a;
1015
- if (!("version" in document)) {
1016
- return true;
1017
- }
1018
- return document.version === ((_a = host.getOpenDocumentByURL(document.uri)) === null || _a === void 0 ? void 0 : _a.version);
1019
- }
1020
- /**
1021
- * Infer the appropriate entry point (a.k.a. "main file") for analyzing a
1022
- * change to the file at the given path. This is necessary because different
1023
- * results can be obtained from compiling the same file with different entry
1024
- * points.
1025
- *
1026
- * Walk directory structure upwards looking for package.json with tspMain or
1027
- * main.tsp file. Stop search when reaching a workspace root. If a root is
1028
- * reached without finding an entry point, use the given path as its own
1029
- * entry point.
1030
- *
1031
- * Untitled documents are always treated as their own entry points as they
1032
- * do not exist in a directory that could pull them in via another entry
1033
- * point.
1034
- */
1035
- async function getMainFileForDocument(path) {
1036
- if (path.startsWith("untitled:")) {
1037
- return path;
1038
- }
1039
- let dir = getDirectoryPath(path);
1040
- const options = { allowFileNotFound: true };
1041
- while (true) {
1042
- let mainFile = "main.tsp";
1043
- let pkg;
1044
- const pkgPath = joinPaths(dir, "package.json");
1045
- const cached = await fileSystemCache.get(pkgPath);
1046
- if (cached === null || cached === void 0 ? void 0 : cached.data) {
1047
- pkg = cached.data;
1048
- }
1049
- else {
1050
- [pkg] = await loadFile(compilerHost, pkgPath, JSON.parse, logMainFileSearchDiagnostic, options);
1051
- await fileSystemCache.setData(pkgPath, pkg !== null && pkg !== void 0 ? pkg : {});
1052
- }
1053
- const tspMain = resolveTspMain(pkg);
1054
- if (typeof tspMain === "string") {
1055
- mainFile = tspMain;
1056
- }
1057
- const candidate = joinPaths(dir, mainFile);
1058
- const stat = await doIO(() => compilerHost.stat(candidate), candidate, logMainFileSearchDiagnostic, options);
1059
- if (stat === null || stat === void 0 ? void 0 : stat.isFile()) {
1060
- return candidate;
1061
- }
1062
- const parentDir = getDirectoryPath(dir);
1063
- if (parentDir === dir) {
1064
- break;
1065
- }
1066
- dir = parentDir;
1067
- }
1068
- return path;
1069
- function logMainFileSearchDiagnostic(diagnostic) {
1070
- log(`Unexpected diagnostic while looking for main file of ${path}`, formatDiagnostic(diagnostic));
1071
- }
1072
- }
1073
- async function getPath(document) {
1074
- if (isUntitled(document.uri)) {
1075
- return document.uri;
1076
- }
1077
- const path = await fileURLToRealPath(document.uri);
1078
- pathToURLMap.set(path, document.uri);
1079
- return path;
1080
- }
1081
- function getURL(path) {
1082
- var _a;
1083
- if (isUntitled(path)) {
1084
- return path;
1085
- }
1086
- return (_a = pathToURLMap.get(path)) !== null && _a !== void 0 ? _a : compilerHost.pathToFileURL(path);
1087
- }
1088
- function isUntitled(pathOrUrl) {
1089
- return pathOrUrl.startsWith("untitled:");
1090
- }
1091
- function getOpenDocument(path) {
1092
- const url = getURL(path);
1093
- return url ? host.getOpenDocumentByURL(url) : undefined;
1094
- }
1095
- async function fileURLToRealPath(url) {
1096
- return getNormalizedRealPath(compilerHost, compilerHost.fileURLToPath(url));
1097
- }
1098
- function createFileSystemCache() {
1099
- const cache = new Map();
1100
- let changes = [];
1101
- return {
1102
- async get(path) {
1103
- for (const change of changes) {
1104
- const path = await fileURLToRealPath(change.uri);
1105
- cache.delete(path);
1106
- }
1107
- changes = [];
1108
- return cache.get(path);
1109
- },
1110
- set(path, entry) {
1111
- cache.set(path, entry);
1112
- },
1113
- async setData(path, data) {
1114
- const entry = await this.get(path);
1115
- if (entry) {
1116
- entry.data = data;
1117
- }
1118
- },
1119
- notify(changes) {
1120
- changes.push(...changes);
1121
- },
1122
- };
1123
- }
1124
617
  function createCompilerHost() {
1125
618
  const base = host.compilerHost;
1126
619
  return {
@@ -1131,7 +624,7 @@ export function createServer(host) {
1131
624
  getSourceFileKind,
1132
625
  };
1133
626
  async function readFile(path) {
1134
- const document = getOpenDocument(path);
627
+ const document = fileService.getOpenDocument(path);
1135
628
  const cached = await fileSystemCache.get(path);
1136
629
  // Try cache
1137
630
  if (cached && (!document || document.version === cached.version)) {
@@ -1165,7 +658,7 @@ export function createServer(host) {
1165
658
  var _a;
1166
659
  // if we have an open document for the path or a cache entry, then we know
1167
660
  // it's a file and not a directory and needn't hit the disk.
1168
- if (getOpenDocument(path) || ((_a = (await fileSystemCache.get(path))) === null || _a === void 0 ? void 0 : _a.type) === "file") {
661
+ if (fileService.getOpenDocument(path) || ((_a = (await fileSystemCache.get(path))) === null || _a === void 0 ? void 0 : _a.type) === "file") {
1169
662
  return {
1170
663
  isFile() {
1171
664
  return true;
@@ -1178,7 +671,7 @@ export function createServer(host) {
1178
671
  return await base.stat(path);
1179
672
  }
1180
673
  function getSourceFileKind(path) {
1181
- const document = getOpenDocument(path);
674
+ const document = fileService.getOpenDocument(path);
1182
675
  if ((document === null || document === void 0 ? void 0 : document.languageId) === "typespec") {
1183
676
  return "typespec";
1184
677
  }