@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 || !
|
|
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
|
-
|
|
217
|
-
model
|
|
292
|
+
...owner
|
|
218
293
|
}),
|
|
219
294
|
namedKey: (position) => ({
|
|
220
295
|
kind: "fieldAttributeNamedKey",
|
|
221
296
|
...position,
|
|
222
|
-
|
|
223
|
-
model
|
|
297
|
+
...owner
|
|
224
298
|
}),
|
|
225
299
|
argumentSlot: (position) => ({
|
|
226
300
|
kind: "fieldAttributeArgumentSlot",
|
|
227
301
|
...position,
|
|
228
|
-
|
|
229
|
-
model
|
|
302
|
+
...owner
|
|
230
303
|
}),
|
|
231
304
|
value: (position) => ({
|
|
232
305
|
kind: "fieldAttributeValue",
|
|
233
306
|
...position,
|
|
234
|
-
|
|
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
|
-
|
|
250
|
-
blockKeyword
|
|
326
|
+
...owner
|
|
251
327
|
}),
|
|
252
328
|
namedKey: (position) => ({
|
|
253
329
|
kind: "blockAttributeNamedKey",
|
|
254
330
|
...position,
|
|
255
|
-
|
|
256
|
-
blockKeyword
|
|
331
|
+
...owner
|
|
257
332
|
}),
|
|
258
333
|
argumentSlot: (position) => ({
|
|
259
334
|
kind: "blockAttributeArgumentSlot",
|
|
260
335
|
...position,
|
|
261
|
-
|
|
262
|
-
blockKeyword
|
|
336
|
+
...owner
|
|
263
337
|
}),
|
|
264
338
|
value: (position) => ({
|
|
265
339
|
kind: "blockAttributeValue",
|
|
266
340
|
...position,
|
|
267
|
-
|
|
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
|
-
|
|
358
|
+
...owner
|
|
282
359
|
}),
|
|
283
360
|
namedKey: (position) => ({
|
|
284
361
|
kind: "modelAttributeNamedKey",
|
|
285
362
|
...position,
|
|
286
|
-
|
|
363
|
+
...owner
|
|
287
364
|
}),
|
|
288
365
|
argumentSlot: (position) => ({
|
|
289
366
|
kind: "modelAttributeArgumentSlot",
|
|
290
367
|
...position,
|
|
291
|
-
|
|
368
|
+
...owner
|
|
292
369
|
}),
|
|
293
370
|
value: (position) => ({
|
|
294
371
|
kind: "modelAttributeValue",
|
|
295
372
|
...position,
|
|
296
|
-
|
|
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 || !
|
|
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 (
|
|
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 =
|
|
334
|
-
if (attributeName === void 0
|
|
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
|
-
|
|
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:
|
|
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
|
|
359
|
-
|
|
360
|
-
const
|
|
361
|
-
|
|
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 :
|
|
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
|
|
449
|
+
return classifyAttributeExpression(cursor, value, [...path, {
|
|
402
450
|
kind: "positionalArgument",
|
|
403
451
|
index: positionalIndex
|
|
404
452
|
}]);
|
|
405
453
|
}
|
|
406
|
-
function
|
|
407
|
-
if (expression
|
|
408
|
-
|
|
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 ?
|
|
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
|
|
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
|
|
598
|
-
|
|
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
|
|
601
|
-
return
|
|
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
|
|
608
|
-
|
|
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
|
|
657
|
-
|
|
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
|
-
|
|
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
|
|
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 &&
|
|
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-
|
|
2689
|
+
//# sourceMappingURL=exports-HR2MWHrQ.mjs.map
|