@prisma/orm-toolchain 8.0.0-rc.11-dev.7 → 8.0.0-rc.11-dev.9

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.
@@ -7,9 +7,9 @@ import { join } from "pathe";
7
7
  import { normalize as normalize$1 } from "node:path";
8
8
  import { fileURLToPath, pathToFileURL } from "node:url";
9
9
  import { format } from "@prisma/orm-framework/psl-parser/format";
10
- import { CompletionItemKind, ConnectionError, ConnectionErrors, DiagnosticSeverity, DidChangeWatchedFilesNotification, DocumentDiagnosticReportKind, FoldingRangeKind, InsertTextFormat, ProposedFeatures, RegistrationRequest, SemanticTokenModifiers, SemanticTokenTypes, TextDocumentSyncKind, TextDocuments, createConnection } from "vscode-languageserver";
10
+ import { CompletionItemKind, ConnectionError, ConnectionErrors, DiagnosticSeverity, DidChangeWatchedFilesNotification, DocumentDiagnosticReportKind, FoldingRangeKind, InsertTextFormat, LSPErrorCodes, ProposedFeatures, RegistrationRequest, ResponseError, SemanticTokenModifiers, SemanticTokenTypes, TextDocumentSyncKind, TextDocuments, createConnection } from "vscode-languageserver";
11
11
  import { TextDocument } from "vscode-languageserver-textdocument";
12
- import { ArrayLiteralAst, AttributeArgAst, AttributeArgListAst, BooleanLiteralExprAst, CompositeTypeDeclarationAst, FieldAttributeAst, FieldDeclarationAst, FunctionCallAst, GenericBlockDeclarationAst, IdentifierAst, KeyValuePairAst, ModelAttributeAst, ModelDeclarationAst, NamespaceDeclarationAst, NumberLiteralExprAst, ObjectLiteralExprAst, StringLiteralExprAst, TypesBlockAst, any, filterChildren, findChildToken, parse, skipTriviaToken } from "@prisma/orm-framework/psl-parser/syntax";
12
+ import { ArrayLiteralAst, AttributeArgListAst, BooleanLiteralExprAst, CompositeTypeDeclarationAst, FieldAttributeAst, FieldDeclarationAst, FunctionCallAst, GenericBlockDeclarationAst, IdentifierAst, KeyValuePairAst, ModelAttributeAst, ModelDeclarationAst, NamespaceDeclarationAst, NumberLiteralExprAst, ObjectLiteralExprAst, StringLiteralExprAst, TypesBlockAst, any, filterChildren, findChildToken, isTrivia, parse, skipTriviaToken } from "@prisma/orm-framework/psl-parser/syntax";
13
13
  import { isAuthoringPslBlockDescriptor } from "@prisma/orm-framework/components/authoring";
14
14
  import { assembleAttributeSpecs, buildSymbolTable, findBlockDescriptor } from "@prisma/orm-framework/psl-parser";
15
15
  import { hasPslInterpreter } from "@prisma/orm-framework/psl-parser/interpret";
