0
# Utility Classes
1
2
Helper classes for common language server tasks including semantic token building, error tracking, and progress reporting. These utilities simplify common operations in language server implementations.
3
4
## Capabilities
5
6
### Semantic Tokens Builder
7
8
Helper class for building semantic tokens responses with proper encoding and delta support.
9
10
```typescript { .api }
11
/**
12
* A helper class to build semantic tokens data arrays
13
*/
14
class SemanticTokensBuilder {
15
/** Create a new semantic tokens builder */
16
constructor();
17
18
/**
19
* Add a semantic token to the builder
20
* @param line Line number (0-based)
21
* @param char Character offset on line (0-based)
22
* @param length Length of the token in characters
23
* @param tokenType Token type index from legend
24
* @param tokenModifiers Token modifiers as bit flags from legend
25
*/
26
push(line: number, char: number, length: number, tokenType: number, tokenModifiers?: number): void;
27
28
/**
29
* Build the final semantic tokens result
30
* @returns SemanticTokens object with encoded data array
31
*/
32
build(): SemanticTokens;
33
34
/**
35
* Build semantic tokens edits for delta requests
36
* @returns SemanticTokensEdits for incremental updates
37
*/
38
buildEdits(): SemanticTokensEdits;
39
}
40
```
41
42
**Usage Example:**
43
44
```typescript
45
import { SemanticTokensBuilder } from "vscode-languageserver";
46
47
// Define token types and modifiers indices (should match client capabilities)
48
const TokenTypes = {
49
keyword: 0,
50
function: 1,
51
variable: 2,
52
string: 3,
53
number: 4,
54
comment: 5
55
};
56
57
const TokenModifiers = {
58
declaration: 1 << 0,
59
definition: 1 << 1,
60
readonly: 1 << 2,
61
static: 1 << 3,
62
deprecated: 1 << 4
63
};
64
65
connection.languages.semanticTokens.on((params) => {
66
const document = documents.get(params.textDocument.uri);
67
if (!document) return null;
68
69
const builder = new SemanticTokensBuilder();
70
71
const lines = document.getText().split('\n');
72
lines.forEach((line, lineIndex) => {
73
// Simple tokenization example
74
const words = line.split(/\s+/);
75
let charOffset = 0;
76
77
words.forEach(word => {
78
const wordStart = line.indexOf(word, charOffset);
79
80
if (word === 'function') {
81
builder.push(lineIndex, wordStart, word.length, TokenTypes.keyword, TokenModifiers.declaration);
82
} else if (word.startsWith('"') && word.endsWith('"')) {
83
builder.push(lineIndex, wordStart, word.length, TokenTypes.string);
84
} else if (/^\d+$/.test(word)) {
85
builder.push(lineIndex, wordStart, word.length, TokenTypes.number);
86
} else if (word.startsWith('//')) {
87
builder.push(lineIndex, wordStart, line.length - wordStart, TokenTypes.comment);
88
break; // Rest of line is comment
89
}
90
91
charOffset = wordStart + word.length;
92
});
93
});
94
95
return builder.build();
96
});
97
```
98
99
### Error Message Tracker
100
101
Helper class for tracking and deduplicating error messages before sending them to the client.
102
103
```typescript { .api }
104
/**
105
* Helps tracking error message. Equal occurrences of the same message are only stored once.
106
* This class is useful if text documents are validated in a loop and equal error messages
107
* should be folded into one.
108
*/
109
class ErrorMessageTracker {
110
/** Create a new error message tracker */
111
constructor();
112
113
/**
114
* Add a message to the tracker
115
* @param message The message to add
116
*/
117
add(message: string): void;
118
119
/**
120
* Send all tracked messages to the connection's window
121
* @param connection The connection established between client and server
122
*/
123
sendErrors(connection: { window: RemoteWindow }): void;
124
}
125
```
126
127
**Usage Example:**
128
129
```typescript
130
import { ErrorMessageTracker } from "vscode-languageserver";
131
132
const errorTracker = new ErrorMessageTracker();
133
134
// Validate multiple documents
135
documents.all().forEach(document => {
136
try {
137
validateDocument(document);
138
} catch (error) {
139
// Add errors to tracker (duplicates will be deduplicated)
140
errorTracker.add(`Error in ${document.uri}: ${error.message}`);
141
}
142
});
143
144
// Send all unique errors to client
145
errorTracker.sendErrors(connection);
146
```
147
148
### Progress Reporting
149
150
Interfaces for reporting work done progress and partial results during long-running operations.
151
152
```typescript { .api }
153
/**
154
* Interface for reporting work done progress
155
*/
156
interface WorkDoneProgressReporter {
157
/**
158
* Begin progress reporting
159
* @param title The title of the progress
160
* @param percentage Optional initial percentage (0-100)
161
* @param message Optional initial message
162
* @param cancellable Whether the operation can be cancelled
163
*/
164
begin(title: string, percentage?: number, message?: string, cancellable?: boolean): void;
165
166
/**
167
* Report progress with percentage
168
* @param percentage Progress percentage (0-100)
169
*/
170
report(percentage: number): void;
171
172
/**
173
* Report progress with message
174
* @param message Progress message
175
*/
176
report(message: string): void;
177
178
/**
179
* Report progress with percentage and message
180
* @param percentage Progress percentage (0-100)
181
* @param message Optional progress message
182
*/
183
report(percentage: number, message?: string): void;
184
185
/** Complete the progress reporting */
186
done(): void;
187
}
188
189
/**
190
* Server-side work done progress reporter
191
*/
192
interface WorkDoneProgressServerReporter extends WorkDoneProgressReporter {
193
/** The progress token */
194
readonly token: ProgressToken;
195
}
196
197
/**
198
* Interface for reporting partial results
199
*/
200
interface ResultProgressReporter<T> {
201
/**
202
* Report partial result data
203
* @param data The partial result data
204
*/
205
report(data: T): void;
206
}
207
```
208
209
**Usage Example:**
210
211
```typescript
212
connection.onWorkspaceSymbol((params, token, workDoneProgress, resultProgress) => {
213
// Begin progress reporting
214
workDoneProgress.begin('Searching workspace symbols');
215
216
const allSymbols: SymbolInformation[] = [];
217
const files = getAllWorkspaceFiles();
218
219
files.forEach((file, index) => {
220
// Report progress
221
const percentage = Math.round((index / files.length) * 100);
222
workDoneProgress.report(percentage, `Processing ${file}`);
223
224
// Get symbols from file
225
const symbols = getSymbolsFromFile(file, params.query);
226
allSymbols.push(...symbols);
227
228
// Report partial results
229
if (symbols.length > 0) {
230
resultProgress.report(symbols);
231
}
232
233
// Check for cancellation
234
if (token.isCancellationRequested) {
235
workDoneProgress.done();
236
return null;
237
}
238
});
239
240
// Complete progress
241
workDoneProgress.done();
242
return allSymbols;
243
});
244
```
245
246
### Registration Utilities
247
248
Interfaces for bulk registration and unregistration of capabilities.
249
250
```typescript { .api }
251
/**
252
* Interface for bulk capability registration
253
*/
254
interface BulkRegistration {
255
/**
256
* Add a registration to the bulk registration
257
* @param type The request/notification type to register
258
* @param registerOptions Optional registration options
259
*/
260
add<RO>(type: ProtocolRequestType0<any, any, any, RO> | ProtocolNotificationType0<RO>, registerOptions?: RO): void;
261
add<P, RO>(type: ProtocolRequestType<P, any, any, any, RO> | ProtocolNotificationType<P, RO>, registerOptions?: RO): void;
262
263
/**
264
* Convert to registration parameters for sending to client
265
*/
266
asRegistrationParams(): RegistrationParams;
267
}
268
269
/**
270
* Interface for bulk capability unregistration
271
*/
272
interface BulkUnregistration extends Disposable {
273
/**
274
* Add an unregistration to the bulk unregistration
275
* @param unregistration The unregistration to add
276
*/
277
add(unregistration: Unregistration): void;
278
279
/**
280
* Convert to unregistration parameters for sending to client
281
*/
282
asUnregistrationParams(): UnregistrationParams;
283
284
/** Dispose all registrations */
285
dispose(): void;
286
}
287
```
288
289
**Usage Example:**
290
291
```typescript
292
// Bulk register multiple capabilities
293
const bulkRegistration = BulkRegistration.create();
294
bulkRegistration.add(HoverRequest.type, { documentSelector: [{ scheme: 'file', language: 'typescript' }] });
295
bulkRegistration.add(CompletionRequest.type, { documentSelector: [{ scheme: 'file', language: 'typescript' }] });
296
bulkRegistration.add(DefinitionRequest.type, { documentSelector: [{ scheme: 'file', language: 'typescript' }] });
297
298
// Send bulk registration to client
299
connection.client.register(bulkRegistration.asRegistrationParams());
300
301
// Later, bulk unregister
302
const bulkUnregistration = BulkUnregistration.create();
303
bulkUnregistration.add({ id: 'hover-registration', method: 'textDocument/hover' });
304
bulkUnregistration.add({ id: 'completion-registration', method: 'textDocument/completion' });
305
306
connection.client.unregister(bulkUnregistration.asUnregistrationParams());
307
```
308
309
### UUID Utilities
310
311
Simple UUID generation utilities for creating unique identifiers.
312
313
```typescript { .api }
314
/**
315
* UUID generation utilities
316
*/
317
namespace UUID {
318
/**
319
* Generate a new UUID v4
320
* @returns A new UUID string
321
*/
322
function generateUuid(): string;
323
}
324
```
325
326
**Usage Example:**
327
328
```typescript
329
import { UUID } from "vscode-languageserver";
330
331
// Generate unique identifiers for registrations
332
const registrationId = UUID.generateUuid();
333
const workDoneToken = UUID.generateUuid();
334
335
connection.client.register({
336
registrations: [{
337
id: registrationId,
338
method: 'textDocument/hover',
339
registerOptions: { documentSelector: [{ scheme: 'file' }] }
340
}]
341
});
342
```
343
344
### Type Checking Utilities
345
346
Utility functions for runtime type checking and validation.
347
348
```typescript { .api }
349
/**
350
* Type checking utilities
351
*/
352
namespace Is {
353
/** Check if value is a function */
354
function func(value: any): value is Function;
355
356
/** Check if value is a string */
357
function string(value: any): value is string;
358
359
/** Check if value is a number */
360
function number(value: any): value is number;
361
362
/** Check if value is a boolean */
363
function boolean(value: any): value is boolean;
364
365
/** Check if value is undefined */
366
function undefined(value: any): value is undefined;
367
368
/** Check if value is defined (not undefined) */
369
function defined<T>(value: T | undefined): value is T;
370
}
371
```
372
373
**Usage Example:**
374
375
```typescript
376
import { Is } from "vscode-languageserver";
377
378
function processValue(value: any) {
379
if (Is.string(value)) {
380
// TypeScript knows value is string here
381
return value.toUpperCase();
382
} else if (Is.number(value)) {
383
// TypeScript knows value is number here
384
return value.toString();
385
} else if (Is.defined(value)) {
386
// TypeScript knows value is not undefined here
387
return JSON.stringify(value);
388
}
389
return 'undefined';
390
}
391
```
392
393
## Core Types
394
395
```typescript { .api }
396
interface SemanticTokens {
397
/** An optional result id */
398
resultId?: string;
399
/** The actual tokens */
400
data: number[];
401
}
402
403
interface SemanticTokensEdits {
404
/** An optional result id */
405
resultId?: string;
406
/** The edits to transform a previous result into a new result */
407
edits: SemanticTokensEdit[];
408
}
409
410
interface SemanticTokensEdit {
411
/** The start offset of the edit */
412
start: number;
413
/** The count of elements to remove */
414
deleteCount: number;
415
/** The elements to insert */
416
data?: number[];
417
}
418
419
interface RemoteWindow {
420
/** Show an error message */
421
showErrorMessage(message: string): void;
422
/** Show a warning message */
423
showWarningMessage(message: string): void;
424
/** Show an information message */
425
showInformationMessage(message: string): void;
426
}
427
428
type ProgressToken = number | string;
429
430
interface RegistrationParams {
431
registrations: Registration[];
432
}
433
434
interface Registration {
435
/** The id used to register the request */
436
id: string;
437
/** The method to register for */
438
method: string;
439
/** Options necessary for the registration */
440
registerOptions?: any;
441
}
442
443
interface UnregistrationParams {
444
unregisterations: Unregistration[];
445
}
446
447
interface Unregistration {
448
/** The id used to unregister the request or notification */
449
id: string;
450
/** The method to unregister for */
451
method: string;
452
}
453
454
interface Disposable {
455
dispose(): void;
456
}
457
```