@prisma-next/language-server 0.14.0-dev.21 → 0.14.0-dev.23

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.
@@ -0,0 +1,701 @@
1
+ import type { SymbolTable } from '@prisma-next/psl-parser';
2
+ import {
3
+ ArrayLiteralAst,
4
+ type AttributeArgAst,
5
+ type AttributeArgListAst,
6
+ type AttributeAst,
7
+ type BlockMemberAst,
8
+ BooleanLiteralExprAst,
9
+ CompositeTypeDeclarationAst,
10
+ type DeclarationAst,
11
+ type DocumentAst,
12
+ type ExpressionAst,
13
+ FieldDeclarationAst,
14
+ FunctionCallAst,
15
+ filterChildren,
16
+ type GenericBlockMemberAst,
17
+ IdentifierAst,
18
+ ModelDeclarationAst,
19
+ type NamedTypeDeclarationAst,
20
+ NamespaceDeclarationAst,
21
+ NumberLiteralExprAst,
22
+ ObjectLiteralExprAst,
23
+ type QualifiedNameAst,
24
+ type SourceFile,
25
+ StringLiteralExprAst,
26
+ type SyntaxToken,
27
+ type TypeAnnotationAst,
28
+ TypesBlockAst,
29
+ } from '@prisma-next/psl-parser/syntax';
30
+ import {
31
+ type Range,
32
+ SemanticTokenModifiers,
33
+ type SemanticTokens,
34
+ type SemanticTokensLegend,
35
+ SemanticTokenTypes,
36
+ } from 'vscode-languageserver';
37
+
38
+ export type SemanticTokenType =
39
+ | 'keyword'
40
+ | 'namespace'
41
+ | 'class'
42
+ | 'struct'
43
+ | 'type'
44
+ | 'property'
45
+ | 'decorator'
46
+ | 'string'
47
+ | 'number'
48
+ | 'comment';
49
+
50
+ export type SemanticTokenModifier = 'declaration' | 'defaultLibrary';
51
+
52
+ export const semanticTokenTypes: readonly SemanticTokenType[] = [
53
+ SemanticTokenTypes.keyword,
54
+ SemanticTokenTypes.namespace,
55
+ SemanticTokenTypes.class,
56
+ SemanticTokenTypes.struct,
57
+ SemanticTokenTypes.type,
58
+ SemanticTokenTypes.property,
59
+ SemanticTokenTypes.decorator,
60
+ SemanticTokenTypes.string,
61
+ SemanticTokenTypes.number,
62
+ SemanticTokenTypes.comment,
63
+ ];
64
+
65
+ export const semanticTokenModifiers = [
66
+ SemanticTokenModifiers.declaration,
67
+ SemanticTokenModifiers.defaultLibrary,
68
+ ] as const satisfies readonly SemanticTokenModifier[];
69
+
70
+ export const semanticTokenModifierIndexes = {
71
+ declaration: 0,
72
+ defaultLibrary: 1,
73
+ } as const satisfies Record<SemanticTokenModifier, number>;
74
+
75
+ export const semanticTokenModifierBits = {
76
+ declaration: 1 << semanticTokenModifierIndexes.declaration,
77
+ defaultLibrary: 1 << semanticTokenModifierIndexes.defaultLibrary,
78
+ } as const satisfies Record<SemanticTokenModifier, number>;
79
+
80
+ export const semanticTokensLegend: SemanticTokensLegend = {
81
+ tokenTypes: [...semanticTokenTypes],
82
+ tokenModifiers: [...semanticTokenModifiers],
83
+ };
84
+
85
+ export interface SemanticTokenSource {
86
+ readonly document: DocumentAst;
87
+ readonly sourceFile: SourceFile;
88
+ readonly symbolTable: SymbolTable | undefined;
89
+ readonly scalarTypes: readonly string[];
90
+ }
91
+
92
+ export interface PendingSemanticToken {
93
+ readonly startOffset: number;
94
+ readonly endOffset: number;
95
+ readonly tokenTypeIndex: number;
96
+ readonly modifierBitset: number;
97
+ readonly splitMultiline: boolean;
98
+ }
99
+
100
+ type TypeReferenceKind = 'class' | 'struct' | 'type';
101
+
102
+ interface TypeReferenceClassification {
103
+ readonly tokenType: TypeReferenceKind;
104
+ readonly modifierBitset?: number;
105
+ }
106
+
107
+ interface IdentifierSegment {
108
+ readonly identifier: IdentifierAst;
109
+ readonly text: string;
110
+ }
111
+
112
+ interface ExpressionContext {
113
+ readonly bareIdentifierTokenType?: SemanticTokenType;
114
+ }
115
+
116
+ export function buildSemanticTokens(source: SemanticTokenSource, range?: Range): SemanticTokens {
117
+ const builder = new SemanticTokensBuilder(source.sourceFile, range);
118
+ for (const token of collectSemanticTokenEvents(source)) {
119
+ builder.add(token);
120
+ }
121
+ return builder.build();
122
+ }
123
+
124
+ export function collectSemanticTokenEvents(
125
+ source: SemanticTokenSource,
126
+ ): readonly PendingSemanticToken[] {
127
+ const comments = collectCommentTokens(source.document);
128
+ const tokens: PendingSemanticToken[] = [];
129
+ collectDeclarations(source, tokens);
130
+ return mergeSourceOrderedTokens(comments, tokens);
131
+ }
132
+
133
+ export class SemanticTokensBuilder {
134
+ readonly #data: number[] = [];
135
+ readonly #sourceFile: SourceFile;
136
+ readonly #rangeOffsets: { readonly lower: number; readonly upper: number } | undefined;
137
+ #previousLine = 0;
138
+ #previousCharacter = 0;
139
+ #first = true;
140
+
141
+ constructor(sourceFile: SourceFile, range?: Range) {
142
+ this.#sourceFile = sourceFile;
143
+ if (range !== undefined) {
144
+ const startOffset = sourceFile.offsetAt(range.start);
145
+ const endOffset = sourceFile.offsetAt(range.end);
146
+ this.#rangeOffsets = {
147
+ lower: Math.min(startOffset, endOffset),
148
+ upper: Math.max(startOffset, endOffset),
149
+ };
150
+ }
151
+ }
152
+
153
+ add(token: PendingSemanticToken): void {
154
+ if (!this.#intersectsRange(token.startOffset, token.endOffset)) {
155
+ return;
156
+ }
157
+
158
+ if (token.splitMultiline) {
159
+ this.#addMultilineSplitToken(token);
160
+ return;
161
+ }
162
+
163
+ this.#encode(token.startOffset, token.endOffset, token.tokenTypeIndex, token.modifierBitset);
164
+ }
165
+
166
+ build(): SemanticTokens {
167
+ return { data: this.#data };
168
+ }
169
+
170
+ #intersectsRange(startOffset: number, endOffset: number): boolean {
171
+ const rangeOffsets = this.#rangeOffsets;
172
+ return (
173
+ rangeOffsets === undefined ||
174
+ (startOffset < rangeOffsets.upper && endOffset > rangeOffsets.lower)
175
+ );
176
+ }
177
+
178
+ #addMultilineSplitToken(token: PendingSemanticToken): void {
179
+ const start = this.#sourceFile.positionAt(token.startOffset);
180
+ const end = this.#sourceFile.positionAt(token.endOffset);
181
+ if (start.line === end.line) {
182
+ this.#encode(token.startOffset, token.endOffset, token.tokenTypeIndex, token.modifierBitset);
183
+ return;
184
+ }
185
+
186
+ for (let line = start.line; line <= end.line; line++) {
187
+ const startOffset =
188
+ line === start.line ? token.startOffset : this.#sourceFile.lineStartOffset(line);
189
+ const endOffset = line === end.line ? token.endOffset : this.#sourceFile.lineEndOffset(line);
190
+ if (endOffset > startOffset && this.#intersectsRange(startOffset, endOffset)) {
191
+ this.#encode(startOffset, endOffset, token.tokenTypeIndex, token.modifierBitset);
192
+ }
193
+ }
194
+ }
195
+
196
+ #encode(
197
+ startOffset: number,
198
+ endOffset: number,
199
+ tokenTypeIndex: number,
200
+ modifierBitset: number,
201
+ ): void {
202
+ const start = this.#sourceFile.positionAt(startOffset);
203
+ const deltaLine = this.#first ? start.line : start.line - this.#previousLine;
204
+ const deltaStart =
205
+ this.#first || deltaLine !== 0 ? start.character : start.character - this.#previousCharacter;
206
+ this.#data.push(deltaLine, deltaStart, endOffset - startOffset, tokenTypeIndex, modifierBitset);
207
+ this.#previousLine = start.line;
208
+ this.#previousCharacter = start.character;
209
+ this.#first = false;
210
+ }
211
+ }
212
+
213
+ function collectCommentTokens(document: DocumentAst): readonly PendingSemanticToken[] {
214
+ const tokens: PendingSemanticToken[] = [];
215
+ for (const token of document.syntax.tokens()) {
216
+ if (token.kind === 'Comment') {
217
+ tokens.push(pendingTokenForToken(token, 'comment'));
218
+ }
219
+ }
220
+ return tokens;
221
+ }
222
+
223
+ function collectDeclarations(source: SemanticTokenSource, tokens: PendingSemanticToken[]): void {
224
+ for (const declaration of source.document.declarations()) {
225
+ collectDeclaration(declaration, source, tokens, undefined);
226
+ }
227
+ }
228
+
229
+ function collectDeclaration(
230
+ declaration: DeclarationAst,
231
+ source: SemanticTokenSource,
232
+ tokens: PendingSemanticToken[],
233
+ namespace: string | undefined,
234
+ ): void {
235
+ if (declaration instanceof ModelDeclarationAst) {
236
+ addToken(declaration.keyword(), 'keyword', tokens);
237
+ addIdentifier(declaration.name(), 'class', tokens, semanticTokenModifierBits.declaration);
238
+ collectBlockMembers(declaration.members(), source, tokens, namespace);
239
+ return;
240
+ }
241
+
242
+ if (declaration instanceof CompositeTypeDeclarationAst) {
243
+ addToken(declaration.keyword(), 'keyword', tokens);
244
+ addIdentifier(declaration.name(), 'struct', tokens, semanticTokenModifierBits.declaration);
245
+ collectBlockMembers(declaration.members(), source, tokens, namespace);
246
+ return;
247
+ }
248
+
249
+ if (declaration instanceof NamespaceDeclarationAst) {
250
+ addToken(declaration.keyword(), 'keyword', tokens);
251
+ addIdentifier(declaration.name(), 'namespace', tokens, semanticTokenModifierBits.declaration);
252
+ const nestedNamespace = declaration.name()?.name();
253
+ for (const nested of declaration.declarations()) {
254
+ collectDeclaration(nested, source, tokens, nestedNamespace);
255
+ }
256
+ return;
257
+ }
258
+
259
+ if (declaration instanceof TypesBlockAst) {
260
+ addToken(declaration.keyword(), 'keyword', tokens);
261
+ for (const namedType of declaration.declarations()) {
262
+ collectNamedTypeDeclaration(namedType, source, tokens, namespace);
263
+ }
264
+ return;
265
+ }
266
+
267
+ addToken(declaration.keyword(), 'keyword', tokens);
268
+ addIdentifier(declaration.name(), 'type', tokens, semanticTokenModifierBits.declaration);
269
+ collectGenericBlockMembers(declaration.members(), source, tokens, namespace);
270
+ }
271
+
272
+ function collectNamedTypeDeclaration(
273
+ declaration: NamedTypeDeclarationAst,
274
+ source: SemanticTokenSource,
275
+ tokens: PendingSemanticToken[],
276
+ namespace: string | undefined,
277
+ ): void {
278
+ addIdentifier(declaration.name(), 'type', tokens, semanticTokenModifierBits.declaration);
279
+ collectTypeAnnotation(declaration.typeAnnotation(), source, tokens, namespace);
280
+ collectAttributes(declaration.attributes(), source, tokens, namespace);
281
+ }
282
+
283
+ function collectGenericBlockMembers(
284
+ members: Iterable<GenericBlockMemberAst>,
285
+ source: SemanticTokenSource,
286
+ tokens: PendingSemanticToken[],
287
+ namespace: string | undefined,
288
+ ): void {
289
+ for (const member of members) {
290
+ if ('key' in member) {
291
+ addIdentifier(member.key(), 'property', tokens);
292
+ collectExpression(member.value(), source, tokens, namespace);
293
+ continue;
294
+ }
295
+ collectAttribute(member, source, tokens, namespace);
296
+ }
297
+ }
298
+
299
+ function collectBlockMembers(
300
+ members: Iterable<BlockMemberAst>,
301
+ source: SemanticTokenSource,
302
+ tokens: PendingSemanticToken[],
303
+ namespace: string | undefined,
304
+ ): void {
305
+ for (const member of members) {
306
+ if (member instanceof FieldDeclarationAst) {
307
+ collectField(member, source, tokens, namespace);
308
+ continue;
309
+ }
310
+ collectAttribute(member, source, tokens, namespace);
311
+ }
312
+ }
313
+
314
+ function collectField(
315
+ field: FieldDeclarationAst,
316
+ source: SemanticTokenSource,
317
+ tokens: PendingSemanticToken[],
318
+ namespace: string | undefined,
319
+ ): void {
320
+ addIdentifier(field.name(), 'property', tokens, semanticTokenModifierBits.declaration);
321
+ collectTypeAnnotation(field.typeAnnotation(), source, tokens, namespace);
322
+ collectAttributes(field.attributes(), source, tokens, namespace);
323
+ }
324
+
325
+ function collectTypeAnnotation(
326
+ annotation: TypeAnnotationAst | undefined,
327
+ source: SemanticTokenSource,
328
+ tokens: PendingSemanticToken[],
329
+ namespace: string | undefined,
330
+ ): void {
331
+ if (annotation === undefined) {
332
+ return;
333
+ }
334
+ collectTypeReference(annotation.name(), source, tokens, namespace);
335
+ collectAttributeArgList(annotation.argList(), source, tokens, namespace);
336
+ }
337
+
338
+ function collectAttributes(
339
+ attributes: Iterable<AttributeAst>,
340
+ source: SemanticTokenSource,
341
+ tokens: PendingSemanticToken[],
342
+ namespace: string | undefined,
343
+ ): void {
344
+ for (const attribute of attributes) {
345
+ collectAttribute(attribute, source, tokens, namespace);
346
+ }
347
+ }
348
+
349
+ function collectAttribute(
350
+ attribute: AttributeAst,
351
+ source: SemanticTokenSource,
352
+ tokens: PendingSemanticToken[],
353
+ namespace: string | undefined,
354
+ ): void {
355
+ collectDecoratorName(attribute.name(), source.sourceFile.text, tokens);
356
+ collectAttributeArgList(attribute.argList(), source, tokens, namespace);
357
+ }
358
+
359
+ function collectDecoratorName(
360
+ name: QualifiedNameAst | undefined,
361
+ sourceText: string,
362
+ tokens: PendingSemanticToken[],
363
+ ): void {
364
+ if (name === undefined) {
365
+ return;
366
+ }
367
+ const segments = identifierSegments(name);
368
+ for (const [index, segment] of segments.entries()) {
369
+ tokens.push(rangeForDecoratorIdentifier(segment.identifier, sourceText, index === 0));
370
+ }
371
+ }
372
+
373
+ function collectAttributeArgList(
374
+ argList: AttributeArgListAst | undefined,
375
+ source: SemanticTokenSource,
376
+ tokens: PendingSemanticToken[],
377
+ namespace: string | undefined,
378
+ ): void {
379
+ if (argList === undefined) {
380
+ return;
381
+ }
382
+ for (const arg of argList.args()) {
383
+ collectAttributeArg(arg, source, tokens, namespace);
384
+ }
385
+ }
386
+
387
+ function collectAttributeArg(
388
+ arg: AttributeArgAst,
389
+ source: SemanticTokenSource,
390
+ tokens: PendingSemanticToken[],
391
+ namespace: string | undefined,
392
+ ): void {
393
+ const name = arg.name();
394
+ addIdentifier(name, 'property', tokens);
395
+ collectExpression(arg.value(), source, tokens, namespace, expressionContextForAttributeArg(name));
396
+ }
397
+
398
+ function collectExpression(
399
+ expression: ExpressionAst | undefined,
400
+ source: SemanticTokenSource,
401
+ tokens: PendingSemanticToken[],
402
+ namespace: string | undefined,
403
+ context: ExpressionContext = {},
404
+ ): void {
405
+ if (expression === undefined) {
406
+ return;
407
+ }
408
+
409
+ if (expression instanceof StringLiteralExprAst) {
410
+ addToken(expression.token(), 'string', tokens);
411
+ return;
412
+ }
413
+
414
+ if (expression instanceof NumberLiteralExprAst) {
415
+ addToken(expression.token(), 'number', tokens);
416
+ return;
417
+ }
418
+
419
+ if (expression instanceof BooleanLiteralExprAst) {
420
+ addToken(expression.token(), 'keyword', tokens);
421
+ return;
422
+ }
423
+
424
+ if (expression instanceof FunctionCallAst) {
425
+ collectTypeReference(expression.name(), source, tokens, namespace);
426
+ for (const arg of expression.args()) {
427
+ collectAttributeArg(arg, source, tokens, namespace);
428
+ }
429
+ return;
430
+ }
431
+
432
+ if (expression instanceof ArrayLiteralAst) {
433
+ for (const element of expression.elements()) {
434
+ collectExpression(element, source, tokens, namespace, context);
435
+ }
436
+ return;
437
+ }
438
+
439
+ if (expression instanceof ObjectLiteralExprAst) {
440
+ for (const field of expression.fields()) {
441
+ addIdentifier(field.key(), 'property', tokens);
442
+ collectExpression(field.value(), source, tokens, namespace);
443
+ }
444
+ return;
445
+ }
446
+
447
+ collectIdentifierExpression(expression, source, tokens, namespace, context);
448
+ }
449
+
450
+ function collectIdentifierExpression(
451
+ identifier: IdentifierAst,
452
+ source: SemanticTokenSource,
453
+ tokens: PendingSemanticToken[],
454
+ namespace: string | undefined,
455
+ context: ExpressionContext,
456
+ ): void {
457
+ const text = identifier.name();
458
+ if (text === undefined) {
459
+ return;
460
+ }
461
+ const bareIdentifierTokenType = context.bareIdentifierTokenType;
462
+ if (bareIdentifierTokenType !== undefined) {
463
+ tokens.push(rangeForIdentifier(identifier, bareIdentifierTokenType));
464
+ return;
465
+ }
466
+ const classification = classifyTypeReference([text], source, namespace);
467
+ tokens.push(
468
+ rangeForIdentifier(identifier, classification.tokenType, classification.modifierBitset),
469
+ );
470
+ }
471
+
472
+ function expressionContextForAttributeArg(name: IdentifierAst | undefined): ExpressionContext {
473
+ const argName = name?.name();
474
+ return argName === 'fields' || argName === 'references'
475
+ ? { bareIdentifierTokenType: 'property' }
476
+ : {};
477
+ }
478
+
479
+ function collectTypeReference(
480
+ name: QualifiedNameAst | undefined,
481
+ source: SemanticTokenSource,
482
+ tokens: PendingSemanticToken[],
483
+ namespace: string | undefined,
484
+ ): void {
485
+ if (name === undefined) {
486
+ return;
487
+ }
488
+
489
+ const segments = identifierSegments(name);
490
+ if (segments.length === 0) {
491
+ return;
492
+ }
493
+
494
+ const path = segments.map((segment) => segment.text);
495
+ for (const segment of segments.slice(0, -1)) {
496
+ if (isKnownNamespace(segment.text, source.symbolTable)) {
497
+ tokens.push(rangeForIdentifier(segment.identifier, 'namespace'));
498
+ }
499
+ }
500
+
501
+ const finalSegment = segments[segments.length - 1];
502
+ if (finalSegment === undefined) {
503
+ return;
504
+ }
505
+ const classification = classifyTypeReference(path, source, namespace);
506
+ tokens.push(
507
+ rangeForIdentifier(
508
+ finalSegment.identifier,
509
+ classification.tokenType,
510
+ classification.modifierBitset,
511
+ ),
512
+ );
513
+ }
514
+
515
+ function classifyTypeReference(
516
+ path: readonly string[],
517
+ source: SemanticTokenSource,
518
+ namespace: string | undefined,
519
+ ): TypeReferenceClassification {
520
+ const name = path[path.length - 1];
521
+ if (name === undefined) {
522
+ return { tokenType: 'type' };
523
+ }
524
+
525
+ const table = source.symbolTable;
526
+ const namespaceName = path.length > 1 ? path[path.length - 2] : namespace;
527
+ const namespaceScope =
528
+ namespaceName !== undefined ? table?.topLevel.namespaces[namespaceName] : undefined;
529
+
530
+ if (namespaceScope !== undefined) {
531
+ if (Object.hasOwn(namespaceScope.models, name)) {
532
+ return { tokenType: 'class' };
533
+ }
534
+ if (Object.hasOwn(namespaceScope.compositeTypes, name)) {
535
+ return { tokenType: 'struct' };
536
+ }
537
+ if (Object.hasOwn(namespaceScope.blocks, name)) {
538
+ return { tokenType: 'type' };
539
+ }
540
+ }
541
+
542
+ if (table !== undefined) {
543
+ if (Object.hasOwn(table.topLevel.models, name)) {
544
+ return { tokenType: 'class' };
545
+ }
546
+ if (Object.hasOwn(table.topLevel.compositeTypes, name)) {
547
+ return { tokenType: 'struct' };
548
+ }
549
+ if (Object.hasOwn(table.topLevel.scalars, name)) {
550
+ return { tokenType: 'type', modifierBitset: semanticTokenModifierBits.defaultLibrary };
551
+ }
552
+ if (
553
+ Object.hasOwn(table.topLevel.typeAliases, name) ||
554
+ Object.hasOwn(table.topLevel.blocks, name)
555
+ ) {
556
+ return { tokenType: 'type' };
557
+ }
558
+ }
559
+
560
+ if (source.scalarTypes.includes(name)) {
561
+ return { tokenType: 'type', modifierBitset: semanticTokenModifierBits.defaultLibrary };
562
+ }
563
+
564
+ return { tokenType: 'type' };
565
+ }
566
+
567
+ function isKnownNamespace(name: string, table: SymbolTable | undefined): boolean {
568
+ return table !== undefined && Object.hasOwn(table.topLevel.namespaces, name);
569
+ }
570
+
571
+ function identifierSegments(name: QualifiedNameAst): readonly IdentifierSegment[] {
572
+ const segments: IdentifierSegment[] = [];
573
+ for (const identifier of filterChildren(name.syntax, IdentifierAst.cast)) {
574
+ const text = identifier.name();
575
+ if (text !== undefined) {
576
+ segments.push({ identifier, text });
577
+ }
578
+ }
579
+ return segments;
580
+ }
581
+
582
+ function addIdentifier(
583
+ identifier: IdentifierAst | undefined,
584
+ tokenType: SemanticTokenType,
585
+ tokens: PendingSemanticToken[],
586
+ modifierBitset = 0,
587
+ ): void {
588
+ if (identifier === undefined) {
589
+ return;
590
+ }
591
+ tokens.push(rangeForIdentifier(identifier, tokenType, modifierBitset));
592
+ }
593
+
594
+ function addToken(
595
+ token: SyntaxToken | undefined,
596
+ tokenType: SemanticTokenType,
597
+ tokens: PendingSemanticToken[],
598
+ modifierBitset = 0,
599
+ ): void {
600
+ if (token === undefined) {
601
+ return;
602
+ }
603
+ tokens.push(pendingTokenForToken(token, tokenType, modifierBitset));
604
+ }
605
+
606
+ function rangeForIdentifier(
607
+ identifier: IdentifierAst,
608
+ tokenType: SemanticTokenType,
609
+ modifierBitset = 0,
610
+ ): PendingSemanticToken {
611
+ const token = identifier.token();
612
+ if (token === undefined) {
613
+ return createPendingSemanticToken(
614
+ identifier.syntax.offset,
615
+ identifier.syntax.offset,
616
+ tokenType,
617
+ modifierBitset,
618
+ );
619
+ }
620
+ return pendingTokenForToken(token, tokenType, modifierBitset);
621
+ }
622
+
623
+ function rangeForDecoratorIdentifier(
624
+ identifier: IdentifierAst,
625
+ sourceText: string,
626
+ includePrefix: boolean,
627
+ ): PendingSemanticToken {
628
+ const range = rangeForIdentifier(identifier, 'decorator');
629
+ if (!includePrefix) {
630
+ return range;
631
+ }
632
+
633
+ let startOffset = range.startOffset;
634
+ while (startOffset > 0 && sourceText.charAt(startOffset - 1) === '@') {
635
+ startOffset--;
636
+ }
637
+ return createPendingSemanticToken(
638
+ startOffset,
639
+ range.endOffset,
640
+ 'decorator',
641
+ range.modifierBitset,
642
+ );
643
+ }
644
+
645
+ function pendingTokenForToken(
646
+ token: SyntaxToken,
647
+ tokenType: SemanticTokenType,
648
+ modifierBitset = 0,
649
+ ): PendingSemanticToken {
650
+ return createPendingSemanticToken(
651
+ token.offset,
652
+ token.offset + token.text.length,
653
+ tokenType,
654
+ modifierBitset,
655
+ );
656
+ }
657
+
658
+ function mergeSourceOrderedTokens(
659
+ left: readonly PendingSemanticToken[],
660
+ right: readonly PendingSemanticToken[],
661
+ ): readonly PendingSemanticToken[] {
662
+ const result: PendingSemanticToken[] = [];
663
+ let leftIndex = 0;
664
+ let rightIndex = 0;
665
+
666
+ while (leftIndex < left.length || rightIndex < right.length) {
667
+ const leftToken = left[leftIndex];
668
+ const rightToken = right[rightIndex];
669
+ if (
670
+ leftToken !== undefined &&
671
+ (rightToken === undefined || leftToken.startOffset <= rightToken.startOffset)
672
+ ) {
673
+ result.push(leftToken);
674
+ leftIndex++;
675
+ } else if (rightToken !== undefined) {
676
+ result.push(rightToken);
677
+ rightIndex++;
678
+ }
679
+ }
680
+
681
+ return result;
682
+ }
683
+
684
+ function createPendingSemanticToken(
685
+ startOffset: number,
686
+ endOffset: number,
687
+ tokenType: SemanticTokenType,
688
+ modifierBitset = 0,
689
+ ): PendingSemanticToken {
690
+ return {
691
+ startOffset,
692
+ endOffset,
693
+ tokenTypeIndex: tokenTypeIndex(tokenType),
694
+ modifierBitset,
695
+ splitMultiline: tokenType === 'string' || tokenType === 'comment',
696
+ };
697
+ }
698
+
699
+ function tokenTypeIndex(tokenType: SemanticTokenType): number {
700
+ return semanticTokenTypes.indexOf(tokenType);
701
+ }