@prisma-next/language-server 0.14.0-dev.46 → 0.14.0-dev.48

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.
@@ -1,8 +1,8 @@
1
- import { join } from "node:path";
2
1
  import { fileURLToPath, pathToFileURL } from "node:url";
3
2
  import { findNearestConfigPathForFile, loadConfig } from "@prisma-next/config-loader";
4
3
  import { format } from "@prisma-next/psl-parser/format";
5
- import { CompletionItemKind, DiagnosticSeverity, DidChangeWatchedFilesNotification, FoldingRangeKind, InsertTextFormat, RegistrationRequest, SemanticTokenModifiers, SemanticTokenTypes, TextDocumentSyncKind, TextDocuments } from "vscode-languageserver";
4
+ import { join } from "pathe";
5
+ import { CompletionItemKind, DiagnosticSeverity, DidChangeWatchedFilesNotification, DocumentDiagnosticReportKind, FoldingRangeKind, InsertTextFormat, RegistrationRequest, SemanticTokenModifiers, SemanticTokenTypes, TextDocumentSyncKind, TextDocuments } from "vscode-languageserver";
6
6
  import { TextDocument } from "vscode-languageserver-textdocument";
7
7
  import { ArrayLiteralAst, AttributeArgListAst, BooleanLiteralExprAst, CompositeTypeDeclarationAst, FieldAttributeAst, FieldDeclarationAst, FunctionCallAst, GenericBlockDeclarationAst, IdentifierAst, KeyValuePairAst, ModelAttributeAst, ModelDeclarationAst, NamespaceDeclarationAst, NumberLiteralExprAst, ObjectLiteralExprAst, StringLiteralExprAst, TypesBlockAst, any, filterChildren, findChildToken, parse, skipTriviaToken } from "@prisma-next/psl-parser/syntax";
8
8
  import { isAuthoringPslBlockDescriptor } from "@prisma-next/framework-components/authoring";
@@ -348,7 +348,7 @@ function provideModelTypeCompletionItems(context, sourceFile, source) {
348
348
  }
349
349
  function provideNamespaceMemberCompletionItems(context, sourceFile, source) {
350
350
  if (context.space !== void 0) return [];
351
- return modelTypeCompletionItems(context, sourceFile, namespaceCandidates(source.symbolTable?.topLevel.namespaces[context.namespace]));
351
+ return modelTypeCompletionItems(context, sourceFile, namespaceCandidates(source.symbolTable.topLevel.namespaces[context.namespace]));
352
352
  }
353
353
  function modelTypeCompletionItems(context, sourceFile, candidates) {
354
354
  const replacementRange = {
@@ -378,7 +378,6 @@ function configuredScalarCandidates(scalarTypes) {
378
378
  }));
379
379
  }
