@prisma/orm-toolchain 8.0.0-rc.11-dev.19 → 8.0.0-rc.11-dev.21

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, LSPErrorCodes, ProposedFeatures, RegistrationRequest, ResponseError, SemanticTokenModifiers, SemanticTokenTypes, TextDocumentSyncKind, TextDocuments, createConnection } from "vscode-languageserver";
10
+ import { CompletionItemKind, ConnectionError, ConnectionErrors, DiagnosticSeverity, DidChangeWatchedFilesNotification, DocumentDiagnosticReportKind, FoldingRangeKind, InsertTextFormat, LSPErrorCodes, MarkupKind, ProposedFeatures, RegistrationRequest, ResponseError, SemanticTokenModifiers, SemanticTokenTypes, TextDocumentSyncKind, TextDocuments, createConnection } from "vscode-languageserver";
11
11
  import { TextDocument } from "vscode-languageserver-textdocument";
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";
12
+ import { ArrayLiteralAst, AttributeArgAst, AttributeArgListAst, BooleanLiteralExprAst, CompositeTypeDeclarationAst, FieldAttributeAst, FieldDeclarationAst, FunctionCallAst, GenericBlockDeclarationAst, IdentifierAst, KeyValuePairAst, ModelAttributeAst, ModelDeclarationAst, NamespaceDeclarationAst, NumberLiteralExprAst, ObjectLiteralExprAst, StringLiteralExprAst, SyntaxNode, TypesBlockAst, any, filterChildren, findChildToken, isTrivia, nonTriviaSibling, 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";
@@ -56,6 +56,77 @@ function pslSpanToRange(span, sourceFile) {
56
56
  end: sourceFile.positionAt(span.end.offset)
57
57
  };
58
58
  }
59
+ function locateAttributeSyntax(input) {
60
+ const offset = input.sourceFile.offsetAt(input.position);
61
+ const at = input.document.syntax.tokenAtOffset(offset);
62
+ if (at.leftBiased()?.kind === "Comment") return void 0;
63
+ const token = at.leftBiased() ?? at.rightBiased();
64
+ const attribute = token === void 0 ? void 0 : skipTriviaToken(token, "prev")?.parent.findAncestor(any(FieldAttributeAst.cast, ModelAttributeAst.cast));
65
+ if (attribute === void 0 || !isWithinAttributeOrOpenArguments(attribute, offset)) return void 0;
66
+ return {
67
+ attribute,
68
+ ...attributeCursor(attribute, offset)
69
+ };
70
+ }
71
+ function isWithinAttributeOrOpenArguments(attribute, offset) {
72
+ if (attribute.syntax.isInside(offset)) return true;
73
+ const args = attribute.argList();
74
+ return args !== void 0 && args.rparen() === void 0 && offset >= args.syntax.endOffset;
75
+ }
76
+ function attributeCursor(attribute, offset) {
77
+ const anchor = attribute.syntax.tokenAtOffset(offset).leftBiased() ?? attribute.syntax.lastToken;
78
+ return {
79
+ offset,
80
+ preceding: anchor === void 0 ? void 0 : skipTriviaToken(anchor, "prev")
81
+ };
82
+ }
83
+ function argumentSiblings(node, selected, offset) {
84
+ let precedingPositionalCount = 0;
85
+ const otherNamedKeys = [];
86
+ let beforeSelected = true;
87
+ for (const arg of node.args()) {
88
+ if (arg.syntax.offset === selected?.syntax.offset) {
89
+ beforeSelected = false;
90
+ continue;
91
+ }
92
+ const name = arg.name()?.name();
93
+ if (name !== void 0) otherNamedKeys.push(name);
94
+ else if (beforeSelected && arg.syntax.offset <= offset) precedingPositionalCount += 1;
95
+ }
96
+ return {
97
+ precedingPositionalCount,
98
+ otherNamedKeys
99
+ };
100
+ }
101
+ function argumentAtCursor(cursor, node) {
102
+ for (const arg of node.args()) if (arg.syntax.offset <= cursor.offset && (containsCursor(arg.syntax, cursor) || recoveredContainerContainsCursor(arg.value(), cursor.offset))) return arg;
103
+ }
104
+ function listElementAtCursor(cursor, node) {
105
+ for (const element of node.elements()) if (containsCursor(element.syntax, cursor) || recoveredContainerContainsCursor(element, cursor.offset)) return element;
106
+ }
107
+ function recordFieldAtCursor(cursor, node) {
108
+ for (const field of node.fields()) {
109
+ const colon = field.colon();
110
+ if (colon !== void 0 && cursor.offset > colon.offset && (containsCursor(field.syntax, cursor) || recoveredContainerContainsCursor(field.value(), cursor.offset))) return field;
111
+ }
112
+ }
113
+ function betweenDelimiters(offset, opening, closing) {
114
+ return opening !== void 0 && offset > opening.offset && (closing === void 0 || offset < closing.endOffset);
115
+ }
116
+ function recoveredContainerContainsCursor(expression, offset) {
117
+ if (expression === void 0 || expression.syntax.endOffset > offset) return false;
118
+ if (!(expression instanceof ArrayLiteralAst ? expression.rbracket() === void 0 : expression instanceof ObjectLiteralExprAst ? expression.rbrace() === void 0 : expression instanceof FunctionCallAst && expression.rparen() === void 0)) return false;
119
+ for (let token = expression.syntax.lastToken?.nextToken; token !== void 0; token = token.nextToken) {
120
+ if (token.offset >= offset) return true;
121
+ if (!isTrivia(token) && token.kind !== "Comma") return false;
122
+ }
123
+ return true;
124
+ }
125
+ function containsCursor(node, cursor) {
126
+ if (node.isInside(cursor.offset)) return true;
127
+ const preceding = cursor.preceding;
128
+ return preceding !== void 0 && preceding.endOffset <= cursor.offset && preceding.offset >= node.offset && preceding.offset < node.endOffset;
129
+ }
59
130
  const UNSUPPORTED = { kind: "unsupported" };