@@ -68,7 +68,7 @@ function classifyPslCompletionContext(input) {
68
68
  const replacementStartOffset = edit?.offset ?? offset;
69
69
  const attributeClassifierInput = {
70
70
  offset,
71
- node: at.leftBiased()?.parent ?? at.rightBiased()?.parent,
71
+ node: attributeAnchor(at),
72
72
  replacementStartOffset
73
73
  };
74
74
  const attributeContext = classifyFieldAttribute(attributeClassifierInput) ?? classifyGenericBlockAttribute(attributeClassifierInput) ?? classifyModelAttribute(attributeClassifierInput);
@@ -205,29 +205,36 @@ function blockBodyContainsOffset(block, offset) {
205
205
  }
206
206
  function classifyFieldAttribute(input) {
207
207
  const attribute = input.node?.findAncestor(FieldAttributeAst.cast);
208
- if (attribute === void 0 || attribute.syntax.isOutside(input.offset)) return;
208
+ if (attribute === void 0 || !attributeContainsOffset(attribute, input.offset)) return;
209
209
  const field = attribute.syntax.findAncestor(FieldDeclarationAst.cast);
210
210
  const model = attribute.syntax.findAncestor(ModelDeclarationAst.cast);
211
211
  if (field === void 0 || model === void 0) return UNSUPPORTED;
212
- if (isAttributeNamePosition(attribute, input.offset)) return {
213
- kind: "fieldAttributeName",
214
- offset: input.offset,
215
- replacementStartOffset: input.replacementStartOffset,
216
- attribute,
217
- field,
218
- model
219
- };
220
- const attributeName = attributeNamedKeyName(attribute, input.offset);
221
- if (attributeName === void 0) return UNSUPPORTED;
222
- return {
223
- kind: "fieldAttributeNamedKey",
224
- offset: input.offset,
225
- replacementStartOffset: input.replacementStartOffset,
226
- attributeName,
227
- attribute,
228
- field,
229
- model
230
- };
212
+ return classifyAttributePosition(attribute, input, {
213
+ name: (position) => ({
214
+ kind: "fieldAttributeName",
215
+ ...position,
216
+ field,
217
+ model
218
+ }),
219
+ namedKey: (position) => ({
220
+ kind: "fieldAttributeNamedKey",
221
+ ...position,
222
+ field,
223
+ model
224
+ }),
225
+ argumentSlot: (position) => ({
226
+ kind: "fieldAttributeArgumentSlot",
227
+ ...position,
228
+ field,
229
+ model
230
+ }),
231
+ value: (position) => ({
232
+ kind: "fieldAttributeValue",
233
+ ...position,
234
+ field,
235
+ model
236
+ })
237
+ });
231
238
  }
232
239
  function classifyGenericBlockAttribute(input) {
233
240
  const attribute = activeModelAttribute(input);
@@ -235,80 +242,226 @@ function classifyGenericBlockAttribute(input) {
235
242
  if (attribute === void 0 || block === void 0) return;
236
243
  const blockKeyword = block.keyword()?.text;
237
244
  if (blockKeyword === void 0 || blockKeyword.length === 0) return UNSUPPORTED;
238
- if (isAttributeNamePosition(attribute, input.offset)) return {
239
- kind: "blockAttributeName",
240
- offset: input.offset,
241
- replacementStartOffset: input.replacementStartOffset,
242
- attribute,
243
- block,
244
- blockKeyword
245
- };
246
- const attributeName = attributeNamedKeyName(attribute, input.offset);
247
- if (attributeName === void 0) return UNSUPPORTED;
248
- return {
249
- kind: "blockAttributeNamedKey",
250
- offset: input.offset,
251
- replacementStartOffset: input.replacementStartOffset,
252
- attributeName,
253
- attribute,
254
- block,
255
- blockKeyword
256
- };
245
+ return classifyAttributePosition(attribute, input, {
246
+ name: (position) => ({
247
+ kind: "blockAttributeName",
248
+ ...position,
249
+ block,
250
+ blockKeyword
251
+ }),
252
+ namedKey: (position) => ({
253
+ kind: "blockAttributeNamedKey",
254
+ ...position,
255
+ block,
256
+ blockKeyword
257
+ }),
258
+ argumentSlot: (position) => ({
259
+ kind: "blockAttributeArgumentSlot",
260
+ ...position,
261
+ block,
262
+ blockKeyword
263
+ }),
264
+ value: (position) => ({
265
+ kind: "blockAttributeValue",
266
+ ...position,
267
+ block,
268
+ blockKeyword
269
+ })
270
+ });
257
271
  }
258
272
  function classifyModelAttribute(input) {
259
273
  const attribute = activeModelAttribute(input);
260
274
  if (attribute === void 0) return;
261
275
  const model = attribute.syntax.findAncestor(ModelDeclarationAst.cast);
262
276
  if (model === void 0) return;
263
- if (isAttributeNamePosition(attribute, input.offset)) return {
264
- kind: "modelAttributeName",
277
+ return classifyAttributePosition(attribute, input, {
278
+ name: (position) => ({
279
+ kind: "modelAttributeName",
280
+ ...position,
281
+ model
282
+ }),
283
+ namedKey: (position) => ({
284
+ kind: "modelAttributeNamedKey",
285
+ ...position,
286
+ model
287
+ }),
288
+ argumentSlot: (position) => ({
289
+ kind: "modelAttributeArgumentSlot",
290
+ ...position,
291
+ model
292
+ }),
293
+ value: (position) => ({
294
+ kind: "modelAttributeValue",
295
+ ...position,
296
+ model
297
+ })
298
+ });
299
+ }
300
+ function activeModelAttribute(input) {
301
+ const attribute = input.node?.findAncestor(ModelAttributeAst.cast);
302
+ if (attribute === void 0 || !attributeContainsOffset(attribute, input.offset)) return;
303
+ return attribute;
304
+ }
305
+ function attributeAnchor(at) {
306
+ const token = at.leftBiased() ?? at.rightBiased();
307
+ return token === void 0 ? void 0 : skipTriviaToken(token, "prev")?.parent;
308
+ }
309
+ function attributeContainsOffset(attribute, offset) {
310
+ if (attribute.syntax.isInside(offset)) return true;
311
+ const args = attribute.argList();
312
+ return args !== void 0 && args.rparen() === void 0 && offset >= args.syntax.endOffset;
313
+ }
314
+ function isAttributeNamePosition(attribute, offset) {
315
+ const argList = attribute.argList();
316
+ return argList === void 0 || offset < argList.syntax.offset;
317
+ }
318
+ function attributeArgumentName(attribute, offset) {
319
+ const args = attribute.argList();
320
+ if (args === void 0 || offset < args.syntax.offset) return void 0;
321
+ const closing = args.rparen();
322
+ if (closing !== void 0 && offset >= closing.endOffset) return void 0;
323
+ return attribute.name()?.identifier()?.name();
324
+ }
325
+ function classifyAttributePosition(attribute, input, factory) {
326
+ const args = attribute.argList();
327
+ if (isAttributeNamePosition(attribute, input.offset)) return factory.name({
265
328
  offset: input.offset,
266
329
  replacementStartOffset: input.replacementStartOffset,
267
- attribute,
268
- model
269
- };
270
- const attributeName = attributeNamedKeyName(attribute, input.offset);
271
- if (attributeName === void 0) return UNSUPPORTED;
272
- return {
273
- kind: "modelAttributeNamedKey",
330
+ replacementEndOffset: attribute.name()?.syntax.endOffset ?? input.offset,
331
+ hasArgumentList: args !== void 0
332
+ });
333
+ const attributeName = attributeArgumentName(attribute, input.offset);
334
+ if (attributeName === void 0 || args === void 0) return UNSUPPORTED;
335
+ const at = attribute.syntax.tokenAtOffset(input.offset);
336
+ const right = at.rightBiased();
337
+ const token = isValueToken(right) ? right : at.leftBiased();
338
+ const replaceToken = isValueToken(token);
339
+ const anchor = at.leftBiased() ?? attribute.syntax.lastToken;
340
+ return classifyArguments({
274
341
  offset: input.offset,
275
- replacementStartOffset: input.replacementStartOffset,
342
+ replacementStartOffset: replaceToken ? token.offset : input.offset,
343
+ replacementEndOffset: replaceToken ? token.endOffset : input.offset,
276
344
  attributeName,
277
- attribute,
278
- model
345
+ preceding: anchor === void 0 ? void 0 : skipTriviaToken(anchor, "prev"),
346
+ factory
347
+ }, args, []);
348
+ }
349
+ function argumentPosition(cursor, path) {
350
+ return {
351
+ offset: cursor.offset,
352
+ replacementStartOffset: cursor.replacementStartOffset,
353
+ replacementEndOffset: cursor.replacementEndOffset,
354
+ attributeName: cursor.attributeName,
355
+ path
279
356
  };
280
357
  }
281
- function activeModelAttribute(input) {
282
- const attribute = input.node?.findAncestor(ModelAttributeAst.cast);
283
- if (attribute === void 0 || attribute.syntax.isOutside(input.offset)) return;
284
- return attribute;
358
+ function classifyArguments(cursor, container, path) {
359
+ const opening = container.lparen();
360
+ const closing = container.rparen();
361
+ if (opening === void 0 || cursor.offset <= opening.offset || closing !== void 0 && cursor.offset >= closing.endOffset) return UNSUPPORTED;
362
+ let positionalIndex = 0;
363
+ let active;
364
+ const existingNamedKeys = [];
365
+ for (const arg of container.args()) {
366
+ const name = arg.name()?.name();
367
+ const selected = arg.syntax.offset <= cursor.offset && (containsCursor(arg.syntax, cursor) || recoveredContainerContainsCursor(arg.value(), cursor.offset));
368
+ if (active === void 0 && selected) active = arg;
369
+ else {
370
+ if (name !== void 0) existingNamedKeys.push(name);
371
+ if (active === void 0 && arg.syntax.offset <= cursor.offset && name === void 0) positionalIndex += 1;
372
+ }
373
+ }
374
+ const position = argumentPosition(cursor, path);
375
+ if (active === void 0) return followsSeparator(cursor, ["LParen", "Comma"]) ? cursor.factory.argumentSlot({
376
+ ...position,
377
+ positionalIndex,
378
+ existingNamedKeys,
379
+ hasColon: false
380
+ }) : UNSUPPORTED;
381
+ const colon = active.colon();
382
+ if (colon !== void 0) {
383
+ if (cursor.offset <= colon.offset) return cursor.factory.namedKey({
384
+ ...position,
385
+ existingNamedKeys,
386
+ hasColon: true
387
+ });
388
+ const name = active.name()?.name();
389
+ return name === void 0 ? UNSUPPORTED : classifyExpression(cursor, active.value(), [...path, {
390
+ kind: "namedArgument",
391
+ name
392
+ }]);
393
+ }
394
+ const value = active.value();
395
+ if (value === void 0 || value instanceof IdentifierAst && value.syntax.isInside(cursor.offset)) return cursor.factory.argumentSlot({
396
+ ...position,
397
+ positionalIndex,
398
+ existingNamedKeys,
399
+ hasColon: false
400
+ });
401
+ return classifyExpression(cursor, value, [...path, {
402
+ kind: "positionalArgument",
403
+ index: positionalIndex
404
+ }]);
405
+ }
406
+ function classifyExpression(cursor, expression, path) {
407
+ if (expression instanceof ArrayLiteralAst) return classifyList(cursor, expression, path);
408
+ if (expression instanceof ObjectLiteralExprAst) return classifyRecord(cursor, expression, path);
409
+ if (expression instanceof FunctionCallAst) {
410
+ const opening = expression.lparen();
411
+ if (opening !== void 0 && cursor.offset > opening.offset) {
412
+ const name = expression.name();
413
+ const identifier = name?.identifier()?.name();
414
+ return identifier !== void 0 && name?.isSimpleName(identifier) === true ? classifyArguments(cursor, expression, [...path, {
415
+ kind: "functionCall",
416
+ name: identifier
417
+ }]) : UNSUPPORTED;
418
+ }
419
+ return expression.name()?.syntax.isInside(cursor.offset) === true ? cursor.factory.value({
420
+ ...argumentPosition(cursor, path),
421
+ syntax: "functionName"
422
+ }) : UNSUPPORTED;
423
+ }
424
+ return expression?.syntax.isOutside(cursor.offset) === true ? UNSUPPORTED : cursor.factory.value({
425
+ ...argumentPosition(cursor, path),
426
+ syntax: "scalar"
427
+ });
285
428
  }
286
- function isAttributeNamePosition(attribute, offset) {
287
- return !attribute.argList()?.syntax.isInside(offset);
429
+ function classifyList(cursor, expression, path) {
430
+ const opening = expression.lbracket();
431
+ const closing = expression.rbracket();
432
+ if (opening === void 0 || cursor.offset <= opening.offset || closing !== void 0 && cursor.offset >= closing.endOffset) return UNSUPPORTED;
433
+ const elementPath = [...path, { kind: "listElement" }];
434
+ for (const element of expression.elements()) if (containsCursor(element.syntax, cursor) || recoveredContainerContainsCursor(element, cursor.offset)) return classifyExpression(cursor, element, elementPath);
435
+ return followsSeparator(cursor, ["LBracket", "Comma"]) ? classifyExpression(cursor, void 0, elementPath) : UNSUPPORTED;
288
436
  }
289
- function attributeNamedKeyName(attribute, offset) {
290
- const argList = attribute.argList();
291
- if (argList === void 0 || argList.syntax.isOutside(offset)) return;
292
- const rparen = argList.rparen();
293
- if (rparen !== void 0 && offset >= rparen.endOffset) return;
294
- for (const arg of argList.args()) {
295
- const colon = arg.colon();
296
- if (colon !== void 0 && offset > colon.offset && offset >= arg.syntax.offset && offset <= arg.syntax.endOffset) return;
297
- }
298
- const token = attribute.syntax.tokenAtOffset(offset).leftBiased();
299
- if (token?.kind === "Colon") return;
300
- const node = token?.parent;
301
- const activeArg = node?.findAncestor(AttributeArgAst.cast);
302
- if (activeArg === void 0 && node?.kind !== "AttributeArgList") return;
303
- if (activeArg !== void 0) {
304
- const colon = activeArg.colon();
305
- if (colon !== void 0 && offset > colon.offset) return;
306
- const value = activeArg.value();
307
- if (colon === void 0 && value !== void 0 && value.syntax.kind !== "Identifier" && value.syntax.isInside(offset)) return;
308
- }
309
- const attributeName = attribute.name()?.identifier()?.name();
310
- if (attributeName === void 0) return;
311
- return attributeName;
437
+ function classifyRecord(cursor, expression, path) {
438
+ const closing = expression.rbrace();
439
+ if (closing !== void 0 && cursor.offset >= closing.endOffset) return UNSUPPORTED;
440
+ for (const field of expression.fields()) {
441
+ const colon = field.colon();
442
+ if (colon !== void 0 && cursor.offset > colon.offset && (containsCursor(field.syntax, cursor) || recoveredContainerContainsCursor(field.value(), cursor.offset))) return classifyExpression(cursor, field.value(), [...path, { kind: "recordValue" }]);
443
+ }
444
+ return UNSUPPORTED;
445
+ }
446
+ function isValueToken(token) {
447
+ return token !== void 0 && (token.kind === "Ident" || token.kind === "StringLiteral" || token.kind === "NumberLiteral");
448
+ }
449
+ function recoveredContainerContainsCursor(expression, offset) {
450
+ if (expression === void 0 || expression.syntax.endOffset > offset) return false;
451
+ if (!(expression instanceof ArrayLiteralAst ? expression.rbracket() === void 0 : expression instanceof ObjectLiteralExprAst ? expression.rbrace() === void 0 : expression instanceof FunctionCallAst && expression.rparen() === void 0)) return false;
452
+ for (let token = expression.syntax.lastToken?.nextToken; token !== void 0; token = token.nextToken) {
453
+ if (token.offset >= offset) return true;
454
+ if (!isTrivia(token) && token.kind !== "Comma") return false;
455
+ }
456
+ return true;
457
+ }
458
+ function containsCursor(node, cursor) {
459
+ if (node.isInside(cursor.offset)) return true;
460
+ const preceding = cursor.preceding;
461
+ return preceding !== void 0 && preceding.endOffset <= cursor.offset && preceding.offset >= node.offset && preceding.offset < node.endOffset;
462
+ }
463
+ function followsSeparator(cursor, kinds) {
464
+ return kinds.includes(cursor.preceding?.kind ?? "");
312
465
  }
313
466
  function classifyGenericBlockParameter(input) {
314
467
  const node = input.at.leftBiased()?.parent;
@@ -360,6 +513,200 @@ function cursorIdentifier(at, offset) {
360
513
  const left = at.leftBiased();
361
514
  if (left?.kind === "Ident" && left.endOffset === offset) return left;
362
515
  }
516
+ function requiredArgumentsSnippet(signature) {
517
+ return requiredArguments(signature).map((argument, index) => requiredArgumentSnippet(argument, index + 1)).join(", ");
518
+ }
519
+ function requiredArguments(signature) {
520
+ const positional = signature.positional ?? [];
521
+ const positionalKeys = new Set(positional.map((argument) => argument.key));
522
+ return [...positional.flatMap((argument) => isOptionalParam(argument.type) ? [] : [{
523
+ kind: "positional",
524
+ argument
525
+ }]), ...Object.entries(signature.named ?? {}).flatMap(([key, type]) => positionalKeys.has(key) || isOptionalParam(type) ? [] : [{
526
+ kind: "named",
527
+ key,
528
+ type
529
+ }])];
530
+ }
531
+ function requiredArgumentSnippet(argument, tabStop) {
532
+ if (argument.kind === "positional") return argSnippetPlaceholder(argument.argument.type, tabStop);
533
+ return `${argument.key}: ${argSnippetPlaceholder(argument.type, tabStop)}`;
534
+ }
535
+ function argSnippetPlaceholder(param, tabStop) {
536
+ const placeholder = `\${${tabStop.toString()}:}`;
537
+ if (param.kind === "str") return `"${placeholder}"`;
538
+ if (param.kind === "list") return `[${placeholder}]`;
539
+ if (param.kind === "record") return `{ ${placeholder} }`;
540
+ return placeholder;
541
+ }
542
+ function isOptionalParam(param) {
543
+ return "optional" in param && param.optional === true;
544
+ }
545
+ function modelSymbolForNode(symbolTable, node) {
546
+ const topLevelMatch = Object.values(symbolTable.topLevel.models).find((model) => sameSyntax(model.node.syntax, node.syntax));
547
+ if (topLevelMatch !== void 0) return topLevelMatch;
548
+ for (const namespace of Object.values(symbolTable.topLevel.namespaces)) {
549
+ const namespaceMatch = Object.values(namespace.models).find((model) => sameSyntax(model.node.syntax, node.syntax));
550
+ if (namespaceMatch !== void 0) return namespaceMatch;
551
+ }
552
+ }
553
+ function fieldSymbolForNode(model, node) {
554
+ return Object.values(model.fields).find((field) => sameSyntax(field.node.syntax, node.syntax));
555
+ }
556
+ function localFieldNames(context, symbols) {
557
+ switch (context.kind) {
558
+ case "blockAttributeNamedKey":
559
+ case "blockAttributeArgumentSlot":
560
+ case "blockAttributeValue": return [];
561
+ case "fieldAttributeNamedKey":
562
+ case "fieldAttributeArgumentSlot":
563
+ case "fieldAttributeValue":
564
+ case "modelAttributeNamedKey":
565
+ case "modelAttributeArgumentSlot":
566
+ case "modelAttributeValue": return Object.keys(modelSymbolForNode(symbols, context.model)?.fields ?? {});
567
+ }
568
+ }
569
+ function referencedFieldNames(context, symbols) {
570
+ switch (context.kind) {
571
+ case "blockAttributeNamedKey":
572
+ case "blockAttributeArgumentSlot":
573
+ case "blockAttributeValue":
574
+ case "modelAttributeNamedKey":
575
+ case "modelAttributeArgumentSlot":
576
+ case "modelAttributeValue": return [];
577
+ case "fieldAttributeNamedKey":
578
+ case "fieldAttributeArgumentSlot":
579
+ case "fieldAttributeValue": {
580
+ const model = modelSymbolForNode(symbols, context.model);
581
+ if (model === void 0) return [];
582
+ const field = fieldSymbolForNode(model, context.field);
583
+ if (field === void 0) return [];
584
+ return Object.keys(referencedModel(symbols, model, field)?.fields ?? {});
585
+ }
586
+ }
587
+ }
588
+ function referencedModel(symbols, model, field) {
589
+ if (field.malformedType === true || field.typeContractSpaceId !== void 0) return void 0;
590
+ if (field.typeNamespaceId !== void 0) return symbols.topLevel.namespaces[field.typeNamespaceId]?.models[field.typeName];
591
+ const namespace = model.node.syntax.findAncestor(NamespaceDeclarationAst.cast)?.name()?.name();
592
+ return (namespace === void 0 ? void 0 : symbols.topLevel.namespaces[namespace]?.models[field.typeName]) ?? symbols.topLevel.models[field.typeName];
593
+ }
594
+ function sameSyntax(left, right) {
595
+ return left.offset === right.offset && left.endOffset === right.endOffset;
596
+ }
597
+ function provideAttributeNamedKeyCompletionItems(input, spec) {
598
+ return orderedItems(resolveGrammar(spec, input.context.path).flatMap((grammar) => "kind" in grammar ? [] : namedKeyItems(input, grammar)));
599
+ }
600
+ function provideAttributeArgumentSlotCompletionItems(input, spec) {
601
+ return orderedItems(resolveGrammar(spec, input.context.path).flatMap((grammar) => {
602
+ if ("kind" in grammar) return [];
603
+ const param = grammar.positional?.[input.context.positionalIndex]?.type;
604
+ return [...valueItems(input, param, "scalar"), ...namedKeyItems(input, grammar)];
605
+ }));
606
+ }
607
+ function provideAttributeValueCompletionItems(input, spec) {
608
+ return orderedItems(resolveGrammar(spec, input.context.path).flatMap((grammar) => "kind" in grammar ? valueItems(input, grammar, input.context.syntax) : []));
609
+ }
610
+ function directArgType(param) {
611
+ return blindCast(param);
612
+ }
613
+ function resolveGrammar(signature, path) {
614
+ let grammars = [signature];
615
+ for (const step of path) grammars = grammars.flatMap((grammar) => advanceGrammar(grammar, step));
616
+ return grammars;
617
+ }
618
+ function advanceGrammar(grammar, step) {
619
+ const type = "kind" in grammar ? directArgType(grammar) : void 0;
620
+ if (type?.kind === "oneOf") return type.alternatives.flatMap((alternative) => advanceGrammar(alternative, step));
621
+ switch (step.kind) {
622
+ case "positionalArgument": {
623
+ if ("kind" in grammar) return [];
624
+ const param = grammar.positional?.[step.index]?.type;
625
+ return param === void 0 ? [] : [param];
626
+ }
627
+ case "namedArgument": {
628
+ if ("kind" in grammar) return [];
629
+ const param = grammar.named?.[step.name];
630
+ return param === void 0 ? [] : [param];
631
+ }
632
+ case "listElement": return type?.kind === "list" ? [type.of] : [];
633
+ case "recordValue": return type?.kind === "record" ? [type.of] : [];
634
+ case "functionCall": return type?.kind === "funcCall" && type.name === step.name ? [type.signature] : [];
635
+ }
636
+ }
637
+ function namedKeyItems(input, signature) {
638
+ return Object.keys(signature.named ?? {}).filter((name) => !input.context.existingNamedKeys.includes(name)).map((name) => {
639
+ const snippet = input.clientSupportsSnippets && !input.context.hasColon;
640
+ const value = snippet ? "${1:}" : "";
641
+ return {
642
+ ...completionItem(input, name, input.context.hasColon ? name : `${name}: ${value}`, CompletionItemKind.Property, snippet),
643
+ ...!input.context.hasColon && input.clientSupportsTriggerSuggestCommand === true ? { command: {
644
+ title: "Suggest argument values",
645
+ command: "editor.action.triggerSuggest"
646
+ } } : {}
647
+ };
648
+ });
649
+ }
650
+ function valueItems(input, param, syntax) {
651
+ if (param === void 0) return [];
652
+ const type = directArgType(param);
653
+ if (type.kind === "oneOf") return type.alternatives.flatMap((alternative) => valueItems(input, alternative, syntax));
654
+ if (type.kind === "funcCall") {
655
+ const snippet = input.clientSupportsSnippets && syntax !== "functionName";
656
+ const text = snippet ? `${type.name}(${requiredArgumentsSnippet(type.signature)})` : type.name;
657
+ return [completionItem(input, type.name, text, CompletionItemKind.Function, snippet)];
658
+ }
659
+ if (syntax === "functionName") return [];
660
+ switch (type.kind) {
661
+ case "identifier": return scalarItems(input, [type.name]);
662
+ case "str": return scalarItems(input, type.value === void 0 ? [] : [JSON.stringify(type.value)]);
663
+ case "num": return scalarItems(input, type.value === void 0 ? [] : [String(type.value)]);
664
+ case "bool": return scalarItems(input, ["true", "false"]);
665
+ case "fieldRef":
666
+ case "referencedFieldRef": return scalarItems(input, input.fieldNames(type.kind));
667
+ case "list":
668
+ case "record":
669
+ case "entityRef":
670
+ case "int":
671
+ case "json":
672
+ case "rejecting": return [];
673
+ }
674
+ }
675
+ function scalarItems(input, labels) {
676
+ return labels.map((label) => completionItem(input, label, label, CompletionItemKind.Value));
677
+ }
678
+ function completionItem(input, label, newText, kind, snippet = false) {
679
+ return {
680
+ label,
681
+ kind,
682
+ detail: kind === CompletionItemKind.Property ? "Attribute argument" : "PSL argument value",
683
+ filterText: label,
684
+ textEdit: {
685
+ range: {
686
+ start: input.sourceFile.positionAt(input.context.replacementStartOffset),
687
+ end: input.sourceFile.positionAt(input.context.replacementEndOffset)
688
+ },
689
+ newText
690
+ },
691
+ ...snippet ? { insertTextFormat: InsertTextFormat.Snippet } : {}
692
+ };
693
+ }
694
+ function orderedItems(items) {
695
+ const seen = /* @__PURE__ */ new Set();
696
+ return items.filter((item) => {
697
+ const key = JSON.stringify([
698
+ item.label,
699
+ item.textEdit,
700
+ item.insertTextFormat
701
+ ]);
702
+ if (seen.has(key)) return false;
703
+ seen.add(key);
704
+ return true;
705
+ }).map((item, index) => ({
706
+ ...item,
707
+ sortText: index.toString().padStart(4, "0")
708
+ }));
709
+ }
363
710
  /**
364
711
  * Presentation-level classification of a `types {}` binding: a
365
712
  * non-constructor binding whose base is a configured scalar type refines that
@@ -403,7 +750,38 @@ function providePslCompletionItems(input) {
403
750
  case "blockAttributeName": return provideAttributeNameCompletionItems(context, input.sourceFile, input.candidates, input.clientSupportsSnippets);
404
751
  case "fieldAttributeNamedKey":
405
752
  case "modelAttributeNamedKey":
406
- case "blockAttributeNamedKey": return provideAttributeNamedKeyCompletionItems(context, input.sourceFile, input.candidates);
753
+ case "blockAttributeNamedKey": {
754
+ const spec = attributeSpecResolver(context, input.candidates)(context.attributeName);
755
+ return spec === void 0 ? [] : provideAttributeNamedKeyCompletionItems({
756
+ context,
757
+ sourceFile: input.sourceFile,
758
+ clientSupportsSnippets: input.clientSupportsSnippets,
759
+ clientSupportsTriggerSuggestCommand: input.clientSupportsTriggerSuggestCommand === true
760
+ }, spec);
761
+ }
762
+ case "fieldAttributeArgumentSlot":
763
+ case "modelAttributeArgumentSlot":
764
+ case "blockAttributeArgumentSlot": {
765
+ const spec = attributeSpecResolver(context, input.candidates)(context.attributeName);
766
+ return spec === void 0 ? [] : provideAttributeArgumentSlotCompletionItems({
767
+ context,
768
+ sourceFile: input.sourceFile,
769
+ clientSupportsSnippets: input.clientSupportsSnippets,
770
+ clientSupportsTriggerSuggestCommand: input.clientSupportsTriggerSuggestCommand === true,
771
+ fieldNames: (kind) => kind === "fieldRef" ? localFieldNames(context, input.candidates.symbolTable) : referencedFieldNames(context, input.candidates.symbolTable)
772
+ }, spec);
773
+ }
774
+ case "fieldAttributeValue":
775
+ case "modelAttributeValue":
776
+ case "blockAttributeValue": {
777
+ const spec = attributeSpecResolver(context, input.candidates)(context.attributeName);
778
+ return spec === void 0 ? [] : provideAttributeValueCompletionItems({
779
+ context,
780
+ sourceFile: input.sourceFile,
781
+ clientSupportsSnippets: input.clientSupportsSnippets,
782
+ fieldNames: (kind) => kind === "fieldRef" ? localFieldNames(context, input.candidates.symbolTable) : referencedFieldNames(context, input.candidates.symbolTable)
783
+ }, spec);
784
+ }
407
785
  case "declarationKeyword": return provideDeclarationKeywordCompletionItems(context, input.sourceFile, input.candidates, input.clientSupportsSnippets);
408
786
  case "genericBlockKey": return provideGenericBlockKeyCompletionItems(context, input.sourceFile, input.candidates);
409
787
  case "modelType": return provideModelTypeCompletionItems(context, input.sourceFile, input.candidates);
@@ -412,17 +790,16 @@ function providePslCompletionItems(input) {
412
790
  }
413
791
  function provideAttributeNameCompletionItems(context, sourceFile, source, clientSupportsSnippets) {
414
792
  const names = attributeNames(context, source);
415
- const replacementEndOffset = attributeNameReplacementEndOffset(context);
416
793
  const replacementRange = {
417
794
  start: sourceFile.positionAt(context.replacementStartOffset),
418
- end: sourceFile.positionAt(replacementEndOffset)
795
+ end: sourceFile.positionAt(context.replacementEndOffset)
419
796
  };
420
797
  const resolveSpec = attributeSpecResolver(context, source);
421
798
  return names.map((name) => {
422
799
  const newText = attributeNameEditText({
423
800
  name,
424
801
  spec: resolveSpec(name),
425
- attribute: context.attribute,
802
+ hasArgumentList: context.hasArgumentList,
426
803
  clientSupportsSnippets
427
804
  });
428
805
  return {
@@ -439,26 +816,6 @@ function provideAttributeNameCompletionItems(context, sourceFile, source, client
439
816
  };
440
817
  });
441
818
  }
442
- function provideAttributeNamedKeyCompletionItems(context, sourceFile, source) {
443
- const spec = attributeSpec(context, source);
444
- if (spec === void 0) return [];
445
- const existing = existingAttributeNamedKeys(context.attribute, context.offset);
446
- const replacementRange = {
447
- start: sourceFile.positionAt(context.replacementStartOffset),
448
- end: sourceFile.positionAt(namedKeyReplacementEndOffset(context))
449
- };
450
- return Object.keys(spec.named).filter((name) => !existing.has(name)).map((name) => ({
451
- label: name,
452
- kind: CompletionItemKind.Property,
453
- detail: "Attribute argument",
454
- sortText: name,
455
- filterText: name,
456
- textEdit: {
457
- range: replacementRange,
458
- newText: name
459
- }
460
- }));
461
- }
462
819
  function attributeNames(context, source) {
463
820
  switch (context.kind) {
464
821
  case "blockAttributeName": {
@@ -474,54 +831,16 @@ function attributeNames(context, source) {
474
831
  }
475
832
  }
476
833
  function attributeNameEditText(input) {
477
- if (!input.clientSupportsSnippets || input.spec === void 0 || input.attribute.argList() !== void 0) return input.name;
478
- const required = requiredAttributeArguments(input.spec);
479
- if (required.length === 0) return input.name;
480
- return `${input.name}(${required.map((argument, index) => requiredAttributeArgumentSnippet(argument, index + 1)).join(", ")})`;
481
- }
482
- function requiredAttributeArguments(spec) {
483
- const positionalKeys = new Set(spec.positional.map((argument) => argument.key));
484
- return [...spec.positional.flatMap((argument) => isOptionalParam(argument.type) ? [] : [{
485
- kind: "positional",
486
- argument
487
- }]), ...Object.entries(spec.named).flatMap(([key, type]) => positionalKeys.has(key) || isOptionalParam(type) ? [] : [{
488
- kind: "named",
489
- key,
490
- type
491
- }])];
492
- }
493
- function requiredAttributeArgumentSnippet(argument, tabStop) {
494
- if (argument.kind === "positional") return argSnippetPlaceholder(argument.argument.type, tabStop);
495
- return `${argument.key}: ${argSnippetPlaceholder(argument.type, tabStop)}`;
496
- }
497
- function argSnippetPlaceholder(param, tabStop) {
498
- const placeholder = `\${${tabStop.toString()}:}`;
499
- if (param.kind === "str") return `"${placeholder}"`;
500
- if (param.kind === "list") return `[${placeholder}]`;
501
- if (param.kind === "record") return `{ ${placeholder} }`;
502
- return placeholder;
503
- }
504
- function isOptionalParam(param) {
505
- return "optional" in param && param.optional === true;
506
- }
507
- function attributeNameReplacementEndOffset(context) {
508
- return context.attribute.name()?.syntax.endOffset ?? context.offset;
509
- }
510
- function namedKeyReplacementEndOffset(context) {
511
- const token = context.attribute.syntax.tokenAtOffset(context.offset).rightBiased();
512
- if (token?.kind === "Ident" && token.offset <= context.offset) return token.endOffset;
513
- return context.offset;
514
- }
515
- function attributeSpec(context, source) {
516
- return attributeSpecForName(context, source, context.attributeName);
517
- }
518
- function attributeSpecForName(context, source, name) {
519
- return attributeSpecResolver(context, source)(name);
834
+ if (!input.clientSupportsSnippets || input.spec === void 0 || input.hasArgumentList) return input.name;
835
+ const required = requiredArgumentsSnippet(input.spec);
836
+ return required.length === 0 ? input.name : `${input.name}(${required})`;
520
837
  }
521
838
  function attributeSpecResolver(context, source) {
522
839
  switch (context.kind) {
523
840
  case "blockAttributeName":
524
- case "blockAttributeNamedKey": {
841
+ case "blockAttributeNamedKey":
842
+ case "blockAttributeArgumentSlot":
843
+ case "blockAttributeValue": {
525
844
  const descriptor = findBlockDescriptor(source.pslBlockDescriptors, context.blockKeyword);
526
845
  return (name) => {
527
846
  const factory = descriptor?.attributes?.[name];
@@ -530,7 +849,9 @@ function attributeSpecResolver(context, source) {
530
849
  };
531
850
  }
532
851
  case "modelAttributeName":
533
- case "modelAttributeNamedKey": {
852
+ case "modelAttributeNamedKey":
853
+ case "modelAttributeArgumentSlot":
854
+ case "modelAttributeValue": {
534
855
  if (source.authoringContributions === void 0) return () => void 0;
535
856
  const model = modelSymbolForNode(source.symbolTable, context.model);
536
857
  if (model === void 0 || source.controlMutationDefaults === void 0) return () => void 0;
@@ -543,7 +864,9 @@ function attributeSpecResolver(context, source) {
543
864
  return (name) => specs.model[name]?.(specContext);
544
865
  }
545
866
  case "fieldAttributeName":
546
- case "fieldAttributeNamedKey": {
867
+ case "fieldAttributeNamedKey":
868
+ case "fieldAttributeArgumentSlot":
869
+ case "fieldAttributeValue": {
547
870
  if (source.authoringContributions === void 0) return () => void 0;
548
871
  const model = modelSymbolForNode(source.symbolTable, context.model);
549
872
  if (model === void 0 || source.controlMutationDefaults === void 0) return () => void 0;
@@ -562,29 +885,6 @@ function attributeSpecResolver(context, source) {
562
885
  }
563
886
  }
564
887
  }
565
- function existingAttributeNamedKeys(attribute, cursorOffset) {
566
- const names = /* @__PURE__ */ new Set();
567
- for (const arg of attribute.argList()?.args() ?? []) {
568
- if (!arg.syntax.isOutside(cursorOffset)) continue;
569
- const name = arg.name()?.name();
570
- if (name !== void 0) names.add(name);
571
- }
572
- return names;
573
- }
574
- function modelSymbolForNode(symbolTable, node) {
575
- const topLevelMatch = Object.values(symbolTable.topLevel.models).find((model) => sameSyntax(model.node.syntax, node.syntax));
576
- if (topLevelMatch !== void 0) return topLevelMatch;
577
- for (const namespace of Object.values(symbolTable.topLevel.namespaces)) {
578
- const namespaceMatch = Object.values(namespace.models).find((model) => sameSyntax(model.node.syntax, node.syntax));
579
- if (namespaceMatch !== void 0) return namespaceMatch;
580
- }
581
- }
582
- function fieldSymbolForNode(model, node) {
583
- return Object.values(model.fields).find((field) => sameSyntax(field.node.syntax, node.syntax));
584
- }
585
- function sameSyntax(left, right) {
586
- return left.offset === right.offset && left.endOffset === right.endOffset;
587
- }
588
888
  function provideDeclarationKeywordCompletionItems(context, sourceFile, source, clientSupportsSnippets) {
589
889
  const replacementRange = {
590
890
  start: sourceFile.positionAt(context.replacementStartOffset),
@@ -1020,7 +1320,7 @@ function createProjectArtifacts(options) {
1020
1320
  function createInterpretSlot(uri, computed) {
1021
1321
  if (interpretation === void 0) return () => [];
1022
1322
  let memo;
1023
- return () => {
1323
+ const interpretDiagnostics = () => {
1024
1324
  if (memo === void 0) {
1025
1325
  const result = interpretation.source.interpret({
1026
1326
  document: computed.document,
@@ -1032,6 +1332,29 @@ function createProjectArtifacts(options) {
1032
1332
  }
1033
1333
  return memo;
1034
1334
  };
1335
+ return () => {
1336
+ try {
1337
+ return interpretDiagnostics();
1338
+ } catch (error) {
1339
+ if (error instanceof ResponseError && (error.code === LSPErrorCodes.RequestCancelled || error.code === LSPErrorCodes.ServerCancelled || error.code === LSPErrorCodes.ContentModified)) throw error;
1340
+ options.onInterpretationError(uri, error);
1341
+ return [{
1342
+ range: {
1343
+ start: {
1344
+ line: 0,
1345
+ character: 0
1346
+ },
1347
+ end: {
1348
+ line: 0,
1349
+ character: 1
1350
+ }
1351
+ },
1352
+ code: "PRISMA_NEXT_INTERPRETATION_FAILED",
1353
+ message: "Semantic diagnostics are unavailable because of an internal error. A subsequent diagnostic request or edit will retry.",
1354
+ severity: ParseDiagnosticSeverity.Error
1355
+ }];
1356
+ }
1357
+ };
1035
1358
  }
1036
1359
  function drop(uri) {
1037
1360
  if (documents.delete(uri)) symbolTable = void 0;
@@ -1578,6 +1901,10 @@ function createServerOn(connection) {
1578
1901
  inputs: resolution.inputs,
1579
1902
  controlStack: resolution.controlStack,
1580
1903
  getText: (uri) => documents.get(uri)?.getText(),
1904
+ onInterpretationError: (uri, error) => {
1905
+ const detail = error instanceof Error ? error.stack ?? error.message : String(error);
1906
+ connection.console.error(`PSL interpretation failed for ${uri}: ${detail}`);
1907
+ },
1581
1908
  ...resolution.interpretation === void 0 ? {} : { interpretation: resolution.interpretation }
1582
1909
  });
1583
1910
  return {
@@ -1679,7 +2006,8 @@ function createServerOn(connection) {
1679
2006
  ...project.controlStack.authoringContributions === void 0 ? {} : { authoringContributions: project.controlStack.authoringContributions },
1680
2007
  ...project.controlStack.controlMutationDefaults === void 0 ? {} : { controlMutationDefaults: project.controlStack.controlMutationDefaults }
1681
2008
  },
1682
- clientSupportsSnippets: clientCapabilities.completionSnippets
2009
+ clientSupportsSnippets: clientCapabilities.completionSnippets,
2010
+ clientSupportsTriggerSuggestCommand: clientCapabilities.completionTriggerSuggestCommand
1683
2011
  })];
1684
2012
  } catch {
1685
2013
  return [];
@@ -1698,7 +2026,15 @@ function createServerOn(connection) {
1698
2026
  full: true,
1699
2027
  range: true
1700
2028
  },
1701
- completionProvider: { triggerCharacters: [".", "@"] },
2029
+ completionProvider: { triggerCharacters: [
2030
+ ".",
2031
+ "@",
2032
+ "[",
2033
+ "(",
2034
+ "{",
2035
+ ":",
2036
+ ","
2037
+ ] },
1702
2038
  ...clientCapabilities.pullDiagnostics ? { diagnosticProvider: {
1703
2039
  interFileDependencies: false,
1704
2040
  workspaceDiagnostics: false
@@ -1817,6 +2153,7 @@ function toLspSeverity(severity) {
1817
2153
  const noClientCapabilities = {
1818
2154
  watchedFilesRegistration: false,
1819
2155
  completionSnippets: false,
2156
+ completionTriggerSuggestCommand: false,
1820
2157
  pullDiagnostics: false,
1821
2158
  diagnosticsRefresh: false
1822
2159
  };
@@ -1824,10 +2161,16 @@ function resolveClientCapabilities(params) {
1824
2161
  return {
1825
2162
  watchedFilesRegistration: params.capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration === true,
1826
2163
  completionSnippets: params.capabilities.textDocument?.completion?.completionItem?.snippetSupport === true,
2164
+ completionTriggerSuggestCommand: supportsCompletionTriggerSuggest(params.initializationOptions),
1827
2165
  pullDiagnostics: params.capabilities.textDocument?.diagnostic !== void 0,
1828
2166
  diagnosticsRefresh: params.capabilities.workspace?.diagnostics?.refreshSupport === true
1829
2167
  };
1830
2168
  }
2169
+ function supportsCompletionTriggerSuggest(options) {
2170
+ if (typeof options !== "object" || options === null || !("completion" in options)) return false;
2171
+ const completion = options.completion;
2172
+ return typeof completion === "object" && completion !== null && "supportsTriggerSuggestCommand" in completion && completion.supportsTriggerSuggestCommand === true;
2173
+ }
1831
2174
  function resolveRootPath(params) {
1832
2175
  const workspaceFolder = params.workspaceFolders?.[0];
1833
2176
  if (workspaceFolder !== void 0) return fileURLToPath(workspaceFolder.uri);
@@ -2091,4 +2434,4 @@ function startServer(streams) {
2091
2434
  //#endregion
2092
2435
  export { startServer };
2093
2436
 
2094
- //# sourceMappingURL=exports-CYa2di3d.mjs.map
2437
+ //# sourceMappingURL=exports-B8peSdx4.mjs.map