380
380
  function topLevelSymbolCandidates(symbolTable) {
381
- if (symbolTable === void 0) return [];
382
381
  const { topLevel } = symbolTable;
383
382
  return [
384
383
  ...symbolCandidates(recordNames(topLevel.models), "topLevelModel", "Model", CompletionItemKind.Class),
@@ -388,7 +387,6 @@ function topLevelSymbolCandidates(symbolTable) {
388
387
  ];
389
388
  }
390
389
  function allNamespaceCandidates(symbolTable) {
391
- if (symbolTable === void 0) return [];
392
390
  return Object.values(symbolTable.topLevel.namespaces).sort((left, right) => compareNames(left.name, right.name)).map(namespaceQualifierCandidate);
393
391
  }
394
392
  function namespaceCandidates(namespace) {
@@ -441,7 +439,10 @@ function hasPslInputs(config) {
441
439
  function resolveSchemaInputs(config) {
442
440
  const inputs = hasPslInputs(config) ? config.contract?.source.inputs : void 0;
443
441
  const uris = new Set(inputs?.map((input) => pathToFileURL(input).toString()));
444
- return { includes: (uri) => uris.has(uri) };
442
+ return {
443
+ includes: (uri) => uris.has(uri),
444
+ uris: () => uris
445
+ };
445
446
  }
446
447
  //#endregion
447
448
  //#region src/config-resolution.ts
@@ -552,29 +553,48 @@ function computeDocumentDiagnostics(uri, text, inputs, controlStack) {
552
553
  }
553
554
  //#endregion
554
555
  //#region src/project-artifacts.ts
555
- function createProjectArtifacts() {
556
+ function createProjectArtifacts(options) {
557
+ const { inputs, controlStack, getText } = options;
556
558
  const documents = /* @__PURE__ */ new Map();
557
559
  let symbolTable;
560
+ function drop(uri) {
561
+ if (documents.delete(uri)) symbolTable = void 0;
562
+ }
563
+ function readDocument(uri) {
564
+ const existing = documents.get(uri);
565
+ if (existing !== void 0) return existing;
566
+ const text = getText(uri);
567
+ if (text === void 0) return;
568
+ const computed = computeDocumentDiagnostics(uri, text, inputs, controlStack);
569
+ if (computed === null) return;
570
+ const artifacts = {
571
+ document: computed.document,
572
+ sourceFile: computed.sourceFile,
573
+ diagnostics: computed.diagnostics
574
+ };
575
+ documents.set(uri, artifacts);
576
+ symbolTable = computed.symbolTable;
577
+ return artifacts;
578
+ }
558
579
  return {
559
- getDocument: (uri) => documents.get(uri),
560
- getSymbolTable: () => symbolTable,
561
- update: (uri, text, inputs, controlStack) => {
562
- const computed = computeDocumentDiagnostics(uri, text, inputs, controlStack);
563
- if (computed === null) {
564
- if (documents.delete(uri)) symbolTable = void 0;
565
- return null;
580
+ document: readDocument,
581
+ symbolTable: () => {
582
+ if (symbolTable !== void 0) return symbolTable;
583
+ for (const uri of inputs.uris()) {
584
+ const artifacts = readDocument(uri);
585
+ if (artifacts === void 0) continue;
586
+ symbolTable ??= buildSymbolTable({
587
+ document: artifacts.document,
588
+ sourceFile: artifacts.sourceFile,
589
+ scalarTypes: controlStack.scalarTypes,
590
+ pslBlockDescriptors: controlStack.pslBlockDescriptors
591
+ }).table;
592
+ return symbolTable;
566
593
  }
567
- documents.set(uri, {
568
- document: computed.document,
569
- sourceFile: computed.sourceFile,
570
- text
571
- });
572
- symbolTable = computed.symbolTable;
573
- return computed.diagnostics;
594
+ throw new Error("invariant violated: project has no open configured input — the server must drop such projects");
574
595
  },
575
- remove: (uri) => {
576
- if (documents.delete(uri)) symbolTable = void 0;
577
- }
596
+ documentChanged: drop,
597
+ documentClosed: drop
578
598
  };
579
599
  }
580
600
  //#endregion
@@ -827,21 +847,19 @@ function classifyTypeReference(path, source, namespace) {
827
847
  if (name === void 0) return { tokenType: "type" };
828
848
  const table = source.symbolTable;
829
849
  const namespaceName = path.length > 1 ? path[path.length - 2] : namespace;
830
- const namespaceScope = namespaceName !== void 0 ? table?.topLevel.namespaces[namespaceName] : void 0;
850
+ const namespaceScope = namespaceName !== void 0 ? table.topLevel.namespaces[namespaceName] : void 0;
831
851
  if (namespaceScope !== void 0) {
832
852
  if (Object.hasOwn(namespaceScope.models, name)) return { tokenType: "class" };
833
853
  if (Object.hasOwn(namespaceScope.compositeTypes, name)) return { tokenType: "struct" };
834
854
  if (Object.hasOwn(namespaceScope.blocks, name)) return { tokenType: "type" };
835
855
  }
836
- if (table !== void 0) {
837
- if (Object.hasOwn(table.topLevel.models, name)) return { tokenType: "class" };
838
- if (Object.hasOwn(table.topLevel.compositeTypes, name)) return { tokenType: "struct" };
839
- if (Object.hasOwn(table.topLevel.scalars, name)) return {
840
- tokenType: "type",
841
- modifierBitset: semanticTokenModifierBits.defaultLibrary
842
- };
843
- if (Object.hasOwn(table.topLevel.typeAliases, name) || Object.hasOwn(table.topLevel.blocks, name)) return { tokenType: "type" };
844
- }
856
+ if (Object.hasOwn(table.topLevel.models, name)) return { tokenType: "class" };
857
+ if (Object.hasOwn(table.topLevel.compositeTypes, name)) return { tokenType: "struct" };
858
+ if (Object.hasOwn(table.topLevel.scalars, name)) return {
859
+ tokenType: "type",
860
+ modifierBitset: semanticTokenModifierBits.defaultLibrary
861
+ };
862
+ if (Object.hasOwn(table.topLevel.typeAliases, name) || Object.hasOwn(table.topLevel.blocks, name)) return { tokenType: "type" };
845
863
  if (source.scalarTypes.includes(name)) return {
846
864
  tokenType: "type",
847
865
  modifierBitset: semanticTokenModifierBits.defaultLibrary
@@ -849,7 +867,7 @@ function classifyTypeReference(path, source, namespace) {
849
867
  return { tokenType: "type" };
850
868
  }
851
869
  function isKnownNamespace(name, table) {
852
- return table !== void 0 && Object.hasOwn(table.topLevel.namespaces, name);
870
+ return Object.hasOwn(table.topLevel.namespaces, name);
853
871
  }
854
872
  function identifierSegments(name) {
855
873
  const segments = [];
@@ -917,13 +935,11 @@ function tokenTypeIndex(tokenType) {
917
935
  const semanticTokenSourceLimit = 1e5;
918
936
  function createServer(connection) {
919
937
  const documents = new TextDocuments(TextDocument);
920
- const projects = /* @__PURE__ */ new Map();
921
- const projectLoads = /* @__PURE__ */ new Map();
938
+ const managedProjects = /* @__PURE__ */ new Map();
922
939
  const documentConfigPaths = /* @__PURE__ */ new Map();
923
940
  let rootPath = process.cwd();
924
941
  let watchedConfigGlob = join(rootPath, "**", CONFIG_FILENAME);
925
- let supportsWatchedFilesRegistration = false;
926
- let clientSupportsSnippets = false;
942
+ let clientCapabilities = noClientCapabilities;
927
943
  let disposed = false;
928
944
  function sendDiagnostics(params) {
929
945
  if (disposed) return;
@@ -936,13 +952,12 @@ function createServer(connection) {
936
952
  async function publish(uri) {
937
953
  const project = await resolveProjectForDocument(uri);
938
954
  if (project === void 0) return;
939
- const document = documents.get(uri);
940
- if (document === void 0) {
955
+ if (documents.get(uri) === void 0) {
941
956
  documentConfigPaths.delete(uri);
942
957
  return;
943
958
  }
944
- const computed = project.artifacts.update(uri, document.getText(), project.inputs, project.controlStack);
945
- if (computed === null) {
959
+ const artifacts = project.artifacts.document(uri);
960
+ if (artifacts === void 0) {
946
961
  sendDiagnostics({
947
962
  uri,
948
963
  diagnostics: []
@@ -951,16 +966,27 @@ function createServer(connection) {
951
966
  }
952
967
  sendDiagnostics({
953
968
  uri,
954
- diagnostics: computed.map((diagnostic) => ({
955
- range: diagnostic.range,
956
- message: diagnostic.message,
957
- code: diagnostic.code,
958
- severity: toLspSeverity(diagnostic.severity),
959
- source: "prisma-next"
960
- }))
969
+ diagnostics: toDiagnostics(artifacts.diagnostics)
961
970
  });
962
971
  }
972
+ /**
973
+ * Project-scoped so a future multi-input symbol table can attach
974
+ * `relatedDocuments` for cross-file effects.
975
+ */
976
+ function buildDocumentDiagnosticReport(project, uri) {
977
+ const artifacts = project.artifacts.document(uri);
978
+ return {
979
+ kind: DocumentDiagnosticReportKind.Full,
980
+ items: artifacts === void 0 ? [] : toDiagnostics(artifacts.diagnostics)
981
+ };
982
+ }
963
983
  async function resolveProjectForDocument(uri) {
984
+ const project = await projectForNearestConfig(uri);
985
+ if (project === void 0 || project.inputs.includes(uri)) return project;
986
+ documentConfigPaths.delete(uri);
987
+ dropProjectWithoutManagedDocuments(project.configPath);
988
+ }
989
+ async function projectForNearestConfig(uri) {
964
990
  const knownConfigPath = documentConfigPaths.get(uri);
965
991
  if (knownConfigPath !== void 0) {
966
992
  const project = await resolveProjectIfLoadable(knownConfigPath);
@@ -969,7 +995,12 @@ function createServer(connection) {
969
995
  }
970
996
  const filePath = filePathFromUri(uri);
971
997
  if (filePath === void 0) return;
972
- const configPath = await findNearestConfigPathForFile(filePath);
998
+ let configPath;
999
+ try {
1000
+ configPath = await findNearestConfigPathForFile(filePath);
1001
+ } catch {
1002
+ return;
1003
+ }
973
1004
  if (configPath === void 0) return;
974
1005
  documentConfigPaths.set(uri, configPath);
975
1006
  const project = await resolveProjectIfLoadable(configPath);
@@ -985,26 +1016,44 @@ function createServer(connection) {
985
1016
  }
986
1017
  }
987
1018
  async function resolveProject(configPath) {
988
- const existing = projects.get(configPath);
989
- if (existing !== void 0) return existing;
990
- const existingLoad = projectLoads.get(configPath);
991
- if (existingLoad !== void 0) return existingLoad;
992
- return queueProjectLoad(configPath);
1019
+ const entry = managedProjects.get(configPath);
1020
+ if (entry === void 0) return startProjectLoad(configPath);
1021
+ return entry.status === "loaded" ? entry.project : entry.load;
993
1022
  }
994
1023
  function refreshProject(configPath) {
995
- return queueProjectLoad(configPath);
1024
+ return startProjectLoad(configPath);
996
1025
  }
997
- function queueProjectLoad(configPath) {
998
- const load = (projectLoads.get(configPath) ?? Promise.resolve()).catch(() => void 0).then(() => loadProject(configPath)).finally(() => {
999
- if (projectLoads.get(configPath) === load) projectLoads.delete(configPath);
1026
+ function startProjectLoad(configPath) {
1027
+ const existing = managedProjects.get(configPath);
1028
+ const previousLoad = existing?.status === "loading" ? existing.load : void 0;
1029
+ const hadLoadedProject = existing?.status === "loaded" || existing?.status === "loading" && existing.hadLoadedProject;
1030
+ const load = (previousLoad ?? Promise.resolve(void 0)).catch(() => void 0).then(() => loadProject(configPath)).then((project) => {
1031
+ if (isCurrentLoad(configPath, load)) if (hasManagedDocuments(configPath)) managedProjects.set(configPath, {
1032
+ status: "loaded",
1033
+ project
1034
+ });
1035
+ else managedProjects.delete(configPath);
1036
+ return project;
1037
+ });
1038
+ managedProjects.set(configPath, {
1039
+ status: "loading",
1040
+ load,
1041
+ hadLoadedProject
1000
1042
  });
1001
- projectLoads.set(configPath, load);
1002
1043
  return load;
1003
1044
  }
1045
+ function isCurrentLoad(configPath, load) {
1046
+ const entry = managedProjects.get(configPath);
1047
+ return entry?.status === "loading" && entry.load === load;
1048
+ }
1004
1049
  async function loadProject(configPath) {
1005
1050
  const resolution = await resolveConfigInputs(configPath);
1006
- const artifacts = projects.get(configPath)?.artifacts ?? createProjectArtifacts();
1007
- const project = resolution.formatter === void 0 ? {
1051
+ const artifacts = createProjectArtifacts({
1052
+ inputs: resolution.inputs,
1053
+ controlStack: resolution.controlStack,
1054
+ getText: (uri) => documents.get(uri)?.getText()
1055
+ });
1056
+ return resolution.formatter === void 0 ? {
1008
1057
  configPath,
1009
1058
  inputs: resolution.inputs,
1010
1059
  controlStack: resolution.controlStack,
@@ -1016,14 +1065,14 @@ function createServer(connection) {
1016
1065
  controlStack: resolution.controlStack,
1017
1066
  artifacts
1018
1067
  };
1019
- projects.set(configPath, project);
1020
- return project;
1021
1068
  }
1022
1069
  function stopManagingProject(configPath) {
1023
- const hadProject = projects.delete(configPath);
1070
+ const entry = managedProjects.get(configPath);
1071
+ const hadProject = entry?.status === "loaded" || entry?.status === "loading" && entry.hadLoadedProject;
1072
+ managedProjects.delete(configPath);
1024
1073
  for (const document of documents.all()) if (documentConfigPaths.get(document.uri) === configPath) {
1025
1074
  documentConfigPaths.delete(document.uri);
1026
- if (hadProject) sendDiagnostics({
1075
+ if (hadProject && !clientCapabilities.pullDiagnostics) sendDiagnostics({
1027
1076
  uri: document.uri,
1028
1077
  diagnostics: []
1029
1078
  });
@@ -1032,6 +1081,13 @@ function createServer(connection) {
1032
1081
  async function republishOpenDocumentsForConfig(configPath) {
1033
1082
  for (const document of documents.all()) {
1034
1083
  if (documentConfigPaths.get(document.uri) === configPath) {
1084
+ if (await resolveProjectForDocument(document.uri) === void 0) {
1085
+ sendDiagnostics({
1086
+ uri: document.uri,
1087
+ diagnostics: []
1088
+ });
1089
+ continue;
1090
+ }
1035
1091
  await publish(document.uri);
1036
1092
  continue;
1037
1093
  }
@@ -1052,13 +1108,8 @@ function createServer(connection) {
1052
1108
  async function formatDocument(uri) {
1053
1109
  const document = documents.get(uri);
1054
1110
  if (document === void 0) return [];
1055
- let project;
1056
- try {
1057
- project = await resolveProjectForDocument(uri);
1058
- } catch {
1059
- return [];
1060
- }
1061
- if (project === void 0 || !project.inputs.includes(uri)) return [];
1111
+ const project = await resolveProjectForDocument(uri);
1112
+ if (project === void 0) return [];
1062
1113
  const source = document.getText();
1063
1114
  let formatted;
1064
1115
  try {
@@ -1081,62 +1132,47 @@ function createServer(connection) {
1081
1132
  async function semanticTokensForDocument(uri, range) {
1082
1133
  const document = documents.get(uri);
1083
1134
  if (document === void 0) return emptySemanticTokens();
1084
- const text = document.getText();
1085
- if (text.length > semanticTokenSourceLimit) return emptySemanticTokens();
1135
+ if (document.getText().length > semanticTokenSourceLimit) return emptySemanticTokens();
1086
1136
  const project = await resolveProjectForDocument(uri);
1087
1137
  if (project === void 0) return emptySemanticTokens();
1088
- const cached = project.artifacts.getDocument(uri);
1089
- if (cached === void 0 || cached.text !== text) return emptySemanticTokens();
1138
+ const artifacts = project.artifacts.document(uri);
1139
+ if (artifacts === void 0) return emptySemanticTokens();
1090
1140
  return buildSemanticTokens({
1091
- document: cached.document,
1092
- sourceFile: cached.sourceFile,
1093
- symbolTable: project.artifacts.getSymbolTable(),
1141
+ document: artifacts.document,
1142
+ sourceFile: artifacts.sourceFile,
1143
+ symbolTable: project.artifacts.symbolTable(),
1094
1144
  scalarTypes: project.controlStack.scalarTypes
1095
1145
  }, range);
1096
1146
  }
1097
1147
  async function completeDocument(uri, position) {
1098
- const document = documents.get(uri);
1099
- if (document === void 0) return [];
1100
- let project;
1101
- try {
1102
- project = await resolveProjectForDocument(uri);
1103
- } catch {
1104
- return [];
1105
- }
1106
- if (project === void 0 || !project.inputs.includes(uri)) return [];
1107
- const cached = currentDocumentArtifact(project, uri, document.getText());
1108
- const symbolTable = project.artifacts.getSymbolTable();
1109
- if (cached === void 0 || symbolTable === void 0) return [];
1148
+ if (documents.get(uri) === void 0) return [];
1149
+ const project = await resolveProjectForDocument(uri);
1150
+ if (project === void 0) return [];
1151
+ const artifacts = project.artifacts.document(uri);
1152
+ if (artifacts === void 0) return [];
1110
1153
  try {
1111
1154
  return [...providePslCompletionItems({
1112
1155
  context: classifyPslCompletionContext({
1113
- document: cached.document,
1114
- sourceFile: cached.sourceFile,
1156
+ document: artifacts.document,
1157
+ sourceFile: artifacts.sourceFile,
1115
1158
  position
1116
1159
  }),
1117
- sourceFile: cached.sourceFile,
1160
+ sourceFile: artifacts.sourceFile,
1118
1161
  candidates: {
1119
1162
  scalarTypes: project.controlStack.scalarTypes,
1120
1163
  pslBlockDescriptors: project.controlStack.pslBlockDescriptors,
1121
- symbolTable
1164
+ symbolTable: project.artifacts.symbolTable()
1122
1165
  },
1123
- clientSupportsSnippets
1166
+ clientSupportsSnippets: clientCapabilities.completionSnippets
1124
1167
  })];
1125
1168
  } catch {
1126
1169
  return [];
1127
1170
  }
1128
1171
  }
1129
- function currentDocumentArtifact(project, uri, text) {
1130
- const cached = project.artifacts.getDocument(uri);
1131
- if (cached?.sourceFile.text === text) return cached;
1132
- if (project.artifacts.update(uri, text, project.inputs, project.controlStack) === null) return;
1133
- return project.artifacts.getDocument(uri);
1134
- }
1135
1172
  connection.onInitialize(async (params) => {
1136
- rootPath = resolveRootPath(params.rootUri, params.rootPath);
1173
+ rootPath = resolveRootPath(params);
1137
1174
  watchedConfigGlob = join(rootPath, "**", CONFIG_FILENAME);
1138
- supportsWatchedFilesRegistration = clientSupportsWatchedFilesRegistration(params);
1139
- clientSupportsSnippets = clientSupportsCompletionSnippets(params);
1175
+ clientCapabilities = resolveClientCapabilities(params);
1140
1176
  return { capabilities: {
1141
1177
  textDocumentSync: TextDocumentSyncKind.Incremental,
1142
1178
  documentFormattingProvider: true,
@@ -1146,11 +1182,15 @@ function createServer(connection) {
1146
1182
  full: true,
1147
1183
  range: true
1148
1184
  },
1149
- completionProvider: { triggerCharacters: ["."] }
1185
+ completionProvider: { triggerCharacters: ["."] },
1186
+ ...clientCapabilities.pullDiagnostics ? { diagnosticProvider: {
1187
+ interFileDependencies: false,
1188
+ workspaceDiagnostics: false
1189
+ } } : {}
1150
1190
  } };
1151
1191
  });
1152
1192
  connection.onInitialized(() => {
1153
- if (supportsWatchedFilesRegistration) connection.sendRequest(RegistrationRequest.type, { registrations: [{
1193
+ if (clientCapabilities.watchedFilesRegistration) connection.sendRequest(RegistrationRequest.type, { registrations: [{
1154
1194
  id: "prisma-next-config-watcher",
1155
1195
  method: DidChangeWatchedFilesNotification.type.method,
1156
1196
  registerOptions: { watchers: [{ globPattern: watchedConfigGlob }] }
@@ -1160,43 +1200,52 @@ function createServer(connection) {
1160
1200
  connection.onDidChangeWatchedFiles(async (params) => {
1161
1201
  const changedConfigPaths = configPathsFromWatchedChanges(params.changes.map((change) => filePathFromUri(change.uri)));
1162
1202
  for (const configPath of changedConfigPaths) {
1163
- try {
1203
+ if (managedProjects.has(configPath)) try {
1164
1204
  await refreshProject(configPath);
1165
1205
  } catch {
1166
1206
  stopManagingProject(configPath);
1167
1207
  continue;
1168
1208
  }
1169
- await republishOpenDocumentsForConfig(configPath);
1209
+ if (!clientCapabilities.pullDiagnostics) await republishOpenDocumentsForConfig(configPath);
1170
1210
  }
1211
+ if (clientCapabilities.pullDiagnostics && clientCapabilities.diagnosticsRefresh && changedConfigPaths.size > 0 && !disposed) connection.languages.diagnostics.refresh().catch(() => void 0);
1171
1212
  });
1172
1213
  connection.onDocumentFormatting((params) => formatDocument(params.textDocument.uri));
1173
1214
  connection.onCompletion((params) => completeDocument(params.textDocument.uri, params.position));
1174
1215
  connection.languages.semanticTokens.on((params) => semanticTokensForDocument(params.textDocument.uri));
1175
1216
  connection.languages.semanticTokens.onRange((params) => semanticTokensForDocument(params.textDocument.uri, params.range));
1217
+ connection.languages.diagnostics.on(async (params) => {
1218
+ const project = await resolveProjectForDocument(params.textDocument.uri);
1219
+ if (project === void 0) return {
1220
+ kind: DocumentDiagnosticReportKind.Full,
1221
+ items: []
1222
+ };
1223
+ return buildDocumentDiagnosticReport(project, params.textDocument.uri);
1224
+ });
1176
1225
  connection.onFoldingRanges(async (params) => {
1177
- let project;
1178
- try {
1179
- project = await resolveProjectForDocument(params.textDocument.uri);
1180
- } catch {
1181
- return [];
1182
- }
1226
+ const project = await resolveProjectForDocument(params.textDocument.uri);
1183
1227
  if (project === void 0) return [];
1184
- const cached = project.artifacts.getDocument(params.textDocument.uri);
1185
- if (cached === void 0) return [];
1186
- return computeFoldingRanges(cached.document, cached.sourceFile);
1228
+ const artifacts = project.artifacts.document(params.textDocument.uri);
1229
+ if (artifacts === void 0) return [];
1230
+ return computeFoldingRanges(artifacts.document, artifacts.sourceFile);
1187
1231
  });
1188
1232
  documents.onDidOpen((event) => {
1233
+ artifactsForDocument(event.document.uri)?.documentChanged(event.document.uri);
1234
+ if (clientCapabilities.pullDiagnostics) return;
1189
1235
  publishSafely(event.document.uri);
1190
1236
  });
1191
1237
  documents.onDidChangeContent((event) => {
1238
+ artifactsForDocument(event.document.uri)?.documentChanged(event.document.uri);
1239
+ if (clientCapabilities.pullDiagnostics) return;
1192
1240
  publishSafely(event.document.uri);
1193
1241
  });
1194
1242
  documents.onDidClose((event) => {
1195
1243
  const uri = event.document.uri;
1196
1244
  const configPath = documentConfigPaths.get(uri);
1197
- if (configPath !== void 0) projects.get(configPath)?.artifacts.remove(uri);
1245
+ artifactsForDocument(uri)?.documentClosed(uri);
1198
1246
  documentConfigPaths.delete(uri);
1199
- sendDiagnostics({
1247
+ if (configPath !== void 0) dropProjectWithoutManagedDocuments(configPath);
1248
+ if (!clientCapabilities.pullDiagnostics) sendDiagnostics({
1200
1249
  uri,
1201
1250
  diagnostics: []
1202
1251
  });
@@ -1205,20 +1254,39 @@ function createServer(connection) {
1205
1254
  connection.listen();
1206
1255
  function artifactsForDocument(uri) {
1207
1256
  const configPath = documentConfigPaths.get(uri);
1208
- return configPath === void 0 ? void 0 : projects.get(configPath)?.artifacts;
1257
+ if (configPath === void 0) return;
1258
+ const entry = managedProjects.get(configPath);
1259
+ return entry?.status === "loaded" ? entry.project.artifacts : void 0;
1260
+ }
1261
+ function hasManagedDocuments(configPath) {
1262
+ for (const managedConfigPath of documentConfigPaths.values()) if (managedConfigPath === configPath) return true;
1263
+ return false;
1264
+ }
1265
+ function dropProjectWithoutManagedDocuments(configPath) {
1266
+ if (hasManagedDocuments(configPath)) return;
1267
+ if (managedProjects.get(configPath)?.status === "loaded") managedProjects.delete(configPath);
1209
1268
  }
1210
1269
  return {
1211
1270
  dispose: () => {
1212
1271
  disposed = true;
1213
1272
  connection.dispose();
1214
1273
  },
1215
- getDocumentAst: (uri) => artifactsForDocument(uri)?.getDocument(uri),
1216
- getProjectSymbolTable: (uri) => artifactsForDocument(uri)?.getSymbolTable()
1274
+ getDocumentAst: (uri) => artifactsForDocument(uri)?.document(uri),
1275
+ getProjectSymbolTable: (uri) => artifactsForDocument(uri)?.symbolTable()
1217
1276
  };
1218
1277
  }
1219
1278
  function emptySemanticTokens() {
1220
1279
  return { data: [] };
1221
1280
  }
1281
+ function toDiagnostics(computed) {
1282
+ return computed.map((diagnostic) => ({
1283
+ range: diagnostic.range,
1284
+ message: diagnostic.message,
1285
+ code: diagnostic.code,
1286
+ severity: toLspSeverity(diagnostic.severity),
1287
+ source: "prisma-next"
1288
+ }));
1289
+ }
1222
1290
  function toLspSeverity(severity) {
1223
1291
  switch (severity) {
1224
1292
  case ParseDiagnosticSeverity.Warning: return DiagnosticSeverity.Warning;
@@ -1227,15 +1295,25 @@ function toLspSeverity(severity) {
1227
1295
  default: return DiagnosticSeverity.Error;
1228
1296
  }
1229
1297
  }
1230
- function clientSupportsWatchedFilesRegistration(params) {
1231
- return params.capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration === true;
1232
- }
1233
- function clientSupportsCompletionSnippets(params) {
1234
- return params.capabilities.textDocument?.completion?.completionItem?.snippetSupport === true;
1298
+ const noClientCapabilities = {
1299
+ watchedFilesRegistration: false,
1300
+ completionSnippets: false,
1301
+ pullDiagnostics: false,
1302
+ diagnosticsRefresh: false
1303
+ };
1304
+ function resolveClientCapabilities(params) {
1305
+ return {
1306
+ watchedFilesRegistration: params.capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration === true,
1307
+ completionSnippets: params.capabilities.textDocument?.completion?.completionItem?.snippetSupport === true,
1308
+ pullDiagnostics: params.capabilities.textDocument?.diagnostic !== void 0,
1309
+ diagnosticsRefresh: params.capabilities.workspace?.diagnostics?.refreshSupport === true
1310
+ };
1235
1311
  }
1236
- function resolveRootPath(rootUri, rootPath) {
1237
- if (rootUri) return fileURLToPath(rootUri);
1238
- if (rootPath) return rootPath;
1312
+ function resolveRootPath(params) {
1313
+ const workspaceFolder = params.workspaceFolders?.[0];
1314
+ if (workspaceFolder !== void 0) return fileURLToPath(workspaceFolder.uri);
1315
+ if (params.rootUri) return fileURLToPath(params.rootUri);
1316
+ if (params.rootPath) return params.rootPath;
1239
1317
  return process.cwd();
1240
1318
  }
1241
1319
  function filePathFromUri(uri) {