60
131
  function classifyPslCompletionContext(input) {
61
132
  const root = input.document.syntax;
@@ -205,34 +276,35 @@ function blockBodyContainsOffset(block, offset) {
205
276
  }
206
277
  function classifyFieldAttribute(input) {
207
278
  const attribute = input.node?.findAncestor(FieldAttributeAst.cast);
208
- if (attribute === void 0 || !attributeContainsOffset(attribute, input.offset)) return;
279
+ if (attribute === void 0 || !isWithinAttributeOrOpenArguments(attribute, input.offset)) return;
209
280
  const field = attribute.syntax.findAncestor(FieldDeclarationAst.cast);
210
281
  const model = attribute.syntax.findAncestor(ModelDeclarationAst.cast);
211
282
  if (field === void 0 || model === void 0) return UNSUPPORTED;
283
+ const owner = {
284
+ ownerKind: "field",
285
+ field,
286
+ model
287
+ };
212
288
  return classifyAttributePosition(attribute, input, {
213
289
  name: (position) => ({
214
290
  kind: "fieldAttributeName",
215
291
  ...position,
216
- field,
217
- model
292
+ ...owner
218
293
  }),
219
294
  namedKey: (position) => ({
220
295
  kind: "fieldAttributeNamedKey",
221
296
  ...position,
222
- field,
223
- model
297
+ ...owner
224
298
  }),
225
299
  argumentSlot: (position) => ({
226
300
  kind: "fieldAttributeArgumentSlot",
227
301
  ...position,
228
- field,
229
- model
302
+ ...owner
230
303
  }),
231
304
  value: (position) => ({
232
305
  kind: "fieldAttributeValue",
233
306
  ...position,
234
- field,
235
- model
307
+ ...owner
236
308
  })
237
309
  });
238
310
  }
@@ -242,30 +314,31 @@ function classifyGenericBlockAttribute(input) {
242
314
  if (attribute === void 0 || block === void 0) return;
243
315
  const blockKeyword = block.keyword()?.text;
244
316
  if (blockKeyword === void 0 || blockKeyword.length === 0) return UNSUPPORTED;
317
+ const owner = {
318
+ ownerKind: "block",
319
+ block,
320
+ blockKeyword
321
+ };
245
322
  return classifyAttributePosition(attribute, input, {
246
323
  name: (position) => ({
247
324
  kind: "blockAttributeName",
248
325
  ...position,
249
- block,
250
- blockKeyword
326
+ ...owner
251
327
  }),
252
328
  namedKey: (position) => ({
253
329
  kind: "blockAttributeNamedKey",
254
330
  ...position,
255
- block,
256
- blockKeyword
331
+ ...owner
257
332
  }),
258
333
  argumentSlot: (position) => ({
259
334
  kind: "blockAttributeArgumentSlot",
260
335
  ...position,
261
- block,
262
- blockKeyword
336
+ ...owner
263
337
  }),
264
338
  value: (position) => ({
265
339
  kind: "blockAttributeValue",
266
340
  ...position,
267
- block,
268
- blockKeyword
341
+ ...owner
269
342
  })
270
343
  });
271
344
  }
@@ -274,75 +347,62 @@ function classifyModelAttribute(input) {
274
347
  if (attribute === void 0) return;
275
348
  const model = attribute.syntax.findAncestor(ModelDeclarationAst.cast);
276
349
  if (model === void 0) return;
350
+ const owner = {
351
+ ownerKind: "model",
352
+ model
353
+ };
277
354
  return classifyAttributePosition(attribute, input, {
278
355
  name: (position) => ({
279
356
  kind: "modelAttributeName",
280
357
  ...position,
281
- model
358
+ ...owner
282
359
  }),
283
360
  namedKey: (position) => ({
284
361
  kind: "modelAttributeNamedKey",
285
362
  ...position,
286
- model
363
+ ...owner
287
364
  }),
288
365
  argumentSlot: (position) => ({
289
366
  kind: "modelAttributeArgumentSlot",
290
367
  ...position,
291
- model
368
+ ...owner
292
369
  }),
293
370
  value: (position) => ({
294
371
  kind: "modelAttributeValue",
295
372
  ...position,
296
- model
373
+ ...owner
297
374
  })
298
375
  });
299
376
  }
300
377
  function activeModelAttribute(input) {
301
378
  const attribute = input.node?.findAncestor(ModelAttributeAst.cast);
302
- if (attribute === void 0 || !attributeContainsOffset(attribute, input.offset)) return;
379
+ if (attribute === void 0 || !isWithinAttributeOrOpenArguments(attribute, input.offset)) return;
303
380
  return attribute;
304
381
  }
305
382
  function attributeAnchor(at) {
306
383
  const token = at.leftBiased() ?? at.rightBiased();
307
384
  return token === void 0 ? void 0 : skipTriviaToken(token, "prev")?.parent;
308
385
  }
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
386
  function classifyAttributePosition(attribute, input, factory) {
326
387
  const args = attribute.argList();
327
- if (isAttributeNamePosition(attribute, input.offset)) return factory.name({
388
+ if (args === void 0 || input.offset < args.syntax.offset) return factory.name({
328
389
  offset: input.offset,
329
390
  replacementStartOffset: input.replacementStartOffset,
330
391
  replacementEndOffset: attribute.name()?.syntax.endOffset ?? input.offset,
331
392
  hasArgumentList: args !== void 0
332
393
  });
333
- const attributeName = attributeArgumentName(attribute, input.offset);
334
- if (attributeName === void 0 || args === void 0) return UNSUPPORTED;
394
+ const attributeName = attribute.name()?.identifier()?.name();
395
+ if (attributeName === void 0) return UNSUPPORTED;
335
396
  const at = attribute.syntax.tokenAtOffset(input.offset);
336
397
  const right = at.rightBiased();
337
398
  const token = isValueToken(right) ? right : at.leftBiased();
338
399
  const replaceToken = isValueToken(token);
339
- const anchor = at.leftBiased() ?? attribute.syntax.lastToken;
340
- return classifyArguments({
400
+ return classifyAttributeArguments({
341
401
  offset: input.offset,
342
402
  replacementStartOffset: replaceToken ? token.offset : input.offset,
343
403
  replacementEndOffset: replaceToken ? token.endOffset : input.offset,
344
404
  attributeName,
345
- preceding: anchor === void 0 ? void 0 : skipTriviaToken(anchor, "prev"),
405
+ preceding: attributeCursor(attribute, input.offset).preceding,
346
406
  factory
347
407
  }, args, []);
348
408
  }
@@ -355,22 +415,10 @@ function argumentPosition(cursor, path) {
355
415
  path
356
416
  };
357
417
  }
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
- }
418
+ function classifyAttributeArguments(cursor, args, path) {
419
+ if (!betweenDelimiters(cursor.offset, args.lparen(), args.rparen())) return UNSUPPORTED;
420
+ const active = argumentAtCursor(cursor, args);
421
+ const { precedingPositionalCount: positionalIndex, otherNamedKeys: existingNamedKeys } = argumentSiblings(args, active, cursor.offset);
374
422
  const position = argumentPosition(cursor, path);
375
423
  if (active === void 0) return followsSeparator(cursor, ["LParen", "Comma"]) ? cursor.factory.argumentSlot({
376
424
  ...position,
@@ -386,7 +434,7 @@ function classifyArguments(cursor, container, path) {
386
434
  hasColon: true
387
435
  });
388
436
  const name = active.name()?.name();
389
- return name === void 0 ? UNSUPPORTED : classifyExpression(cursor, active.value(), [...path, {
437
+ return name === void 0 ? UNSUPPORTED : classifyAttributeExpression(cursor, active.value(), [...path, {
390
438
  kind: "namedArgument",
391
439
  name
392
440
  }]);
@@ -398,20 +446,33 @@ function classifyArguments(cursor, container, path) {
398
446
  existingNamedKeys,
399
447
  hasColon: false
400
448
  });
401
- return classifyExpression(cursor, value, [...path, {
449
+ return classifyAttributeExpression(cursor, value, [...path, {
402
450
  kind: "positionalArgument",
403
451
  index: positionalIndex
404
452
  }]);
405
453
  }
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);
454
+ function classifyAttributeExpression(cursor, expression, path) {
455
+ if (expression === void 0) return cursor.factory.value({
456
+ ...argumentPosition(cursor, path),
457
+ syntax: "scalar"
458
+ });
459
+ if (expression instanceof ArrayLiteralAst) {
460
+ if (!betweenDelimiters(cursor.offset, expression.lbracket(), expression.rbracket())) return UNSUPPORTED;
461
+ const element = listElementAtCursor(cursor, expression);
462
+ return element !== void 0 || followsSeparator(cursor, ["LBracket", "Comma"]) ? classifyAttributeExpression(cursor, element, [...path, { kind: "listElement" }]) : UNSUPPORTED;
463
+ }
464
+ if (expression instanceof ObjectLiteralExprAst) {
465
+ const closing = expression.rbrace();
466
+ if (closing !== void 0 && cursor.offset >= closing.endOffset) return UNSUPPORTED;
467
+ const field = recordFieldAtCursor(cursor, expression);
468
+ return field !== void 0 ? classifyAttributeExpression(cursor, field.value(), [...path, { kind: "recordValue" }]) : UNSUPPORTED;
469
+ }
409
470
  if (expression instanceof FunctionCallAst) {
410
471
  const opening = expression.lparen();
411
472
  if (opening !== void 0 && cursor.offset > opening.offset) {
412
473
  const name = expression.name();
413
474
  const identifier = name?.identifier()?.name();
414
- return identifier !== void 0 && name?.isSimpleName(identifier) === true ? classifyArguments(cursor, expression, [...path, {
475
+ return identifier !== void 0 && name?.isSimpleName(identifier) === true ? classifyAttributeArguments(cursor, expression, [...path, {
415
476
  kind: "functionCall",
416
477
  name: identifier
417
478
  }]) : UNSUPPORTED;
@@ -421,45 +482,14 @@ function classifyExpression(cursor, expression, path) {
421
482
  syntax: "functionName"
422
483
  }) : UNSUPPORTED;
423
484
  }
424
- return expression?.syntax.isOutside(cursor.offset) === true ? UNSUPPORTED : cursor.factory.value({
485
+ return expression.syntax.isOutside(cursor.offset) ? UNSUPPORTED : cursor.factory.value({
425
486
  ...argumentPosition(cursor, path),
426
487
  syntax: "scalar"
427
488
  });
428
489
  }
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;
436
- }
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
490
  function isValueToken(token) {
447
491
  return token !== void 0 && (token.kind === "Ident" || token.kind === "StringLiteral" || token.kind === "NumberLiteral");
448
492
  }
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
493
  function followsSeparator(cursor, kinds) {
464
494
  return kinds.includes(cursor.preceding?.kind ?? "");
465
495
  }
@@ -513,35 +543,6 @@ function cursorIdentifier(at, offset) {
513
543
  const left = at.leftBiased();
514
544
  if (left?.kind === "Ident" && left.endOffset === offset) return left;
515
545
  }
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
546
  function modelSymbolForNode(symbolTable, node) {
546
547
  const topLevelMatch = Object.values(symbolTable.topLevel.models).find((model) => sameSyntax(model.node.syntax, node.syntax));
547
548
  if (topLevelMatch !== void 0) return topLevelMatch;
@@ -594,18 +595,75 @@ function referencedModel(symbols, model, field) {
594
595
  function sameSyntax(left, right) {
595
596
  return left.offset === right.offset && left.endOffset === right.endOffset;
596
597
  }
597
- function provideAttributeNamedKeyCompletionItems(input, spec) {
598
- return orderedItems(resolveGrammar(spec, input.context.path).flatMap((grammar) => "kind" in grammar ? [] : namedKeyItems(input, grammar)));
598
+ function attributeSpecResolver(context, source) {
599
+ switch (context.ownerKind) {
600
+ case "block": {
601
+ const descriptor = findBlockDescriptor(source.pslBlockDescriptors, context.blockKeyword);
602
+ return (name) => {
603
+ const factory = descriptor?.attributes?.[name];
604
+ if (factory === void 0) return void 0;
605
+ return blindCast(factory)();
606
+ };
607
+ }
608
+ case "model": {
609
+ if (source.authoringContributions === void 0) return () => void 0;
610
+ const model = modelSymbolForNode(source.symbolTable, context.model);
611
+ if (model === void 0 || source.controlMutationDefaults === void 0) return () => void 0;
612
+ const specs = assembleAttributeSpecs(source.authoringContributions);
613
+ const specContext = {
614
+ symbols: source.symbolTable,
615
+ model,
616
+ controlMutationDefaults: source.controlMutationDefaults.defaultFunctionRegistry
617
+ };
618
+ return (name) => specs.model[name]?.(specContext);
619
+ }
620
+ case "field": {
621
+ if (source.authoringContributions === void 0) return () => void 0;
622
+ const model = modelSymbolForNode(source.symbolTable, context.model);
623
+ if (model === void 0 || source.controlMutationDefaults === void 0) return () => void 0;
624
+ const field = fieldSymbolForNode(model, context.field);
625
+ if (field === void 0) return () => void 0;
626
+ const specs = assembleAttributeSpecs(source.authoringContributions);
627
+ const specContext = {
628
+ symbols: source.symbolTable,
629
+ model,
630
+ controlMutationDefaults: source.controlMutationDefaults.defaultFunctionRegistry
631
+ };
632
+ return (name) => specs.field[name]?.({
633
+ ...specContext,
634
+ field
635
+ });
636
+ }
637
+ }
599
638
  }
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
- }));
639
+ function requiredArgumentsSnippet(signature) {
640
+ return requiredArguments(signature).map((argument, index) => requiredArgumentSnippet(argument, index + 1)).join(", ");
606
641
  }
607
- function provideAttributeValueCompletionItems(input, spec) {
608
- return orderedItems(resolveGrammar(spec, input.context.path).flatMap((grammar) => "kind" in grammar ? valueItems(input, grammar, input.context.syntax) : []));
642
+ function requiredArguments(signature) {
643
+ const positional = signature.positional ?? [];
644
+ const positionalKeys = new Set(positional.map((argument) => argument.key));
645
+ return [...positional.flatMap((argument) => isOptionalParam(argument.type) ? [] : [{
646
+ kind: "positional",
647
+ argument
648
+ }]), ...Object.entries(signature.named ?? {}).flatMap(([key, { type }]) => positionalKeys.has(key) || isOptionalParam(type) ? [] : [{
649
+ kind: "named",
650
+ key,
651
+ type
652
+ }])];
653
+ }
654
+ function requiredArgumentSnippet(argument, tabStop) {
655
+ if (argument.kind === "positional") return argSnippetPlaceholder(argument.argument.type, tabStop);
656
+ return `${argument.key}: ${argSnippetPlaceholder(argument.type, tabStop)}`;
657
+ }
658
+ function argSnippetPlaceholder(param, tabStop) {
659
+ const placeholder = `\${${tabStop.toString()}:}`;
660
+ if (param.kind === "str") return `"${placeholder}"`;
661
+ if (param.kind === "list") return `[${placeholder}]`;
662
+ if (param.kind === "record") return `{ ${placeholder} }`;
663
+ return placeholder;
664
+ }
665
+ function isOptionalParam(param) {
666
+ return "optional" in param && param.optional === true;
609
667
  }
610
668
  function directArgType(param) {
611
669
  return blindCast(param);
@@ -626,7 +684,7 @@ function advanceGrammar(grammar, step) {
626
684
  }
627
685
  case "namedArgument": {
628
686
  if ("kind" in grammar) return [];
629
- const param = grammar.named?.[step.name];
687
+ const param = grammar.named?.[step.name]?.type;
630
688
  return param === void 0 ? [] : [param];
631
689
  }
632
690
  case "listElement": return type?.kind === "list" ? [type.of] : [];
@@ -634,6 +692,19 @@ function advanceGrammar(grammar, step) {
634
692
  case "functionCall": return type?.kind === "funcCall" && type.name === step.name ? [type.signature] : [];
635
693
  }
636
694
  }
695
+ function provideAttributeNamedKeyCompletionItems(input, spec) {
696
+ return orderedItems(resolveGrammar(spec, input.context.path).flatMap((grammar) => "kind" in grammar ? [] : namedKeyItems(input, grammar)));
697
+ }
698
+ function provideAttributeArgumentSlotCompletionItems(input, spec) {
699
+ return orderedItems(resolveGrammar(spec, input.context.path).flatMap((grammar) => {
700
+ if ("kind" in grammar) return [];
701
+ const param = grammar.positional?.[input.context.positionalIndex]?.type;
702
+ return [...valueItems(input, param, "scalar"), ...namedKeyItems(input, grammar)];
703
+ }));
704
+ }
705
+ function provideAttributeValueCompletionItems(input, spec) {
706
+ return orderedItems(resolveGrammar(spec, input.context.path).flatMap((grammar) => "kind" in grammar ? valueItems(input, grammar, input.context.syntax) : []));
707
+ }
637
708
  function namedKeyItems(input, signature) {
638
709
  return Object.keys(signature.named ?? {}).filter((name) => !input.context.existingNamedKeys.includes(name)).map((name) => {
639
710
  const snippet = input.clientSupportsSnippets && !input.context.hasColon;
@@ -653,8 +724,16 @@ function valueItems(input, param, syntax) {
653
724
  if (type.kind === "oneOf") return type.alternatives.flatMap((alternative) => valueItems(input, alternative, syntax));
654
725
  if (type.kind === "funcCall") {
655
726
  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)];
727
+ const hasParameters = (type.signature.positional?.length ?? 0) > 0 || Object.keys(type.signature.named ?? {}).length > 0;
728
+ const args = requiredArgumentsSnippet(type.signature) || (hasParameters ? "${1:}" : "");
729
+ const text = snippet ? `${type.name}(${args})` : type.name;
730
+ return [{
731
+ ...completionItem(input, type.name, text, CompletionItemKind.Function, snippet),
732
+ ...snippet && input.clientSupportsTriggerParameterHintsCommand === true && hasParameters ? { command: {
733
+ title: "Show argument hints",
734
+ command: "editor.action.triggerParameterHints"
735
+ } } : {}
736
+ }];
658
737
  }
659
738
  if (syntax === "functionName") return [];
660
739
  switch (type.kind) {
@@ -747,7 +826,7 @@ function providePslCompletionItems(input) {
747
826
  case "genericBlockValue": return [];
748
827
  case "fieldAttributeName":
749
828
  case "modelAttributeName":
750
- case "blockAttributeName": return provideAttributeNameCompletionItems(context, input.sourceFile, input.candidates, input.clientSupportsSnippets);
829
+ case "blockAttributeName": return provideAttributeNameCompletionItems(context, input.sourceFile, input.candidates, input.clientSupportsSnippets, input.clientSupportsTriggerParameterHintsCommand === true);
751
830
  case "fieldAttributeNamedKey":
752
831
  case "modelAttributeNamedKey":
753
832
  case "blockAttributeNamedKey": {
@@ -768,6 +847,7 @@ function providePslCompletionItems(input) {
768
847
  sourceFile: input.sourceFile,
769
848
  clientSupportsSnippets: input.clientSupportsSnippets,
770
849
  clientSupportsTriggerSuggestCommand: input.clientSupportsTriggerSuggestCommand === true,
850
+ clientSupportsTriggerParameterHintsCommand: input.clientSupportsTriggerParameterHintsCommand === true,
771
851
  fieldNames: (kind) => kind === "fieldRef" ? localFieldNames(context, input.candidates.symbolTable) : referencedFieldNames(context, input.candidates.symbolTable)
772
852
  }, spec);
773
853
  }
@@ -779,6 +859,7 @@ function providePslCompletionItems(input) {
779
859
  context,
780
860
  sourceFile: input.sourceFile,
781
861
  clientSupportsSnippets: input.clientSupportsSnippets,
862
+ clientSupportsTriggerParameterHintsCommand: input.clientSupportsTriggerParameterHintsCommand === true,
782
863
  fieldNames: (kind) => kind === "fieldRef" ? localFieldNames(context, input.candidates.symbolTable) : referencedFieldNames(context, input.candidates.symbolTable)
783
864
  }, spec);
784
865
  }
@@ -788,7 +869,7 @@ function providePslCompletionItems(input) {
788
869
  case "namespaceMember": return provideNamespaceMemberCompletionItems(context, input.sourceFile, input.candidates);
789
870
  }
790
871
  }
791
- function provideAttributeNameCompletionItems(context, sourceFile, source, clientSupportsSnippets) {
872
+ function provideAttributeNameCompletionItems(context, sourceFile, source, clientSupportsSnippets, clientSupportsTriggerParameterHintsCommand) {
792
873
  const names = attributeNames(context, source);
793
874
  const replacementRange = {
794
875
  start: sourceFile.positionAt(context.replacementStartOffset),
@@ -812,7 +893,11 @@ function provideAttributeNameCompletionItems(context, sourceFile, source, client
812
893
  range: replacementRange,
813
894
  newText
814
895
  },
815
- ...newText !== name ? { insertTextFormat: InsertTextFormat.Snippet } : {}
896
+ ...newText !== name ? { insertTextFormat: InsertTextFormat.Snippet } : {},
897
+ ...newText !== name && clientSupportsTriggerParameterHintsCommand ? { command: {
898
+ title: "Show argument hints",
899
+ command: "editor.action.triggerParameterHints"
900
+ } } : {}
816
901
  };
817
902
  });
818
903
  }
@@ -835,56 +920,6 @@ function attributeNameEditText(input) {
835
920
  const required = requiredArgumentsSnippet(input.spec);
836
921
  return required.length === 0 ? input.name : `${input.name}(${required})`;
837
922
  }
838
- function attributeSpecResolver(context, source) {
839
- switch (context.kind) {
840
- case "blockAttributeName":
841
- case "blockAttributeNamedKey":
842
- case "blockAttributeArgumentSlot":
843
- case "blockAttributeValue": {
844
- const descriptor = findBlockDescriptor(source.pslBlockDescriptors, context.blockKeyword);
845
- return (name) => {
846
- const factory = descriptor?.attributes?.[name];
847
- if (factory === void 0) return;
848
- return blindCast(factory)();
849
- };
850
- }
851
- case "modelAttributeName":
852
- case "modelAttributeNamedKey":
853
- case "modelAttributeArgumentSlot":
854
- case "modelAttributeValue": {
855
- if (source.authoringContributions === void 0) return () => void 0;
856
- const model = modelSymbolForNode(source.symbolTable, context.model);
857
- if (model === void 0 || source.controlMutationDefaults === void 0) return () => void 0;
858
- const specs = assembleAttributeSpecs(source.authoringContributions);
859
- const specContext = {
860
- symbols: source.symbolTable,
861
- model,
862
- controlMutationDefaults: source.controlMutationDefaults.defaultFunctionRegistry
863
- };
864
- return (name) => specs.model[name]?.(specContext);
865
- }
866
- case "fieldAttributeName":
867
- case "fieldAttributeNamedKey":
868
- case "fieldAttributeArgumentSlot":
869
- case "fieldAttributeValue": {
870
- if (source.authoringContributions === void 0) return () => void 0;
871
- const model = modelSymbolForNode(source.symbolTable, context.model);
872
- if (model === void 0 || source.controlMutationDefaults === void 0) return () => void 0;
873
- const field = fieldSymbolForNode(model, context.field);
874
- if (field === void 0) return () => void 0;
875
- const specs = assembleAttributeSpecs(source.authoringContributions);
876
- const specContext = {
877
- symbols: source.symbolTable,
878
- model,
879
- controlMutationDefaults: source.controlMutationDefaults.defaultFunctionRegistry
880
- };
881
- return (name) => specs.field[name]?.({
882
- ...specContext,
883
- field
884
- });
885
- }
886
- }
887
- }
888
923
  function provideDeclarationKeywordCompletionItems(context, sourceFile, source, clientSupportsSnippets) {
889
924
  const replacementRange = {
890
925
  start: sourceFile.positionAt(context.replacementStartOffset),
@@ -1728,6 +1763,193 @@ function createPendingSemanticToken(startOffset, endOffset, tokenType, modifierB
1728
1763
  function tokenTypeIndex(tokenType) {
1729
1764
  return semanticTokenTypes.indexOf(tokenType);
1730
1765
  }
1766
+ function classifyPslSignatureContext(input) {
1767
+ const syntax = locateAttributeSyntax(input);
1768
+ if (syntax === void 0) return void 0;
1769
+ const attributeName = syntax.attribute.name()?.identifier()?.name();
1770
+ const args = syntax.attribute.argList();
1771
+ const position = args === void 0 ? void 0 : signatureArguments(syntax, args, []);
1772
+ const owner = signatureOwner(syntax.attribute);
1773
+ return attributeName === void 0 || position === void 0 || owner === void 0 ? void 0 : {
1774
+ ...owner,
1775
+ ...position,
1776
+ attributeName
1777
+ };
1778
+ }
1779
+ function signatureOwner(attribute) {
1780
+ if (attribute instanceof FieldAttributeAst) {
1781
+ const field = attribute.syntax.findAncestor(FieldDeclarationAst.cast);
1782
+ const model = attribute.syntax.findAncestor(ModelDeclarationAst.cast);
1783
+ return field === void 0 || model === void 0 ? void 0 : {
1784
+ ownerKind: "field",
1785
+ field,
1786
+ model
1787
+ };
1788
+ }
1789
+ const block = attribute.syntax.findAncestor(GenericBlockDeclarationAst.cast);
1790
+ if (block !== void 0) {
1791
+ const blockKeyword = block.keyword()?.text;
1792
+ return blockKeyword === void 0 || blockKeyword.length === 0 ? void 0 : {
1793
+ ownerKind: "block",
1794
+ block,
1795
+ blockKeyword
1796
+ };
1797
+ }
1798
+ const model = attribute.syntax.findAncestor(ModelDeclarationAst.cast);
1799
+ return model === void 0 ? void 0 : {
1800
+ ownerKind: "model",
1801
+ model
1802
+ };
1803
+ }
1804
+ function signatureArguments(context, args, path) {
1805
+ if (!betweenDelimiters(context.offset, args.lparen(), args.rparen())) return void 0;
1806
+ const containing = argumentAtCursor(context, args);
1807
+ const next = context.preceding?.kind === "Comma" ? nonTriviaSibling(context.preceding, "next") : void 0;
1808
+ const following = next instanceof SyntaxNode && next.parent?.offset === args.syntax.offset ? AttributeArgAst.cast(next) : void 0;
1809
+ const active = containing ?? following;
1810
+ const { precedingPositionalCount: positionalIndex, otherNamedKeys: existingNamedKeys } = argumentSiblings(args, active, context.offset);
1811
+ if (active === void 0) return ["LParen", "Comma"].includes(context.preceding?.kind ?? "") ? {
1812
+ path,
1813
+ argumentSlot: {
1814
+ positionalIndex,
1815
+ existingNamedKeys
1816
+ }
1817
+ } : void 0;
1818
+ if (active.colon() !== void 0) {
1819
+ const name = active.name()?.name();
1820
+ if (name === void 0) return void 0;
1821
+ const valuePath = [...path, {
1822
+ kind: "namedArgument",
1823
+ name
1824
+ }];
1825
+ return active === containing ? signatureExpression(context, active.value(), valuePath) : {
1826
+ path: valuePath,
1827
+ argumentSlot: void 0
1828
+ };
1829
+ }
1830
+ const value = active.value();
1831
+ if (value === void 0 || value instanceof IdentifierAst && value.syntax.isInside(context.offset)) return {
1832
+ path,
1833
+ argumentSlot: {
1834
+ positionalIndex,
1835
+ existingNamedKeys
1836
+ }
1837
+ };
1838
+ const valuePath = [...path, {
1839
+ kind: "positionalArgument",
1840
+ index: positionalIndex
1841
+ }];
1842
+ return active === containing ? signatureExpression(context, value, valuePath) : {
1843
+ path: valuePath,
1844
+ argumentSlot: void 0
1845
+ };
1846
+ }
1847
+ function signatureExpression(context, expression, path) {
1848
+ const position = {
1849
+ path,
1850
+ argumentSlot: void 0
1851
+ };
1852
+ if (expression === void 0 || context.offset <= expression.syntax.offset || context.offset >= expression.syntax.endOffset && !recoveredContainerContainsCursor(expression, context.offset)) return position;
1853
+ if (expression instanceof ArrayLiteralAst) return betweenDelimiters(context.offset, expression.lbracket(), expression.rbracket()) ? signatureExpression(context, listElementAtCursor(context, expression), [...path, { kind: "listElement" }]) : void 0;
1854
+ if (expression instanceof ObjectLiteralExprAst) {
1855
+ const closing = expression.rbrace();
1856
+ if (closing !== void 0 && context.offset >= closing.endOffset) return void 0;
1857
+ const field = recordFieldAtCursor(context, expression);
1858
+ return field !== void 0 ? signatureExpression(context, field.value(), [...path, { kind: "recordValue" }]) : position;
1859
+ }
1860
+ if (expression instanceof FunctionCallAst) {
1861
+ const opening = expression.lparen();
1862
+ if (opening !== void 0 && context.offset > opening.offset) {
1863
+ const name = expression.name();
1864
+ const identifier = name?.identifier()?.name();
1865
+ return identifier !== void 0 && name?.isSimpleName(identifier) === true ? signatureArguments(context, expression, [...path, {
1866
+ kind: "functionCall",
1867
+ name: identifier
1868
+ }]) : void 0;
1869
+ }
1870
+ return expression.name()?.syntax.isInside(context.offset) === true ? position : void 0;
1871
+ }
1872
+ return position;
1873
+ }
1874
+ function providePslSignatureHelp(input) {
1875
+ const context = classifyPslSignatureContext(input);
1876
+ if (context === void 0) return null;
1877
+ const spec = attributeSpecResolver(context, input.candidates)(context.attributeName);
1878
+ if (spec === void 0) return null;
1879
+ return signatureHelp(context, spec, input.clientSupportsLabelOffsets === true);
1880
+ }
1881
+ function signatureHelp(context, spec, labelOffsets) {
1882
+ const callIndex = context.path.reduce((last, step, index) => step.kind === "functionCall" ? index : last, -1);
1883
+ const call = context.path[callIndex];
1884
+ const signaturePath = context.path.slice(0, callIndex + 1);
1885
+ const name = call?.kind === "functionCall" ? call.name : `${spec.level === "field" ? "@" : "@@"}${spec.name}`;
1886
+ const signatures = resolveGrammar(spec, signaturePath).flatMap((grammar) => {
1887
+ if ("kind" in grammar) return [];
1888
+ const active = context.path[callIndex + 1];
1889
+ const params = parameters(grammar, active?.kind === "namedArgument" ? active.name : void 0);
1890
+ const index = parameterIndex(context, active, params);
1891
+ return [{
1892
+ signature: renderSignature(name, grammar, params, labelOffsets),
1893
+ index
1894
+ }];
1895
+ });
1896
+ if (signatures.length === 0) return null;
1897
+ const matched = signatures.findIndex(({ signature, index }) => index >= 0 && index < (signature.parameters?.length ?? 0));
1898
+ const activeSignature = matched < 0 ? 0 : matched;
1899
+ const activeParameter = signatures[activeSignature]?.index;
1900
+ return {
1901
+ signatures: signatures.map(({ signature }) => signature),
1902
+ activeSignature,
1903
+ ...matched >= 0 && activeParameter !== void 0 ? { activeParameter } : {}
1904
+ };
1905
+ }
1906
+ function parameterIndex(context, active, params) {
1907
+ if (active?.kind === "namedArgument") return params.findIndex((param) => param.key === active.name);
1908
+ if (active?.kind === "positionalArgument") return active.index;
1909
+ const slot = context.argumentSlot;
1910
+ if (slot !== void 0) return params.findIndex((param, index) => index >= slot.positionalIndex && !slot.existingNamedKeys.includes(param.key));
1911
+ return -1;
1912
+ }
1913
+ function parameters(signature, activeName) {
1914
+ const positional = signature.positional ?? [];
1915
+ const keys = new Set(positional.map((param) => param.key));
1916
+ const namedAlias = activeName === void 0 ? void 0 : signature.named?.[activeName];
1917
+ return [...positional.map((param) => param.key === activeName && namedAlias !== void 0 ? {
1918
+ key: param.key,
1919
+ ...namedAlias
1920
+ } : param), ...Object.entries(signature.named ?? {}).flatMap(([key, param]) => keys.has(key) ? [] : [{
1921
+ key,
1922
+ ...param
1923
+ }])];
1924
+ }
1925
+ function renderSignature(name, signature, params, labelOffsets) {
1926
+ let label = `${name}(`;
1927
+ const rendered = params.map((param, index) => {
1928
+ if (index > 0) label += ", ";
1929
+ const positional = index < (signature.positional?.length ?? 0);
1930
+ const optional = "optional" in param.type && param.type.optional === true;
1931
+ const typeLabel = optional && param.type.label.includes(" | ") ? `(${param.type.label})` : param.type.label;
1932
+ const text = positional ? `${typeLabel}${optional ? "?" : ""}` : `${param.key}${optional ? "?" : ""}: ${param.type.label}`;
1933
+ const start = label.length;
1934
+ label += text;
1935
+ const documentation = positional ? `**${param.key}**\n\n${param.documentation}${signature.named?.[param.key] === void 0 ? "" : `\n\nAccepted positionally or by \`${param.key}:\`.`}` : param.documentation;
1936
+ return {
1937
+ label: positional && labelOffsets ? [start, label.length] : text,
1938
+ documentation: {
1939
+ kind: MarkupKind.Markdown,
1940
+ value: documentation
1941
+ }
1942
+ };
1943
+ });
1944
+ return {
1945
+ label: `${label})`,
1946
+ documentation: {
1947
+ kind: MarkupKind.Markdown,
1948
+ value: signature.documentation
1949
+ },
1950
+ parameters: rendered
1951
+ };
1952
+ }
1731
1953
  /** The project a new load of this config could fall back to. */
1732
1954
  function lastGoodProject(entry) {
1733
1955
  if (entry === void 0 || entry.status === "failed") return;
@@ -2007,12 +2229,36 @@ function createServerOn(connection) {
2007
2229
  ...project.controlStack.controlMutationDefaults === void 0 ? {} : { controlMutationDefaults: project.controlStack.controlMutationDefaults }
2008
2230
  },
2009
2231
  clientSupportsSnippets: clientCapabilities.completionSnippets,
2010
- clientSupportsTriggerSuggestCommand: clientCapabilities.completionTriggerSuggestCommand
2232
+ clientSupportsTriggerSuggestCommand: clientCapabilities.completionTriggerSuggestCommand,
2233
+ clientSupportsTriggerParameterHintsCommand: clientCapabilities.completionTriggerParameterHintsCommand
2011
2234
  })];
2012
2235
  } catch {
2013
2236
  return [];
2014
2237
  }
2015
2238
  }
2239
+ async function signatureHelpForDocument(uri, position) {
2240
+ if (documents.get(uri) === void 0) return null;
2241
+ const project = await resolveProjectForDocument(uri);
2242
+ if (project === void 0) return null;
2243
+ const artifacts = project.artifacts.document(uri);
2244
+ if (artifacts === void 0) return null;
2245
+ try {
2246
+ return providePslSignatureHelp({
2247
+ clientSupportsLabelOffsets: clientCapabilities.signatureLabelOffsets,
2248
+ document: artifacts.document,
2249
+ sourceFile: artifacts.sourceFile,
2250
+ position,
2251
+ candidates: {
2252
+ pslBlockDescriptors: project.controlStack.pslBlockDescriptors,
2253
+ symbolTable: project.artifacts.symbolTable(),
2254
+ ...project.controlStack.authoringContributions === void 0 ? {} : { authoringContributions: project.controlStack.authoringContributions },
2255
+ ...project.controlStack.controlMutationDefaults === void 0 ? {} : { controlMutationDefaults: project.controlStack.controlMutationDefaults }
2256
+ }
2257
+ });
2258
+ } catch {
2259
+ return null;
2260
+ }
2261
+ }
2016
2262
  connection.onInitialize(async (params) => {
2017
2263
  rootPath = resolveRootPath(params);
2018
2264
  watchedConfigGlob = join(rootPath, "**", CONFIG_FILENAME);
@@ -2035,6 +2281,7 @@ function createServerOn(connection) {
2035
2281
  ":",
2036
2282
  ","
2037
2283
  ] },
2284
+ signatureHelpProvider: { triggerCharacters: ["(", ","] },
2038
2285
  ...clientCapabilities.pullDiagnostics ? { diagnosticProvider: {
2039
2286
  interFileDependencies: false,
2040
2287
  workspaceDiagnostics: false
@@ -2063,6 +2310,7 @@ function createServerOn(connection) {
2063
2310
  });
2064
2311
  connection.onDocumentFormatting((params) => formatDocument(params.textDocument.uri));
2065
2312
  connection.onCompletion((params) => completeDocument(params.textDocument.uri, params.position));
2313
+ connection.onSignatureHelp((params) => signatureHelpForDocument(params.textDocument.uri, params.position));
2066
2314
  connection.languages.semanticTokens.on((params) => semanticTokensForDocument(params.textDocument.uri));
2067
2315
  connection.languages.semanticTokens.onRange((params) => semanticTokensForDocument(params.textDocument.uri, params.range));
2068
2316
  connection.languages.diagnostics.on(async (params) => {
@@ -2153,7 +2401,9 @@ function toLspSeverity(severity) {
2153
2401
  const noClientCapabilities = {
2154
2402
  watchedFilesRegistration: false,
2155
2403
  completionSnippets: false,
2404
+ signatureLabelOffsets: false,
2156
2405
  completionTriggerSuggestCommand: false,
2406
+ completionTriggerParameterHintsCommand: false,
2157
2407
  pullDiagnostics: false,
2158
2408
  diagnosticsRefresh: false
2159
2409
  };
@@ -2161,15 +2411,17 @@ function resolveClientCapabilities(params) {
2161
2411
  return {
2162
2412
  watchedFilesRegistration: params.capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration === true,
2163
2413
  completionSnippets: params.capabilities.textDocument?.completion?.completionItem?.snippetSupport === true,
2164
- completionTriggerSuggestCommand: supportsCompletionTriggerSuggest(params.initializationOptions),
2414
+ signatureLabelOffsets: params.capabilities.textDocument?.signatureHelp?.signatureInformation?.parameterInformation?.labelOffsetSupport === true,
2415
+ completionTriggerSuggestCommand: supportsCompletionCommand(params.initializationOptions, "supportsTriggerSuggestCommand"),
2416
+ completionTriggerParameterHintsCommand: supportsCompletionCommand(params.initializationOptions, "supportsTriggerParameterHintsCommand"),
2165
2417
  pullDiagnostics: params.capabilities.textDocument?.diagnostic !== void 0,
2166
2418
  diagnosticsRefresh: params.capabilities.workspace?.diagnostics?.refreshSupport === true
2167
2419
  };
2168
2420
  }
2169
- function supportsCompletionTriggerSuggest(options) {
2421
+ function supportsCompletionCommand(options, capability) {
2170
2422
  if (typeof options !== "object" || options === null || !("completion" in options)) return false;
2171
2423
  const completion = options.completion;
2172
- return typeof completion === "object" && completion !== null && "supportsTriggerSuggestCommand" in completion && completion.supportsTriggerSuggestCommand === true;
2424
+ return typeof completion === "object" && completion !== null && capability in completion && Reflect.get(completion, capability) === true;
2173
2425
  }
2174
2426
  function resolveRootPath(params) {
2175
2427
  const workspaceFolder = params.workspaceFolders?.[0];
@@ -2434,4 +2686,4 @@ function startServer(streams) {
2434
2686
  //#endregion
2435
2687
  export { startServer };
2436
2688
 
2437
- //# sourceMappingURL=exports-DGC_nDQ0.mjs.map
2689
+ //# sourceMappingURL=exports-HR2MWHrQ.mjs.map